面试常见基础编程题

基础编程题

二分搜索

  1. 经典二分搜索

    需要注意的点

    • 循环条件 left <= right 还是 left < right
      • 需要看 left == right 是不是可以进入循环 可能是target吗
      • 需要防止程序死循环 如果left == right == mid 在循环体内不能出去 就会构成死循环
    • 左右边界更新 right = mid - 1 还是 right = mid
    class Solution:
        def bs(self, nums, target):
            left, right = 0, len(nums) - 1
            while left <= right:
                mid = (left + right) // 2
                if nums[mid] == target:
                    return mid + 1
                elif nums[mid] < target:
                    left = mid + 1  # 为什么可以+1 因为mid不可能是target 
                else:
                    right = mid - 1  # 同理 这样也保证了算法可以收敛 不会死循环
            return -1
    
  2. 一个有重复元素的数组中查找 key 的最左位置

    跟第一题的区别:

    • 循环条件
    • 边界更新
    • 循环体内不判断是否找到目标 循环体外才判断 需要考虑出循环的两种情况
    class Solution:
        def bs(self, nums, target):
            left, right = 0, len(nums) - 1
            while left < right:
                mid = (left + right) // 2
                if nums[mid] < target: # 左半部分可以丢掉 并且mid不可能是target
                    left = mid + 1
                else: # nums[mid] >= target
                    right = mid # 因为mid可能是target
            return right if nums[right] == target else -1 # 因为走到这里有两种情况 找到目标 或者没有目标
    

链表

  1. O(1)时间复杂度删除链表节点

    题目描述:给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间删除该节点。
    解题思路:常规的做法是从链表的头结点开始遍历,找到需要删除的节点的前驱节点,把它的 next 指向要删除节点的下一个节点,平均时间复杂度为O(n),不满足题目要求。
    那是不是一定要得到被删除的节点的前一个节点呢?其实不用的。我们可以很方面地得到要删除节点的下一个节点,如果我们把下一个节点的内容复制到要删除的节点上覆盖原有的内容,再把下一个节点删除,那就相当于把当前要删除的节点删除了。

    单向链表不能回头 只能往后面走

    class Solution:
        def deleteNode(self, pHead, Node):
            if pHead == None or Node == None:
                return
            if Node.next:
                Node.val = Node.next.val
                Node.next = Node.next.next
            # 此时确定 Node 位于链表尾
            elif pHead == Node: # 链表只有一个元素
                pHead = None
            else:
                while pHead.next.next:
                    pHead = pHead.next
                pHead.next = None
            return pHead
    
  1. 翻转链表

    定义两个指针precur,每反转一个节点涉及到三个操作:

    • 连接cur指针的next
    • 分别移动指针precur

    关于Python 等号左右多个变量同时赋值
    两个原则:

    • 等号右边的变量存放的都是旧的值 等号左边的变量存放的都是新的值
    • 等号左边的变量会被刷新 注意先后顺序 先取值后赋值
    class Solution:
        def ReverseList(self, pHead):
            prev = None # 辅助节点
            while pHead:
                pHead.next, prev, pHead = prev, pHead, pHead.next
            return prev
    

    正常的代码

    class Solution:
        def reverse(self, pHead):
            prev = None
            while pHead:
                tp = pHead.next # 存下下一个节点
                pHead.next = prev
                prev = pHead
                pHead = tp
            return prev
    

    递归版本:

    class Solution:
        def ReverseList(self, pHead):
            if not pHead or not pHead.next: # 停止向下递归 往回返
                return pHead
            res = self.ReverseList(pHead.next)
            pHead.next.next = pHead # 连接
            pHead.next = None # 尾节点
            return res
    
  1. 判断链表是否有环

    快慢指针 如何证明?

    感想: while循环内部作为函数return出口很常见 while循环体外往往代表着另外一种情况

    class Solution(object):
        def hasCycle(self, head):
            """
            :type head: ListNode
            :rtype: bool
            """
            fast, slow = head, head
            while fast and fast.next:
                fast = fast.next.next
                slow = slow.next
                if fast == slow: 
                    return True
            return False
    
  1. 找到环形链表的入口

    先判断有没有环 代码同上

    之后将快指针移动到链表头 两个指针同时移动 如何证明??

    使用while - else语法区分出循环的两种情况

    class Solution(object):
        def detectCycle(self, head):
            """
            :type head: ListNode
            :rtype: bool
            """
            slow = fast = head
            while fast and fast.next:
                slow = slow.next
                fast = fast.next.next
                if slow == fast:
                    break
            else: # 因为出循环存在两种情况 所以需要区分 这里使用while else语法作区分
                return None
            while head != slow: # 直接移动头指针
                slow = slow.next
                head = head.next
            return head
    
  1. 计算环的长度

    快慢指针在环内相遇之后 让满指针继续移动 再次相遇经过多少步就可以计算环的长度

  2. 删除单链表倒数第n个节点

    class Solution:
        def FindKthToTail(self, head, k):
            # write code here
            if head == None or k <= 0:
                return None
    
            # 设定两个指针 第一个指针指向头节点 第二个指向k-1节点
            p1, p2 = head, head
            i = 0
            while p2.next and i < k - 1:
                p2 = p2.next
                i += 1
            # while 循环结束 一定要对各种不同的情况进行判断
            if i != k - 1:  # 链表没有K个元素
                return None
    
            while p2.next:
                p1, p2 = p1.next, p2.next
            return p1
    

for循环版本

  class Solution:
    def FindKthToTail(self, head, k):
        if not head or k <= 0:
            return
        fast = slow = head
        for i in range(k - 1):
            if not (fast and fast.next): # 为了保证fast指针最后停下的位置也是非空的  所以加上fast.next
                return
            fast = fast.next

        while fast and fast.next:
            fast = fast.next
            slow = slow.next
        return slow
  1. 求链表中间节点

  2. 判断两个链表是否相交

队列和栈

字符串

排序

  1. 插入排序

    • 左侧已排序
    • 右侧未排序
    • 将未排序部分的第一个元素插入到已排序部分的合适的位置
    • 插入排序稳定 相同值的元素的相对顺序不会改变
image.png
def insertionSort(nums):
    for i in range(1, len(nums)):
        cur, p = nums[i], i  # 取出当值位置的数值 因此当前位置可以被填充
        while p - 1 >= 0 and nums[p - 1] > cur:
            nums[p] = nums[p - 1]
            p -= 1
        nums[p] = cur
    return nums
  1. 归并排序

    image.png
def merge_sort(nums):
    if len(nums) <= 1:
        return nums
    mid = len(nums) // 2
    left = merge_sort(nums[:mid])
    right = merge_sort(nums[mid:])
    return merge(left, right)  # 合并左右两个已经排序的部分

def merge(left, right):
    l, r = 0, 0
    result = [] # 需要开辟新空间存放排完序的结果
    while l < len(left) and r < len(right):
        if left[l] < right[r]:  # 将left与right较小元素按序加入新序列
            result.append(left[l])
            l += 1
        else:
            result.append(right[r])
            r += 1
    result += left[l:]
    result += right[r:]
    return result
  1. 快速排序

    效率底 但是容易理解的版本 生成了新的数组

    def quicksort(nums):
        size = len(nums)
        if not nums or size < 2:  # 递归出口,空数组或者只有一个元素的数组都是有序的
            return nums
        idx = 0 # 标定点
        pivot = nums[idx] # 标定值
        less_part = [nums[i] for i in range(size) if nums[i] <= pivot and idx != i]
        great_part = [nums[i] for i in range(size) if nums[i] > pivot and idx != i]
        return quicksort(less_part) + [pivot] + quicksort(great_part)
    

    正常 版本 直接在原始数组上修改

    image.png
    def quickSort(nums, first, last):
        splitpoint = partition(nums, first, last)
        quickSort(nums, first, splitpoint - 1)
        quickSort(nums, splitpoint + 1, last)
    
    
    def partition(nums, first, last):
        rand = randint(first, last)  # 优化,随机取标定点,以解决近乎有序的列表
        nums[first], nums[rand] = nums[rand], nums[first]
    
        pivot = nums[first]
        left = first + 1
        right = last
        while True:  # 这里使用了双路快排,以解决有大量相同值的列表
            while left <= right and nums[left] < pivot:
                left += 1
            while right >= left and nums[right] > pivot:
                right -= 1
    
            if left > right:
                break
            else:
                nums[left], nums[right] = nums[right], nums[left]
                left += 1
                right -= 1 # 这两行代码必须有 否则程序可能死循环 测试样例 [3,2,2,2,3]
        nums[first], nums[right] = nums[right], nums[first] # right处于<=v部分最后一个元素 
        return right
    
    

动态规划

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容