2021年秋招面试算法总结

面试常考算法
前言

  1. 每一道算法题,AC之后要会分析这道题的时间复杂度和空间复杂度,面试经常会被问到。
  2. 一道算法题只刷一遍肯定是不够的,做过的算法题刷3遍左右
  3. hard难度的题有时间可以刷,没时间不要勉强,因为投入产出比非常低,有这个时间把做过的题再温习一遍效果更好。
  4. 现在LeetCode的题量太大,盲目刷效率很低,我尽可能把面试常考的题罗列一下,先保证这些题掌握好,其他题你们自己根据实际情况去扩展。
  5. 有些题LeetCode上没有,我选择的是LintCode上的题,LintCode链接:https://www.lintcode.com/,LintCode的答案在这个链接中搜索:https://www.jiuzhang.com/solution/。
  6. 如果想进阿里的话,算法倒不是那么重要,阿里更注重项目深度,多深挖自己的项目和所用技术、框架的原理或者底层,还有Spring相关的原理多看看。

文章目录

一:排序

重点:快速排序、归并排序、堆排序(面试问排序基本就是这三个,理解并背熟)

class Solution:
    def sortIntegers2(self, A):
        # -------------------快速排序-------------------------------
        def quickSort(nums, left, right):
            if left >= right: return
            key = nums[left]
            i, j = left, right

            while i < j:
                while i < j and nums[j] >= key: j -= 1
                if i >= j: break
                nums[i] = nums[j]
                i += 1
                while i < j and nums[i] <= key: i += 1
                if i >= j: break
                nums[j] = nums[i]
                j -= 1

            nums[i] = key
            quickSort(nums, left, i-1)
            quickSort(nums, i+1, right)

        quickSort(A, 0, len(A)-1)

        # ---------------归并排序------------------------------------
        def merge(left, right):
            s = []
            i = j = 0

            while i < len(left) and j < len(right):
                if left[i] <= right[j]:
                    s.append(left[i])
                    i += 1
                else:
                    s.append(right[j])
                    j += 1

            if i < len(left): s += left[i:]
            if j < len(right): s += right[j:]
            return s

        def mergeSort(nums):
            if len(nums) < 2: return nums
            mid = len(nums)//2
            left = mergeSort(nums[:mid])
            right = mergeSort(nums[mid:])
            return merge(left, right)

        mergeSort(A)
        # ----------堆排序,从小到大--------------------------------
        def adjustHeap(arr, parent, length):
            temp = arr[parent]
            child = parent * 2 + 1  # 数组下标从0开始,左儿子

            while child < length:
                # 判断左子节点和右子节点的大小,若右边大,则把child定位到右边
                if child + 1 < length and arr[child] < arr[child + 1]:
                    child += 1

                # 若child大于父节点,则交换位置,否则退出循环
                if arr[child] > arr[parent]:
                    arr[parent] = arr[child]
                    parent = child
                    child = parent * 2 + 1
                else:
                    break
            arr[parent] = temp  # 调整到合适地方了!

        def heapSort(arr):
            # 构建大顶推,从最下面的非叶子节点开始向上遍历
            for i in range(len(arr) / 2 - 1, -1, -1):  # 左闭右开区间
                adjustHeap(arr, i, len(arr))
            # 循环执行以下操作:1.交换堆顶元素和末尾元素 2.重新调整为大顶堆
            for i in range(len(arr) - 1, -1, -1):
                arr[0], arr[i] = arr[i], arr[0]
                adjustHeap(arr, 0, i)  # length为开区间

一般:冒泡排序、选择排序、插入排序、拓扑排序

LintCode 463 Sort Integers(用冒泡排序、选择排序、插入排序都实现一遍)
class Solution:
    def sortIntegers(self, A):
        # write your code here
        # ----------简单的冒泡排序,还有双向冒泡------------        
        for i in range(len(A)):
            for j in range(1, len(A)):
                if A[j] < A[j-1]:
                    A[j], A[j-1] = A[j-1], A[j]

        return A

        # ----------选择排序------------------------------       

        for i in range(len(A)):
            idx = i
            for j in range(i+1, len(A)):
                if A[j] < A[idx]:  # 每一轮找最小值下标
                    idx = j
            A[i], A[idx] = A[idx], A[i]

        return A

        # ------------插入排序------------------------------

        for i in range(1, len(A) + 1):
            for j in range(i - 1, 0, -1):  # 从后往年进行
                if A[j - 1] > A[j]:
                    A[j - 1], A[j] = A[j], A[j - 1]
                else:
                    break
        return A
LeetCode 148 Sort List(链表排序,最简单是用归并排序做,有余力可以尝试快速排序来做)
class Solution(object):
    def sortList(self, head):
        def merge(head1, head2):  # 把两个链表合并成一个有序链表
            newHead = ListNode(-1)
            p = newHead
            while head1 and head2:
                if head1.val <= head2.val:
                    p.next = head1
                    p = p.next
                    head1 = head1.next
                else:
                    p.next = head2
                    p = p.next
                    head2 = head2.next
            
            if head1: p.next = head1
            if head2: p.next = head2
            
            return newHead.next
        
        def mergeSort(head):  # 使用快慢指针来切分链表
            if not head or not head.next:
                return head
            slow = head
            fast = head.next.next  # 这里需要走两步
            while fast and fast.next:
                slow = slow.next
                fast = fast.next.next
            
            head2 = mergeSort(slow.next)  # 下一段链表
            slow.next = None  # 断开链表
            head3 = mergeSort(head)  # 前一段链表
            return merge(head2, head3)
        
        return mergeSort(head)

Leetcode 23. Merge k Sorted Lists k个链表的排序,猿辅导面试还问k个数组的排序,二维数组的排序,把每一行看做一个数组,归并排序

k个链表归并排序:时间:O(N) + O(NlogK) 空间:O(1) 时间空间都是最优

def mergeKLists(self, lists):
	def merge(head1, head2): # 合并两个链表,有空链表时也可以!
            newHead = ListNode(-1)
            p = newHead
            while head1 and head2:
                if head1.val <= head2.val:
                    p.next = head1
                    p = p.next
                    head1 = head1.next
                else:
                    p.next = head2
                    p = p.next
                    head2 = head2.next
            
            if head1: p.next = head1
            if head2: p.next = head2
            
            return newHead.next
        
        n = len(lists)
        if n < 1: return None
        interval = 1 
        while interval < n:
            for i in range(0, n-interval, interval*2):  # 先两两合并
                lists[i] = merge(lists[i], lists[i+interval])
            
            interval *= 2  # 再四四合并,再八八合并
        
        return lists[0]
import sys
import heapq

# 节省内存,矩阵很大,正整数,没有范围
def printM(nums):
    for i in range(len(nums)):
        nums[i].sort()
    
    point = [0] * len(nums) # point[i] = j
    heap = []
    for i in range(len(nums)):
        heapq.heappush(heap, (i, nums[i][0]))  # 按照值排序
        point[i] += 1
    
    count = len(nums) * len(nums[0])
    
    while count > 0:   # 这里越界
        Min = heapq.heappop()
        print(Min[1])
        count -= 1
        row, col = Min[0], point[Min[0]]
        
        if col >= len(nums[0]): continue  # 这里越界
        point[Min[0]] += 1
        heapq.heappush(heap, (row, nums[row][col])
LeetCode 215 Kth Largest Element in an Array(快速选择算法)(面试题40:最小的k个数)滴滴问到

从大到小排序中的第k大的数,可以用堆来实现,但是不是最优解!

利用快排的性质,第k次pivot刚好是放好第k大的数,这就是快速选择算法啊!
时间复杂度为O(N)啊!

class Solution(object):
    def findKthLargest(self, nums, k):
        def partition(nums, left, right): # 从大到小排序!
            key = nums[left]
            while left < right:
                while left < right and nums[right] <= key: right -= 1
                if left >= right: break
                nums[left] = nums[right]
                left += 1
                while left < right and nums[left] >= key: left += 1
                if left >= right: break
                nums[right] = nums[left]
                right -= 1
            
            nums[left] = key
            return left
        
        # 这才是我面试想写的版本,当然还有另外的版本!
        left, right = 0, len(nums)-1
        while True:
            pivot = partition(nums, left, right)  # pivot的下标,也就是找到第几大的数

            if pivot < k-1:   # 第k大元素必出现在右子数组
                left = pivot + 1
            elif pivot > k-1:  # 第k大元素必出现在左子数组
                right = pivot - 1
            else:
                return nums[pivot]
LeetCode 347 Top K Frequent Elements(堆排序、桶排序)

先通过hashMap计数,然后再根据频率来排序,获得频率最高的k个元素!

class Solution(object):
    def topKFrequent(self, nums, k):
        
        # c = collections.Counter(nums)  # Counter({1: 3, 2: 2, 3: 1})
        # k_most_common = c.most_common(k)  # [(1, 3), (2, 2)]
        # return [element for element, count in k_most_common]  # [1, 2]
        
        # 桶排序,时间复杂度和空间复杂度都是O(N)
        bucket = [[] for i in range(len(nums)+1)]
        res = []
        freq = collections.Counter(nums)
        for key, val in freq.items():  # key为数字,val为这个数字出现的次数
            bucket[val].append(key)
        
        for i in range(len(bucket)-1, -1, -1):  # 从大往小找
            if len(bucket[i]): res += bucket[i]
            if len(res) >= k: return res[0:k]
LintCode 532 Reverse Pairs(归并排序的应用)(面试题51:数组中的逆序对)
class Solution:
    def InversePairs(self, data):
        # write code here
        if not data: return 0
        self.res = 0
        
        def divide(nums): # 不断划分子数组,返回的是数组
            if len(nums)==1:
                return nums
            mid = len(nums)//2
            left = divide(nums[:mid])
            right = divide(nums[mid:])
            return merge(left, right) # 合并子数组
        
        def merge(left, right):
            news = []  # 将nums1和nums2有序进行合并后的新数组
            i = j = 0
            while i < len(left) and j < len(right):
                if left[i] > right[j]: # 找到逆序对了!!!从大到小排序!
                    news.append(left[i])
                    self.res += (len(right)-j) # j之后的数都小于left[i]
                    i += 1
                else:
                    news.append(right[j])
                    j += 1
            if i < len(left): news += left[i:]
            if j < len(right): news += right[j:]
            return news
        
        divide(data)
        return self.res%1000000007

LeetCode 315 Count of Smaller Numbers After Self(归并排序的应用)
class Solution:
    def countSmaller(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        def divide(tupn):
            if len(tupn) == 1:
                return tupn
            mid = len(tupn) // 2
            left = divide(tupn[:mid])
            right = divide(tupn[mid:])
            return merge(left, right)
        
        def merge(left, right):
            news = []
            i = j = 0
            while i < len(left) and j < len(right):
                if left[i][0] > right[j][0]: # 从大往小排序的时候顺便把逆序对求出来了
                    news.append(left[i])
                    res[left[i][1]] += len(right) - j
                    i += 1
                else:
                    news.append(right[j])
                    j += 1
            if i < len(left): news += left[i:] 
            if j < len(right): news += right[j:]
            return news
        
        if not nums:
            return []
        res = [0] * len(nums)
        tupn = [(n,i) for i,n in enumerate(nums)]  # 元组
        divide(tupn)
        return res
LeetCode 207 Course Schedule (拓扑排序)
class Solution(object):
    def canFinish(self, numCourses, prerequisites):
        graph = collections.defaultdict(list)  #建立邻接表 1:[0]表示先学课程1,再学课程0
        inDegree = [0] * numCourses # 记录每一门课的入度,表示先修课程的门数
        for pair in prerequisites:
            graph[pair[1]].append(pair[0])
            inDegree[pair[0]] += 1
        
        for i in range(numCourses):
            circle = False  # 记录是否有入度为0的点
            for j in range(numCourses): # 找到一个入度为0的点作为遍历的起始点
                if inDegree[j] == 0: 
                    circle = True
                    break
            if not circle: return False  # 所有节点的入度都大于0,说明有环
            inDegree[j] = -1 # 这个入度为0的节点访问过了,不会再重复访问了
            
            for v in graph[j]: # 去掉入度为0的点和,邻接节点的入度减1
                inDegree[v] -= 1
        
        return True

LeetCode 210 Course Schedule II(拓扑排序)
class Solution(object):
    def findOrder(self, numCourses, prerequisites):
        graph = collections.defaultdict(list)
        indegree = [0] * numCourses
        res = []
        
        for pair in prerequisites:  # 建立邻接表
            graph[pair[1]].append(pair[0])
            indegree[pair[0]] += 1
        
        for i in range(numCourses):
            no_circle = False 
            for j in range(numCourses): # 寻找入度为0的节点
                if indegree[j] == 0:
                    no_circle = True
                    break
            if not no_circle: return []
            
            res.append(j) # 把入度为0的点记录下来
            indegree[j] = -1 # 把入度为0的点标记访问了
            
            for k in graph[j]:  # 修改入度为0的相邻节点的入度
                indegree[k] -= 1
        
        return res
leetcode 56 Merge Intervals (合并n个区间) 百度面试就问过
def merge(nums):
  if not nums or not nums[0]: return []
  nums.sort(key = lambda x : x[0])  # start
  res = []
  for i in range(len(nums)):
    if not res or res[-1][1] < nums[i][0]:
      res.append(nums[i])
    else:
      res[-1][1] = max(res[-1][1], nums[i][1])
  return res

nums = [[1, 1]]
print(merge(nums))

二:数组

442. Find All Duplicates in an Array(面试题3:数组中重复的数字)

要求:不能使用额外空间,时间复杂度为O(N)

class Solution(object):
    def findDuplicates(self, nums):
        if not nums: return []
        res = []
        
        for num in nums:
            index = abs(num) - 1
            if nums[index] < 0:
                res.append(index+1)
            nums[index] *= -1
        
        return res
leetcode 387. First Unique Character in a String(面试题50:第一个只出现一次的字符)
class Solution(object):
    def firstUniqChar(self, s):
        count = collections.Counter(s)
        for i in range(len(s)):
            if count[s[i]] == 1:
                return i
        
        return -1
                
LeetCode 189 Rotate Array(翻转数组)(面试题58:翻转字符串)
class Solution(object):
    def rotate(self, nums, k):      
        k =  k % len(nums) 
        if k == 0: return nums
        
        n = len(nums)
        
        for i in range(n//2):  # 所有值翻转
            nums[i], nums[n-i-1] = nums[n-i-1], nums[i]
        
        for i in range(k//2): # 前k个值翻转
            nums[i], nums[k-i-1] = nums[k-i-1], nums[i]
        
        i = k
        j = n-1
        while i < j: # 后n-k个值翻转
            nums[i], nums[j] = nums[j], nums[i]
            i += 1
            j -= 1
LintCode 31 Partition Array(快速排序中的一次partition)

把小于k的放在一边,大于k的数放另外一边

def partitionArray(self, nums, k):
        # write your code here
        if not nums: return 0
        
        def partition(nums, k):
            key = nums[0]
            i, j = 0, len(nums)-1
            
            while i < j:
                while i < j and nums[j] >= k: j -= 1  # 所有小于k的元素移到左边
                if i >= j: break
                nums[i] = nums[j]
                i += 1
                while i < j and nums[i] < k: i += 1 # 所有大于等于k的元素移到右边
                if i >= j: break
                nums[j] = nums[i]
                j -= 1
            
            nums[i] = key
            if nums[i] < k: return i+1  # 支点还是小
            return i
        
        return partition(nums, k)

LintCode 373 Partition Array by Odd and Even(快速排序中的一次partition)

分割一个整数数组,使得奇数在前偶数在后。

	def partitionArray(self, nums):
        # write your code here
        if not nums: return []
        
        i, j = 0, len(nums)-1
        key = nums[0]
        
        while i < j:
            while i < j and nums[j] % 2 == 0: j -= 1
            if i >= j: break
            nums[i] = nums[j]
            i += 1
            while i < j and nums[i] % 2 == 1: i += 1
            if i >= j: break
            nums[j] = nums[i]
            j -= 1
        
        nums[i] = key
LintCode 144 Interleaving Positive and Negative Numbers(快速排序中的一次partition)

给出一个含有正整数和负整数的数组,重新排列成一个正负数交错的数组

def rerange(self, A):
        # write your code here
        
        tag = 0 # 看正负数的个数, tag > 0, 负数多
        for a in A:
            if a > 0: tag -= 1
            else: tag += 1
        
        if not tag: tag = 1 # tag = 0表示正负数一样多
        i = 0
        j = 1
        
        while i < len(A) and j < len(A):
            while i < len(A) and tag * A[i] < 0: i += 2
            while j < len(A) and tag * A[j] > 0: j += 2
            if i < len(A) and j < len(A):
                A[i], A[j] = A[j], A[i]
            i += 2
            j += 2

LeetCode 54 Spiral Matrix(面试题29 顺时针打印矩阵)
res = []
if not matrix: return res
rowBegin = 0
rowEnd = len(matrix) - 1
colBegin = 0
colEnd = len(matrix[0]) - 1

while rowBegin <= rowEnd and colBegin <= colEnd:
    # to right
    for j in range(colBegin, colEnd+1): res.append(matrix[rowBegin][j])
    rowBegin += 1
    
    # to down
    for j in range(rowBegin, rowEnd+1): res.append(matrix[j][colEnd])
    colEnd -= 1
    
    # to left
    if rowBegin <= rowEnd:
        for j in range(colEnd, colBegin-1, -1): res.append(matrix[rowEnd][j])
        rowEnd -= 1
    
    # to up
    if colBegin <= colEnd:
        for j in range(rowEnd, rowBegin-1, -1): res.append(matrix[j][colBegin])
        colBegin += 1

return res

LeetCode 59 Spiral Matrix II (第一行,最后一列,最后一行,第一列,四个循环)
class Solution(object):
    def generateMatrix(self, n):
        if n == 1: return [[1]]
        matrix = [[0]*n for i in range(n)] # n*n的矩阵
        elem = range(n*n) # 0 - n*n-1
        
        rowBegin = 0
        rowEnd = n - 1
        colBegin = 0
        colEnd = n - 1
        i = 0
        while rowBegin <= rowEnd and colBegin <= colEnd:
            # to right
            for j in range(colBegin, colEnd+1): 
                matrix[rowBegin][j] = elem[i] + 1
                i += 1
            rowBegin += 1
            
            # to down
            for j in range(rowBegin, rowEnd+1): 
                matrix[j][colEnd] = elem[i] + 1
                i += 1
            colEnd -= 1
            
            # to left
            if rowBegin <= rowEnd:
                for j in range(colEnd, colBegin-1, -1): 
                    matrix[rowEnd][j] = elem[i] + 1
                    i += 1
                rowEnd -= 1
            
            # to up
            if colBegin <= colEnd:
                for j in range(rowEnd, rowBegin-1, -1): 
                    matrix[j][colBegin] = elem[i] + 1
                    i += 1
                colBegin += 1
            
        return matrix

leetcode 560. Subarray Sum Equals K

求子数组之和等于k的个数,这个数组里有负数啊!一定得记住,当出现负数时,不能使用双指针来求子数组之和了,因为移动指针的条件变得不清楚了

class Solution(object):
    def subarraySum(self, nums, k):
        ans = 0
        sum_ = 0
        dict_ = collections.defaultdict(int)
        
        dict_[0] = 1 # 初始化,防止所有元素都为0
        
        for i in range(len(nums)):
            sum_ += nums[i]
            if sum_ - k in dict_:
                ans += dict_[sum_ - k]
            dict_[sum_] += 1
                    
        return ans
LintCode 138 Subarray Sum(子数组之和 = 0,hashmap、空间换时间)

找到和为 0的子数组。

def subarraySum(self, nums):
        # write your code here
        map = {0:-1}
        sum = 0
        
        for i in range(len(nums)):
            sum += nums[i]
            if sum in map:
                return [map[sum]+1,i]
            map[sum] = i
        return []
LintCode 139 Subarray Sum Closest(排序)

LeetCode 136 Single Number(位操作、异或)

一个数组中,只有一个元素出现一次,其余元素都出现了两次,找出那个出现一次的元素

from collections import Counter
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        # 解法一
        # return (2 * (sum(set(nums)))) - sum(nums)
        
        # 解法二
        # return collections.Counter(nums).most_common()[-1][0]
        
        # 解法三
        a = 0
        for i in nums:
            a ^= i
        return a
LeetCode 137 Single Number II(位操作、通用解法)

除了一个元素出现一次,其余元素出现三次
要求不适用额外空间,线性时间复杂度

# 解法一:数学方法,使用了额外的空间,不符合题意要求!
return (sum(set(nums)) * 3 - sum(nums) ) /2
class Solution {
    public int singleNumber(int[] nums) {
        int res = 0;
        for(int i = 0; i < 32; i++){
            int sum = 0;
            for(int n: nums)
                if((n >> i & 1) == 1)  //看第i位是否为1
                    sum++;
            sum %= 3;
            res = res | sum<<i; //还原那个出现一次的数
        }
        return res;
    }
}

/*
统计每一位为1的数字的个数,当不是3的倍数时,说明这个1是出现一次数字的
*/
LeetCode 260 Single Number III(位操作)(面试题56:数组中数字出现的次数)

数组中有两个数只出现一次,其余数都出现两次,找出这两个只出现一次的数

class Solution(object):
    def singleNumber(self, nums):
        if len(nums) <= 2: return nums
        # 第一步
        xor = 0
        for num in nums:
            xor ^= num
        
        # 第二步
        idx = 0
        while xor & 1 == 0:
            xor >>= 1 # 右移一位
            idx += 1
        
        # 第三步
        res1 = res2 = 0
        for num in nums:
            if self.isBit(num, idx):
                res1 ^= num
            else:
                res2 ^= num
        
        return [res1, res2]
    
    # 判断数字num的第idx位是否为1
    def isBit(self, num, idx):
        num >>= idx
        return num&1
LeetCode 263 Ugly Number

因子只能是2,3,5的数就是丑数

class Solution(object):
    def isUgly(self, num):
        if num <= 0: return False
        while num % 2 == 0: num /= 2
        while num % 3 == 0: num /= 3
        while num % 5 == 0: num /= 5
        return num == 1
        
LeetCode 264 Ugly Number II(面试题49:丑数)

求第n个丑数

class Solution(object):
    def nthUglyNumber(self, n):
        if n < 0: return -1
        if n==1: return 1
        ugly = [0] * n
        ugly[0] = 1
        i2 = i3 = i5 = 0
        next2, next3, next5 = 2, 3, 5
        
        for i in range(1, n):
            ugly[i] = min(next2, next3, next5)
            
            if ugly[i] == next2:
                i2 += 1
                next2 = ugly[i2] * 2
            if ugly[i] == next3:
                i3 += 1
                next3 = ugly[i3] * 3
            if ugly[i] == next5:
                i5 += 1
                next5 = ugly[i5] * 5
                
        # print(ugly)
        return ugly[-1]
LeetCode 295 Find Median from Data Stream(数据流中位数,设计、最大堆、最小堆)(面试题41:数据流中的中位数)剑指offer
import heapq
class MedianFinder(object):
    def __init__(self):
        self.L = 0
        self.max_heap = []
        self.min_heap = []
        
    def addNum(self, num):
        self.L += 1
        heapq.heappush(self.max_heap, -num) # 建立最大堆,默认是最小堆
        max_heap_top = heapq.heappop(self.max_heap)  # 弹出堆顶
        heapq.heappush(self.min_heap, -max_heap_top) 
        
        if self.L & 1: # 奇数个,则大顶推多一个元素,大顶堆的堆顶就是中位数
            min_heap_top = heapq.heappop(self.min_heap)
            heapq.heappush(self.max_heap, -min_heap_top)
    def findMedian(self):
        if self.L & 1: 
            return -self.max_heap[0]
        else:
            return (-self.max_heap[0] + self.min_heap[0])/2.0
LeetCode 480 Sliding Window Median(滑动窗口中位数,最大堆、最小堆)

LeetCode 239 Sliding Window Maximum(单调双端队列)(面试题59:队列的最大值)
class Solution(object):
    def maxSlidingWindow(self, num, size):
        # write code here
        res = []
        if size==0: return res
        d = collections.deque()  # 双端队列,记录的是下标啊!
        begin = 0
        for i in range(len(num)):
            begin = i-size+1 # begin~begin+i-1为窗口的下标的范围
            if not d:
                d.append(i)
            elif begin > d[0]: # 最大值在移出窗口了
                d.popleft()
            
            while d and num[d[-1]] <= num[i]: # 进来了一个更大值
                d.pop()
            d.append(i)  # 记录的是下标啊!
            if begin >= 0:
                res.append(num[d[0]])
        return res
LeetCode 128 Longest Consecutive Sequence(hashset、空间换时间,时空复杂度为O(N))

求最长的连续序列
Input: [100, 4, 200, 1, 3, 2]
Output: 4
Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.

def longestConsecutive(self, nums):
	res = 0
    nums_set = set(nums)
    
    for num in nums_set:
        if num-1 not in nums_set:
            y = num + 1
            while y in nums_set:
                y += 1
            res = max(res, y-num) # 更新最长连续数组
    
    return res 
LeetCode 169 Majority Element(面试题39:数组中出现次数超过一半的数字)

可能并不存在那个超过半数的数,此时需要特殊判断!

class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        count = 1
        res = numbers[0]
        n = len(numbers)
        for i in range(1,n):
            if numbers[i] == res:
                count += 1
            else:
                count -= 1
                if count <= 0: 
                    res = numbers[i]
                    count = 1
        
        if count > 0:  # count==1时可能是最后一个数,但是不是出现超过一半
            tmp = 0
            for i in range(n):
                if numbers[i] == res:
                    tmp += 1
            if tmp > n//2:
                return res
        
        return 0

三:双指针

LeetCode 392 Is Subsequence(双指针)

判断字符串s1是否是字符串s2的子序列,子序列不要求连续,但是得相对有序

思考题:当多次查询时如何实现?
// Eg-1. s=“abc”, t=“bahbgdca”
// idx=[a={1,7}, b={0,3}, c={6}]
// i=0 (‘a’): prev=1
// i=1 (‘b’): prev=3
// i=2 (‘c’): prev=6 (return true)
// Eg-2. s=“abc”, t=“bahgdcb”
// idx=[a={1}, b={0,6}, c={5}]
// i=0 (‘a’): prev=1
// i=1 (‘b’): prev=6
// i=2 (‘c’): prev=? (return false)

class Solution(object):
    def isSubsequence(self, s, t):
        idx = collections.defaultdict(list)
        for i, c in enumerate(t): # 记录t中每个字符的位置,然后当多次输入s时,只需要查询一下就行!
            idx[c].append(i)
        
        # 开始查询了!
        prev = 0  # 记录前一个字符的位置,之后查询只能在后面的位置查
        for i, c in enumerate(s):            
            j = bisect.bisect_left(idx[c], prev) # 查看第i个字符是否存在
            if j == len(idx[c]): return False   # 不存在时
            prev = idx[c][j] + 1 
        return True 
LeetCode 1 Two Sum (两个数字之和=target,hashmap,空间换时间)
class Solution(object):
    def twoSum(self, nums, target):
        h = {}
        for i, num in enumerate(nums):
            n = target - num
            if n not in h:
                h[num] = i
            else:
                return [h[n], i]

leetcode 167 Two Sum II - Input array is sorted(双指针是最优解,或者hashmap或者二分查找,面试题57:和为s的数字)
class Solution(object):
    def twoSum(self, nums, target):
# --------方法一:双指针--------------------------
#         if len(nums) < 2: return []
#         low, high = 0, len(nums)-1
        
#         while low < high:
#             if nums[low] + nums[high] > target:
#                 high -= 1
#             elif nums[low] + nums[high] < target:
#                 low += 1
#             else:
#                 return [low+1, high+1]
#         return []

# ------------方法二:hashmap-----------------
        # if len(nums) < 2: return []
        # mapp = {}
        # for idx, num in enumerate(nums):
        #     if target - num not in mapp:
        #         mapp[num] = idx
        #     else:
        #         return [mapp[target-num]+1, idx+1]
        # return []
# -------方法三:二分查找--------------------------
        
#         if len(nums) < 2: return []
#         def bisect_right(nums, target): # 寻找最右边的那个数
#             l, r = 0, len(nums) - 1
#             while l < r:
#                 m = (l + r) // 2 + 1
#                 if nums[m] > target:
#                     r = m - 1
#                 else:
#                     l = m
#             return l if nums[l] == target else -1

        
#         for idx, num in enumerate(nums):
#             j = bisect_right(nums, target-num)
#             if j != -1 and j != idx:
#                 return [idx+1, j+1]

#  -----第二次重写双指针----------
        if len(nums) < 2: return []
        i, j = 0, len(nums)-1
        
        while i < j:
            if nums[i] + nums[j] > target:
                j -= 1
            elif nums[i] + nums[j] < target:
                i += 1               
            else:
                return [i+1, j+1]
        
        return []
            
LeetCode 15 3Sum (排序+双指针) 百度二面面试问到了
class Solution(object):
    def threeSum(self, nums):
        if len(nums) < 3: return []
    
        res = []
        nums.sort()
        n = len(nums)
        
        for i in range(n-2):
            if nums[i]>0: break
            if i>0 and nums[i]==nums[i-1]: continue # 去重
                
            # 固定nums[i],双指针遍历寻找另外两个数,可能有多个结果
            left, right = i+1, n-1
            while left < right:
                total = nums[i] + nums[left] + nums[right]
                if total < 0:
                    left += 1
                elif total > 0:
                    right -= 1
                else:
                    res.append([nums[i], nums[left], nums[right]])  
                    while left < right and nums[left] == nums[left+1]: # 去重
                        left += 1
                    
                    while left < right and nums[right] == nums[right-1]:
                        right -= 1
                    
                    left += 1
                    right -= 1
        return res
LeetCode 16 3Sum Closest (排序+双指针)
class Solution(object):
    def threeSumClosest(self, nums, target):
        
        closed = float('inf') # 绝对值之差
        
        nums.sort()
        for k in range(len(nums)-2):
            # 只有一个答案,去重单独考虑
            
            i, j = k+1, len(nums)-1 
            
            while i < j:
                sums = nums[k] + nums[i] + nums[j]  # 三数之和
                if abs(target - sums) < abs(closed): # 新来的距离更近
                    closed = target - sums  # 更新啊
                    if closed == 0: # 三数之和刚好等于target,此时就是距离最近的
                        return sums # 返回的是最近的三数之和,不是diff
                        
                if sums < target:
                    i += 1
                else:
                    j -= 1
            
        return target - closed  # 三数之和
LeetCode 18 4Sum
class Solution(object):
    def fourSum(self, nums, target):
        if len(nums) < 4: return []
        res = []
        nums.sort()
        
        for i in range(len(nums)-3):
            if i > 0 and nums[i] == nums[i-1]: continue  # 优化
            
            for j in range(i+1, len(nums)-2):
                if j > i+1 and nums[j] == nums[j-1]: continue # 优化
                low, high = j + 1, len(nums) - 1
                
                while low < high:
                    total = nums[i] + nums[j] + nums[low] + nums[high]
                    
                    if total > target:
                        high -= 1
                    elif total < target:
                        low += 1
                    else:
                        res.append([nums[i], nums[j], nums[low], nums[high]])
                        while low < high and nums[high] == nums[high-1]: high -= 1
                        while low < high and nums[low] == nums[low+1]: low += 1
                        
                        low +=1
                        high -= 1
        
        return res      

leetcode 3 Longest Substring Without Repeating Characters(抖音面试问到,我做过但是没印象了)(面试题48:最长不含重复字符的子字符串)(hashmap 加双指针(滑动窗口))
class Solution(object):
    def lengthOfLongestSubstring(self, s):
		i = j = 0
        res = 0
        mapp = collections.defaultdict() # 字母:下标
        for j in range(len(s)):
            if s[j] in mapp: # 把i直接跳到重复元素那
                i = max(mapp[s[j]] + 1, i) # s[j]重复了,更新到j+1
            res = max(res, j-i+1)  # 更新窗口大小
            mapp[s[j]] = j   # 更新每个字母出现的下标
        return res
LeetCode 42 Trapping Rain Water(单调栈) 腾讯字节面过
class Solution(object):
    def trap(self, height):
        if not height: return 0
        stack = []
        res = 0
        n = len(height)
        
        for i in range(n):
            while stack and height[stack[-1]] < height[i]: # 遇到一个更高的高度
                pre = stack.pop() # 左起第一个小的元素的下标
                if not stack: break # 不能蓄水
                res += (min(height[i], height[stack[-1]]) - height[pre]) * (i - stack[-1] - 1)
            stack.append(i)
        
        return res
470. Implement Rand10() Using Rand7() 腾讯面过,字节面了,还问最优解

在这里插入图片描述
方法一:平均调用rand7()的次数是:2.45

class Solution{
    public int rand10() {
        int row, col, idx;
        do {
            row = rand7();
            col = rand7();
            idx = col + (row - 1) * 7;  // 把二维表当作一维表中的位置,下标从0开始
        } while (idx > 40);
        return 1 + (idx - 1) % 10;  // 对应二维表中的数
    }
}

方法二:平均调用rand7()的次数是:2.2123

class Solution{
    public int rand10() {
        int a, b, idx;
        while(true){
            a = rand7();
            b = rand7();
            idx = b + (a - 1) * 7;
            if (idx <= 40)
                return 1 + (idx - 1) % 10;
            
            // 如果落在41-49之间,我们再生成 1 - 63 的范围
            // a的范围是1-9,b的范围是1-7,所以构成的矩阵是7*9=63的大小
            a = idx - 40;  //这里可以少调用一次rand7(),直接用上次的就行
            b = rand7();
            idx = b + (a - 1) * 7;
            if (idx <= 60)
                return 1 + (idx - 1) * 7;
            
            // 如果落在61-63之间,则再生成 1 - 21 的范围
            a = idx - 60;
            b = rand7();
            idx = b + (a - 1) * 7;
            if(idx <= 20)
                return 1 + (idx - 1) % 10;
        }
    }
}

三:链表

链表题没什么好说的,就是一些指针的操作,注意以下两点就行
7. 当你预估到返回的链表头结点可能跟原有的链表头节点不一样时,建一个虚拟节点dummy,值任意,比如0,最后返回的新的链表头结点就是dummy.next,这一条非常好用!
8. 当操作一个链表节点的时候,时刻想一想要访问的链表节点是否为null

LeetCode 206 Reverse Linked List(递归、迭代都问过)(面试题24 反转链表)
class ListNode(object):
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution(object):
    def reverseList(self, head):
# ------方法一:递归版本------------------------------------
        if not head or not head.next: return head
        last = self.reverseList(head.next)
        head.next.next = head
        head.next = None
        return last
    
# -------方法二:非递归版本:三指针------------------------------------
        if not head or not head.next: return head
        pre = None
        p = head
        nextp = head.next
        
        while p:
            nextp = p.next
            p.next = pre
            pre = p
            p = nextp
       
        return pre
面试题6 从尾到头打印链表

可以用栈来做!

class Solution:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
# -------方法一:用了空间----------------------------
#        ans = []
#        p = listNode
#        while p:
#            ans.append(p.val)
#            p = p.next
#        return ans[::-1] # 切片来逆序输出

# ------方法二:递归--------------------
        if listNode is None: return [] 
        return self.printListFromTailToHead(listNode.next) + [listNode.val]
LeetCode 92 Reverse Linked List II (和k个节点翻转类似,reverse(start, end) )

翻转m-n之间的链表,并且只能走一趟遍历啊,要求是one-pass,以前写的先数出m-n之间的链表再调用翻转函数是不符合要求的!

class Solution:
    def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
        # 使用四个指针,dummy, pre, start, then
        if not head: return head
        dummy = ListNode(-1)
        dummy.next = head
        pre = dummy
        for i in range(m-1): # 走m-2步,到开始翻转的前一个节点
            pre = pre.next
        
        start = pre.next
        then = pre.next.next
        
        # 开启翻转
        for i in range(n-m): 
            start.next = then.next
            then.next = pre.next
            pre.next = then
            
            then = start.next
        
        return dummy.next

'''
// 1 - 2 -3 - 4 - 5 ; m=2; n=4 ---> pre = 1, start = 2, then = 3
// dummy-> 1 -> 2 -> 3 -> 4 -> 5

// first reversing : dummy->1 - 3 - 2 - 4 - 5; pre = 1, start = 2, then = 4
// second reversing: dummy->1 - 4 - 3 - 2 - 5; pre = 1, start = 2, then = 5 (finish)
'''
LeetCode 25. K 个一组翻转链表
def reverseKGroup(self, head, k):
        dummy = jump = ListNode(-1)  # 头结点
        dummy.next = head
        left = right = head   # 标记开始和结束
        
        while True:
            count = 0
            
            while right and count < k:
                right = right.next
                count += 1
            
            if count == k:
                cur, pre = left, right
                for i in range(k):
                    tmp = cur.next
                    cur.next = pre
                    pre = cur
                    cur = tmp
                
                jump.next = pre
                jump = left
                left = right
            else:
                return dummy.next
LeetCode 86 Partition List (LeetCode 328 Odd Even Linked List 思想一样,乍看题还没想到怎么做,重点注意一下!)

划分链表,把小于x的放前边,大于等于x放后面

class Solution(object):
    def partition(self, head, x):
        if not head or not head.next: return head
        small = large = None
        headSmall = headLarge = None
        p = head
        
        while p:
            if p.val < x:
                if not headSmall:
                    small = headSmall = p
                else:
                    small.next = p
                    small = small.next
            else:
                if not headLarge:
                    large = headLarge = p
                else:
                    large.next = p
                    large = large.next
            
            p = p.next
        
        if not headSmall or not headLarge: return head
        
        # 防止出现环啊
        if small: small.next = None
        if large: large.next = None
           
        small.next = headLarge
        return headSmall
LeetCode 83 Remove Duplicates from Sorted List (删除重复元素,但是重复元素还留一个)
class Solution(object):
    def deleteDuplicates(self, head):
        if not head or not head.next: return head
        p = head
        
        while p:
            while p.next and p.val == p.next.val:
                p.next = p.next.next
            p = p.next
            
        return head
LeetCode 82 Remove Duplicates from Sorted List II(重复元素都删除,剑指Offer上也有,递归和迭代两种方法来做)猿辅导面试问过我
class ListNode():
    def __init__(self, value):
        self.val = value
        self.next = None

def deleteDup(head):
    if not head or not head.next: return head
    dummy = ListNode(-1)
    dummy.next = head
    
    p = dummy
    cur = head
    while cur and cur.next:
        if cur.val != cur.next.val:
            p = p.next
            cur = cur.next
        else:
            val = cur.val
            while cur and cur.val == val:
                cur = cur.next
            p.next = cur
    
    return dummy.next
LeetCode 237 Delete Node in a Linked List(面试题18 删除链表的节点,只给需要删除的节点,所以通过交换值后删除后一个元素)
class Solution(object):
    def deleteNode(self, node):
        tmp = node.next.val
        node.next.val = node.val
        node.val = tmp
        
        node.next = node.next.next
LeetCode 19 Remove Nth Node From End of List(面试题22:链表中倒数第k个节点,要求one pass,所以使用双指针来做,并且考虑首位元素的删除,需要建立虚拟头结点啊!)

面试还问你特殊情况怎么处理,也就是空链表,k的范围考虑
k<=0 or k>L时 :不删出
1<=k<=L:删除第k个

class Solution(object):
    def removeNthFromEnd(self, head, n):
        if not head: return None
        if not head.next and n == 1: return None
        
        newHead = ListNode(-1)
        newHead.next = head
        
        start = end = head
        pre = newHead
        while n > 1:
            end = end.next
            n -= 1
        
        while end.next:
            pre = pre.next
            start = start.next
            end = end.next
        
        # 删中间元素还是最后一个元素,需要区分!
        if start.next:
            pre.next = start.next
        else:
            pre.next = None
        return newHead.next
LeetCode 203 Remove Linked List Elements(移出链表中所有值为val的节点)
class Solution(object):
    def removeElements(self, head, val):
        if not head: return None
        
        newHead = ListNode(-1)
        newHead.next = head
        
        pre = newHead
        p = head
        
        while p:
            if p.val == val:
                pre.next = p.next
                p = p.next
            else:
                p = p.next
                pre = pre.next
        
        return newHead.next
LeetCode 61 Rotate List(长度取余,双指针遍历)

链表右移k位,溢出部分放头部

class Solution(object):
    def rotateRight(self, head, k):
        if not head or not head.next: return head
        
        L = 0
        p = head
        
        while p:
            L += 1
            p = p.next
        
        k %= L
        if k <= 0: return head
        # 双指针,距离k步
        pre = None
        p = q = head
        
        while k:
            q = q.next
            k -= 1
        
        #print(q.val)
        while q.next:
            p = p.next
            q = q.next
        res = p.next
        #print(p.val, q.val)
        p.next = None
        q.next = head
        return res
LeetCode 2 Add Two Numbers(链表相加,考虑进位,重写没写好,注意一下,时空复杂度都是O(M+N)啊)

两个链表存的数,左对齐了,把链表相加,获得结果也是个链表,并且进位是从左向右进位的,最高位在最右边!

class Solution(object):
    def addTwoNumbers(self, head1, head2):
        if not head1: return head2
        if not head2: return head1
        
        # 所有值都加到head1上来就行
        p1, p2 = head1, head2
        carry = 0
        dummy = ListNode(-1)
        cur = dummy
        
        while p1 or p2:
            x = p1.val if p1 else 0
            y = p2.val if p2 else 0
            
            val = x + y + carry
            
            if val < 10:
                carry = 0
            else:
                val -= 10
                carry = 1
            
            cur.next = ListNode(val)
            cur = cur.next
            if p1: p1 = p1.next
            if p2: p2 = p2.next
        
        if carry:
            cur.next = ListNode(carry)
        return dummy.next
LeetCode 445 Add Two Numbers II (用栈来实现,头部插入)

右对齐,进位从右到左边,但是链表只能从前往后遍历,因此需要栈来存链表中的数!

class Solution(object):
    def addTwoNumbers(self, l1, l2):
        if not l1: return l2
        if not l2: return l1
        
        stack1, stack2 = [], []
        p1, p2 = l1, l2
        dummy = ListNode(-1)
        carry = 0
        
        while p1:
            stack1.append(p1.val)
            p1 = p1.next
        
        while p2:
            stack2.append(p2.val)
            p2 = p2.next
        
        while stack1 or stack2:
            x = stack1.pop() if stack1 else 0
            y = stack2.pop() if stack2 else 0
            val = x + y + carry
            
            if val < 10:
                carry = 0
            else:
                carry = 1
                val -= 10
            
            # 头插法
            tmp = dummy.next
            dummy.next = ListNode(val)
            dummy.next.next = tmp
        
        if carry:
            tmp = dummy.next
            dummy.next = ListNode(carry)
            dummy.next.next = tmp
        
        if not dummy.next.next and dummy.next.val == 0: return dummy.next
        return dummy.next if dummy.next.val > 0 else dummy.next.next
LeetCode 234 Palindrome Linked List(快慢指针+反转一半链表,又没写好,如果可以使用多余空间,用双端队列挺不错)

判断 链表是否是回文子串

class Solution(object):
    def isPalindrome(self, head):
        if not head or not head.next: return True
        if not head.next.next: return True if head.val == head.next.val else False
        
        slow, fast = head, head.next # 奇数个节点时,前一半可能多一个元素
        
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        
        head1 = head
        head2 = self.reverse(slow)
        
        while head1 and head2:
            if head1.val != head2.val: return False
            head1 = head1.next
            head2 = head2.next
        
        return True
    
    def reverse(self, head):
        if not head or not head.next: return head
        last = self.reverse(head.next)
        head.next.next = head
        head.next = None
        return last
LeetCode 24 Swap Nodes in Pairs (一时半会儿没写好,多看看!定义swap函数进行两两交换啊!)

成对交换链表中的节点,不是修改值

class Solution(object):
    def swapPairs(self, head):
        if not head or not head.next: return head
        
        def swap(p, q):
            p.next = None
            q.next = p
            return q, p
        
        dummy = ListNode(-1)
        dummy.next = head
        pre = dummy
        p, q = head, head.next
        
        while p and q:
            last = q.next
            first, second = swap(p, q)
            pre.next = first
            pre = p
            p = last
            if p and p.next: 
                q = p.next
            else:
                q = None
        
        if p: pre.next = p
        return dummy.next
LeetCode 160 Intersection of Two Linked Lists(面试题52:两个链表的第一个公共结点) (第一次写是计算长度,让某一指针先走长度之差,第二次写不需要计算长度,)
class Solution(object):
    def getIntersectionNode(self, headA, headB):
        if not headA or not headB: return None
        a, b = headA, headB
        
        while a != b:
            a = a.next if a else headB
            b = b.next if b else headA
        return a
LeetCode 141 Linked List Cycle(面试题23:链表中环的入口节点,快慢指针)
class Solution(object):
    def hasCycle(self, head):
        if not head or not head.next: return False
        slow = fast = head
        
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast: return True
        
        return False
LeetCode 142 Linked List Cycle II(先找相遇节点,再一个指针从头走,一个节点从相遇节点走,直到再次相遇!)
class Solution(object):
    def detectCycle(self, head):
        if not head or not head.next: return None
        
        slow = fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:  # 有环,来找环的入口结点
                p, q = head, fast
                
                while p != q:
                    p = p.next
                    q = q.next
                
                return p
        return None
LeetCode 21 Merge Two Sorted Lists(面试题25:合并两个排序的链表,虚拟头结点,不需要创建新节点,在原来链表上修改next指针就行,循环or 递归都行)
class Solution(object):
    def mergeTwoLists(self, l1, l2):
        if not l1 or not l2: return l1 or l2
        dummy = ListNode(-1)
        pre = dummy
        p, q = l1, l2
        
        while p and q:
            if p.val < q.val:
                pre.next = p
                p = p.next
            else:
                pre.next = q
                q = q.next
            
            pre = pre.next
        
        pre.next = p or q
        return dummy.next
LeetCode 23 Merge k Sorted Lists(变型:N个倒序链表合并,循环控制两两合并再四四合并等等!,循环内条件注意不要越界!)
class Solution(object):
    def mergeKLists(self, lists):
    	def merge(head1, head2):
            dummy = ListNode(-1)
            pre = dummy
            p, q = head1, head2
            
            while p and q:
                if p.val <= q.val:
                    pre.next = p
                    p = p.next
                else:
                    pre.next = q
                    q = q.next
                
                pre = pre.next
            
            pre.next = p or q
            return dummy.next
        
        if not lists: return None
        intervals = 1
        while intervals < len(lists):
            for i in range(0, len(lists)-intervals, 2*intervals):
                lists[i] = merge(lists[i], lists[i+intervals])
            
            intervals *= 2
        
        return lists[0]
LeetCode 148. Sort List (归并排序,快慢指针进行divide,然后进行merge)
class Solution(object):
    def sortList(self, head):
        if not head: return None
        
        def merge(head1, head2):
            dummy = ListNode(-1)
            pre = dummy
            p, q = head1, head2
            
            while p and q:
                if p.val <= q.val:
                    pre.next = p
                    p = p.next
                else:
                    pre.next = q
                    q = q.next
                
                pre = pre.next
            
            pre.next = p or q
            return dummy.next
        
        def mergeSort(head):
            if not head or not head.next:
                return head
            slow = head
            fast = head.next.next
            while fast and fast.next:
                slow = slow.next
                fast = fast.next.next
            head2 = slow.next
            slow.next = None
            left = mergeSort(head)
            right = mergeSort(head2)
            return merge(left, right)
        
        return mergeSort(head)

也可以使用快速排序啊

class Solution(object):
    def sortList(self, head):
        def partition(start, end):
            node = start.next.next
            pivotPrev = start.next
            pivotPrev.next = end
            pivotPost = pivotPrev
            while node != end:
                temp = node.next
                if node.val > pivotPrev.val:
                    node.next = pivotPost.next
                    pivotPost.next = node
                elif node.val < pivotPrev.val:
                    node.next = start.next
                    start.next = node
                else:
                    node.next = pivotPost.next
                    pivotPost.next = node
                    pivotPost = pivotPost.next
                node = temp
            return [pivotPrev, pivotPost]
        
        def quicksort(start, end):
            if start.next != end:
                prev, post = partition(start, end)
                quicksort(start, prev)
                quicksort(post, end)

        newHead = ListNode(0)
        newHead.next = head
        quicksort(newHead, None)
        return newHead.next
LeetCode 138 Copy List with Random Pointer(面试题35:复杂链表的复制,深拷贝,首先复制节点,再建立联系,用dict来保存,注意指针不能指向None,key为原始节点,value为新建节点)
class Solution(object):
    def copyRandomList(self, pHead):
        if not pHead: return None
        randomDict = collections.defaultdict(Node)
        p = pHead
        
        while p:
            randomDict[p] = Node(p.val) # 首先创建节点
            p = p.next
        
        p = pHead
        while p: # 然后建立关系
            if p.next: randomDict[p].next = randomDict[p.next]
            if p.random: randomDict[p].random = randomDict[p.random]
            p = p.next
        
        return randomDict[pHead]
LeetCode 143 Reorder List(很久以前做过,竟然忘了思路了,快慢指针把原始数组划分前后两部分,翻转后一半,再两段进行合并!)

Given a singly linked list L: L0→L1→…→Ln-1→Ln,
reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→…

class Solution(object):
    def reorderList(self, head):
        # 不是修改里面的值,而是改变节点!
        if not head or not head.next or not head.next.next: return head
        
        slow = head
        fast = head.next.next
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        
        head2 = slow.next
        slow.next = None
        
        # 链表反转
        head2 = self.reverse(head2)
        
        # 合并前后两个链表
        p1, p2 = head, head2
        while p1.next and p2.next:
            tmp1 = p1.next
            tmp2 = p2.next
            p1.next = p2
            p2.next = tmp1
            p1 = tmp1
            p2 = tmp2
        
        p1.next = p2
        return head
            
    
    def reverse(self, head):
        pre = None
        p = head
        nextp = None
        
        while p:
            nextp = p.next
            p.next = pre
            pre = p
            p = nextp
        return pre
LeetCode 146 LRU Cache(面试问到,非常重要的一道题,双链表 + HashMap解决) 字节面试问到过,如何优化?
class LRUCache(object):
    def __init__(self, capacity):
        self.capacity = capacity
        
        self.head = LinkedNode(-1, -1) # 有点问题
        self.tail = LinkedNode(-2, -2)
        self.head.next = self.tail
        self.tail.pre = self.head
        self.hashMap = collections.defaultdict(LinkedNode) # key:LinkedNode
        # HashMap 的 Value 指向双向链表实现的 LRU 的 Node 节点

    def get(self, key):
        res = -1
        if key in self.hashMap:
            curNode = self.hashMap[key]
            res = curNode.val
            self._remove(curNode)
            self._add(curNode)
        return res

    def put(self, key, value):
        # 如果存在,则删除
        if key in self.hashMap:
            self._remove(self.hashMap[key])
        
        # 插入头部
        curNode = LinkedNode(key,value)
        self._add(curNode)
        self.hashMap[key] = curNode
        
        if len(self.hashMap) > self.capacity:
            last = self.tail.pre
            self._remove(last)
            self.hashMap.pop(last.key) # 删除的是最后一个节点
        
    def _remove(self, node): # 删除某节点
        p = node.pre
        q = node.next
        p.next = q
        q.pre = p
    
    def _add(self, node): # 插入到头部
        p = self.head.next
        node.next = p
        self.head.next = node
        p.pre = node
        node.pre = self.head

五:二分查找:精髓在于如何排除一半的元素

LeetCode 35 Search Insert Position(bisect_left来寻找插入位置)
class Solution(object):
    def searchInsert(self, nums, target):
        if not nums: return 0
        if target < nums[0]: return 0
        if target > nums[-1]: return len(nums)
        
        low, high = 0, len(nums)-1
        
        while low < high:
            mid = low + (high-low)//2
            if nums[mid] < target:  # 修改左边界,
                low = mid + 1
            else:
                high = mid
        return low
LeetCode 34 Find First and Last Position of Element in Sorted Array(面试题53:数字在排序数组中出现的次数 ) (bisect_left,bisect_right,两者相减)
class Solution(object):
    def searchRange(self, nums, target):
        if not nums: return [-1, -1]
        
        def bisect_left(nums, target):  # 寻找最左边的那个数,更新哪一边就是找对的最那边的数
            l, r = 0, len(nums) - 1
            while l < r:
                m = (l + r) // 2
                if nums[m] < target:
                    l = m + 1
                else:
                    r = m
            return l if nums[l] == target else -1

        def bisect_right(nums, target): # # 寻找最左边的那个数
            l, r = 0, len(nums) - 1
            while l < r:
                m = (l + r) // 2 + 1
                if nums[m] > target:
                    r = m - 1
                else:
                    l = m
            return l if nums[l] == target else -1

        return [bisect_left(nums, target), bisect_right(nums, target)]
LeetCode 162 Find Peak Element(判断条件nums[mid] > nums[mid+1]

剑指上的旋转数组的最小数字:判断条件为nums[mid] > nums[high],会有重复元素,所以需要判断是否相等:elif nums[mid] == nums[high]
一个先递增后递减的序列,允许有重复值,找出最大值(和162题一样解法)

class Solution(object):
    def findPeakElement(self, nums):
        low, high = 0, len(nums) - 1
        
        while low < high:
            mid = low + (high - low) // 2
            if nums[mid] > nums[mid+1]:  # 此时mid在递减的序列上
                high = mid
            else:
                low = mid + 1
        
        return low
LeetCode 74 Search a 2D Matrix 猿辅导面试

(方法一:和右上角元素比,每次去掉一行或一列,
方法二:也可以看做一个一维数组啊,因为整体是一个有序数组,与下一题240有区别)

def bisec_left(nums, target):
    if not nums: return -1,-1
    if target < nums[0][0] or target > nums[-1][-1]: return -1,-1
    m, n = len(nums), len(nums[0])
    low, high = 0, m + n - 1
    
    while low < high:
        mid = low + (high - low)//2
        if nums[mid//n][mid%n] < target:
            low = mid + 1
        else:
            high = mid
    
    return low//n, low%n if nums[mid//n][mid%n] == target else -1,-1
LeetCode 240 Search a 2D Matrix II(面试题4:二维数组中的查找,整体不是有序数组,只是每行或每列有序,但行与列之间没有关系,和右上角元素比,每次去掉一行或一列)

时间复杂度为O(m+n)

class Solution(object):
    def searchMatrix(self, matrix, target):
        if not matrix or not matrix[0]: return False
        row, col = 0, len(matrix[0])-1
        
        while row < len(matrix) and col >=0:
            if matrix[row][col] < target:
                row += 1
            elif matrix[row][col] > target:
                col -= 1
            else:
                return True
        
        return False
LeetCode 153 Find Minimum in Rotated Sorted Array (面试题11 旋转数组的最小值,找最小值,无重复元素)美团面试
def minNumberInRotateArray(self, nums):
        # write code here
        if not nums: return 0
        
        low, high = 0, len(nums)-1
        
        while low < high:
            mid = low + (high-low)//2
            if nums[mid] > nums[high]: # 最小值在右边
                low = mid + 1
            elif nums[mid] == nums[high]: # 出现相同元素
                high -= 1
            else:  # 最小值在左边
                high = mid 
        return nums[low]
LeetCode 154 Find Minimum in Rotated Sorted Array II (有重复元素,完全适用于无重复元素的情况,也就是这题的代码在153上直接通过,答案和上一题一样!
LeetCode 33 Search in Rotated Sorted Array (无重复值中找特定值,首先得找最小值的下标,然后计算旋转前的位置,根据旋转前的位置进行二分查找)

突然想起来,找到最小值的下标后,可以通过切片来还原旋转前的数组,然后变成了整体有序的数组了!

class Solution(object):
    def search(self, nums, target):
        if not nums: return -1
        low, high = 0, len(nums)-1
        
        # 先把最小值下标找出来,也就是pivot的位置
        # leetcode 153, 旋转数组的最小值
        
        while low < high:
            mid = low + (high - low) // 2
            if nums[mid] > nums[high]:  # 最小值在右边
                low = mid + 1
            else:
                high = mid
        
        bias = low  # 最小值下标就是偏移量
        low, high = 0, len(nums)-1
        n = len(nums)
        while low <= high:
            mid = low + (high - low) // 2
            realMid = (mid + bias) % n
            if nums[realMid] == target:
                return realMid
            elif nums[realMid] > target:
                high = mid - 1
            else:
                low = mid + 1
        return -1
LeetCode 81 Search in Rotated Sorted Array II (the worst case is O(n),有重复值中找特定值,思路和上一题一样,找最小值也是按照有重复值来找的思路是错误的,比如11112111,就不知道最小值下标到底是哪个。)

这个不太好写,再想想!


LeetCode 378 Kth Smallest Element in a Sorted Matrix 最小堆或二分查找

(二维矩阵、每行每列都有序,但整体不是有序的,找到第k个大,搜索空间为二维数组的最小值和最大值,check()函数来统计数组中比mid小的数字的个数啊!
时间复杂度:O(N∗log(数组max−数组min)))

class Solution(object):
    def kthSmallest(self, matrix, k):
        if not len(matrix) or not len(matrix[0]): return 0
        low, high = matrix[0][0], matrix[-1][-1] # 最小值与最大值
        
        def check(nums, mid): # 统计数组中比mid小的数字的个数啊!
            count = 0
            j = len(matrix[0])-1 
            for i in range(len(matrix)):
                while j >= 0 and matrix[i][j] > mid: j -= 1 # 从右往左找,直接去掉了一列的值
                count += (j+1) # 这一列都比mid小
            
            return count
        
        while low < high:
            mid = low + (high-low)//2
            if check(matrix, mid) < k: # 1-mid之间的数字的个数比k小
                low = mid + 1
            else:
                high = mid
        return low
LeetCode 4 Median of Two Sorted Arrays(两个有序数组的中位数)
class Solution(object):
    def findMedianSortedArrays(self, A, B):
        m, n = len(A), len(B)
        if m > n:
            A, B, m, n = B, A, n, m
        if n == 0:
            raise ValueError

        imin, imax, half_len = 0, m, (m + n + 1) / 2
        while imin <= imax:
            i = (imin + imax) / 2
            j = half_len - i
            if i < m and B[j-1] > A[i]:
                # i is too small, must increase it
                imin = i + 1
            elif i > 0 and A[i-1] > B[j]:
                # i is too big, must decrease it
                imax = i - 1
            else:
                # i is perfect
                if i == 0: max_of_left = B[j-1]
                elif j == 0: max_of_left = A[i-1]
                else: max_of_left = max(A[i-1], B[j-1])

                if (m + n) % 2 == 1:
                    return max_of_left

                if i == m: min_of_right = B[j]
                elif j == n: min_of_right = A[i]
                else: min_of_right = min(A[i], B[j])

                return (max_of_left + min_of_right) / 2.0
        
'''
        时间复杂度不是O(m+n)啊,而是log(m+n) ,
        有序数组,并且要log的时间复杂度,肯定得考虑二分查找
切分左右两个部分:
      left_part          |        right_part
A[0], A[1], ..., A[i-1]  |  A[i], A[i+1], ..., A[m-1]
B[0], B[1], ..., B[j-1]  |  B[j], B[j+1], ..., B[n-1]
'''
有序数组(a,a+1,a+2,a+3,….a+m,a+m,a+m+1,….) 寻找重复数字a+m

(变型:一个整型有序数组,数组里除了一个数字之外,其他的数字都出现了两次。请写程序找出这个只出现一次的数字) 异或操作

六:栈

LeetCode 155 Min Stack(面试题30 包含min函数的栈, 维护一个最小栈,字节面试问了最优解,用一个栈来实现)
class MinStack(object):

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []
        self.minstack = [] # 维护一个有序的栈,栈顶保存最小值

    def push(self, x): # 需要维护最小栈
        """
        :type x: int
        :rtype: None
        """
        self.stack.append(x)
        if not self.minstack:
            self.minstack.append(x)
        elif x <= self.getMin():
            self.minstack.append(x)

    def pop(self):  # 需要维护最小栈
        """
        :rtype: None
        """
        val = self.stack.pop()
        if val == self.getMin():
            self.minstack.pop()
        

    def top(self):
        """
        :rtype: int
        """
        return self.stack[-1]
        

    def getMin(self):
        """
        :rtype: int
        """
        if self.minstack:
            return self.minstack[-1]
        return -1
LeetCode 232 Implement Queue using Stacks(面试题9 两个栈实现队列,当需要pop或peek时,先看stack2是否为空,为空就把第一个栈中的元素全部pop到第二个栈中,不为空就返回stack2中的元素)
class MyQueue(object):

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.stack1 = []
        self.stack2 = []

    def push(self, x):
        """
        Push element x to the back of queue.
        :type x: int
        :rtype: None
        """
        self.stack1.append(x)

    def pop(self):
        """
        Removes the element from in front of queue and returns that element.
        :rtype: int
        """
        if not self.stack2:
            while self.stack1:
                self.stack2.append(self.stack1.pop())
        if self.stack2:
            return self.stack2.pop()
        return -1

    def peek(self):
        """
        Get the front element.
        :rtype: int
        """
        if not self.stack2:
            while self.stack1:
                self.stack2.append(self.stack1.pop())
        if self.stack2:
            return self.stack2[-1]
        return -1

    def empty(self):
        """
        Returns whether the queue is empty.
        :rtype: bool
        """
        if not self.stack1 and not self.stack2:
            return True
        return False
LeetCode 225 Implement Stack using Queues (双端队列很容易实现,appendleft,popleft)
class MyStack(object):

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.deq = collections.deque() # 双端队列
        

    def push(self, x):
        """
        Push element x onto stack.
        :type x: int
        :rtype: None
        """
        self.deq.appendleft(x)

    def pop(self):
        """
        Removes the element on top of the stack and returns that element.
        :rtype: int
        """
        if self.deq:
            return self.deq.popleft()
        return -1

    def top(self):
        """
        Get the top element.
        :rtype: int
        """
        if self.deq:
            return self.deq[0]
        return -1

    def empty(self):
        """
        Returns whether the stack is empty.
        :rtype: bool
        """
        if not self.deq:
            return True
        return False
LeetCode 84 Largest Rectangle in Histogram (单调栈)
LeetCode 85 Maximal Rectangle (单调栈)
接雨水的题(单调栈)
LeetCode 394 Decode String
面试题31:栈的压入、弹出序列
剑指offer :数据流中的中位数,用两个栈来实现
LeetCode Basic Calculator I, II, III 美团面试问到过

七:BFS

LeetCode 102 Binary Tree Level Order Traversal (面试题32:层次遍历二叉树)
LeetCode 107 Binary Tree Level Order Traversal II (层序遍历)
LeetCode 103 Binary Tree Zigzag Level Order Traversal (之字形遍历)
987. Vertical Order Traversal of a Binary Tree(314. Binary Tree Vertical Order Traversal)(二叉树垂直遍历)(从上往下看二叉树,打印节点)(都是同一个解题思路)
[LeetCode] 490. The Maze 迷宫
[LeetCode] 505. The Maze II 迷宫之二
[LeetCode] 499 The Maze III 迷宫之三

八:DFS

LeetCode 200 Number of Islands
面试题8 二叉树的下一个节点(有右子树,就是右子树最左边叶子,没有右子树,则当前节点是父节点的左儿子时是下一个节点)
LeetCode 144 Binary Tree Preorder Traversal (前序遍历,递归和迭代都实现一遍)
LeetCode 94 Binary Tree Inorder Traversal (中序遍历,递归和迭代都实现一遍)
LeetCode 145 Binary Tree Postorder Traversal (后序遍历,递归和迭代都实现一遍)
LeetCode 255. Verify Preorder Sequence in Binary Search Tree(检查二叉搜索树的前序遍历)

(面试题33:二叉搜索树的后序遍历序列)

LeetCode 105 Construct Binary Tree from Preorder and Inorder Traversal 根据先序中序、重建二叉树
LeetCode 106 Construct Binary Tree from Inorder and Postorder Traversal 根据中序后序、重建二叉树
面试题37:序列化与反序列化二叉树
LeetCode 110 Balanced Binary Tree(判断是否为平衡二叉树)
class Solution(object):
    def isBalanced(self, root):
        def dfs(root):  # 定义为以root为根的树,返回左右子树的最大高度
            if not root: return 0
            left = dfs(root.left)
            right = dfs(root.right)
            if abs(left - right) > 1:
                self.isBalanced = False
            return 1 + max(left, right)
        
        self.isBalanced = True
        dfs(root)
        return self.isBalanced
LeetCode 98 Validate Binary Search Tree(判断是否为平衡二叉搜索树)中序遍历为有序数组
# 方法一:中序遍历的顺序单调递增, 先左再根再右
class Solution(object): 
    def isValidBST(self, root):
        self.last = float('-inf')
        self.flag = True
        def inOrder(root):
            if not root: return
            inOrder(root.left)
            if root.val <= self.last: 
                self.flag = False
                return
            self.last = root.val
            inOrder(root.right)
        
        inOrder(root)
        return self.flag


# 方法二:限定子节点范围,比如左子树的范围(-inf, root.val)右子树为(root.val,+inf)
class Solution(object):
    def isValidBST(self, root):
        def helper(root, minn, maxx): # 定义函数为,root节点在区间[minn, maxx]之间
            if not root: return True
            if root.val >= maxx or root.val <= minn: return False # root.val应该在区间内
            
            return helper(root.left, minn, root.val) and helper(root.right, root.val, maxx)
        
        
        return helper(root, float('-inf'), float('inf'))

LeetCode 236 Lowest Common Ancestor of a Binary Tree(最近公共祖先)(面试题68:树中两个节点的最低公共祖先)
LeetCode 104 Maximum Depth of Binary Tree(面试题55:二叉树的深度)
LeetCode 111 Minimum Depth of Binary Tree(二叉树的最小深度)
LeetCode 257 Binary Tree Paths
LeetCode 112 Path Sum
LeetCode 113 Path Sum II(面试题34:二叉树中和为某一值的路径)
LeetCode 437 Path Sum III
LeetCode 114 Flatten Binary Tree to Linked List
LintCode 595 Binary Tree Longest Consecutive Sequence
LintCode 614 Binary Tree Longest Consecutive Sequence II
LintCode 375 Clone Binary Tree
LeetCode 101 Symmetric Tree(面试题28 对称的二叉树)
LeetCode 572 Subtree of Another Tree(面试题26:树的子结构)
leetcode 226. Invert Binary Tree(面试题27 二叉树的镜像)
LeetCode 124 Binary Tree Maximum Path Sum
LeetCode 109 Convert Sorted List to Binary Search Tree
LintCode 378 Convert Binary Search Tree to Doubly Linked List

(面试题36:二叉搜索树与双向链表)

leetcode 230. Kth Smallest Element in a BST(面试题54:二叉搜索树的第k小节点)
leetcode 450 Delete Node in a BST(二叉搜索树删除一个节点)

九:回溯法(有个回溯总结,记得去看)

LeetCode 494 Target Sum
LeetCode 78 Subsets
LeetCode 90 Subsets II
LeetCode 39 Combination Sum
LeetCode 40 Combination Sum II
LeetCode 46 Permutations (面试题38:字符串的排列)
LeetCode 47 Permutations II
LeetCode 31 Next Permutations (字节面试问过,逆序遍历交换再翻转)
LeetCode 131 Palindrome Partitioning
leetcode 79. Word Search(面试题12 矩阵中的路径)
剑指offer 面试题13:机器人的运动范围 (我一直拖着没有做,回溯,DFS,BFS都可以啊)

十:动态规划

LeetCode 53 Maximum Subarray(DP)(面试题42:连续子数组的最大和) 百度问到了!我博客也写了 微软面试题:连续子序列的最大和
LeetCode 152 Maximum Product Subarray(DP)
LeetCode 70 Climbing Stairs 做了
LeetCode 62 Unique Paths 做了
LeetCode 63 Unique Paths II 做了
980. Unique Paths III (dfs)
174. Dungeon Game
91. Decode Ways 做了
639. Decode Ways II
LeetCode 64 Minimum Path Sum(面试题47:礼物的最大价值)
LeetCode 120 Triangle
LeetCode 198 House Robber
LeetCode 213 House Robber II
LeetCode 279 Perfect Squares
LeetCode 221 Maximal Square
LeetCode 300 Longest Increasing Subsequence
LeetCode 115 Distinct Subsequences
LeetCode 5 Longest Palindromic Substring(最长回文子串)
LeetCode 322 Coin Change
LeetCode 72 Edit Distance 做了,课上讲过
LeetCode 403 Frog Jump
LeetCode 354 Russian Doll Envelopes
LeetCode 368 Largest Divisible Subset
LeetCode 329 Longest Increasing Path in a Matrix

(这道题我面实习时腾讯微信和微软都问过,好好掌握下,属于记忆化搜索,即DFS+剪枝)

LeetCode 10 Regular Expression Matching(面试题19:正则表达式匹配)
LeetCode 97 Interleaving String
leetcode 121. Best Time to Buy and Sell Stock(面试题63:股票的最大利润)
leetcode 91. Decode Ways(面试题46:把数字翻译成字符串)
最长公共子序列与最长公共子串 字节问过,我博客写过!
把背包问题都学一下哈,笔试面试还是有遇到的!

十一:贪心

贪心算法考的非常少,面试中非常少见,因为贪心算法的数学证明是很难的,短时间你不太可能证明出来,而且面试官也大概率证不出来,只有机试中才小概率会出贪心算法题,比如头条这种。所以做了以下这几个题就行。具体原因可以看一下这个链接:https://www.jiuzhang.com/qa/2100/

LeetCode 55 Jump Game
LeetCode 45 Jump Game II
LeetCode 134 Gas Station
LeetCode 135 Candy(变型:N个孩子站成一圈)

十二:Math题 一些trick题,考察数学和逻辑推理

LintCode 365 Count 1 in Binary
LintCode 1 A + B Problem(面试题65:不用加减乘除做加法)
LintCode 3 Digit Counts(面试题43:1~n整数中1出现的次数)
LintCode 379 Reorder array to construct the minimum number

(面试题45:把数组排成最小的数)(leetcode 179. Largest Number)

leetcode 400 Nth Digit(面试题44:数字序列中某一位的数字)
面试题15:二进制中1的个数
面试题62:圆圈中最后剩下的数
4. Median of Two Sorted Arrays

其余面经算法题

手写堆排序
字符串浮点数转成浮点数
接雨水(单调栈)
八皇后

java多线程题
☐ 启动3个线程A、B、C,使A打印1、2、3,然后B打印4、5、6,然后C打印7、8、9 ,A打印10、11、12,B打印13、14、15,C打印16、17、18,依次类推

编程实现多线程同步与互斥的demo
智能指针的实现
设计模式的实现
多态的实现

template
class Shared_ptr{
private:
int *count;
T *_ptr;

public:
Shared_ptr(): count(0), _ptr((T*) 0) {}
Shared_ptr(T* p): count(new int(1)), _ptr§ {}
Shared_ptr(Shared_ptr &other): count(&(++*other.count)), _ptr(other._ptr) {}

~Shared_ptr(){
    if (_ptr && --*count==0){
        delete count;
        delete ptr;
    }
}

T* operator*() {return *_ptr;}
T& operator->() {return _ptr;}

Shared_ptr<T> & operator=(Shared_ptr<T> &other){
    if(this==&other)
        return *this
    ++(*other.count);
    if (this->_ptr && --(*this.count)==0){
        delete count;
        delete _ptr;
    }
    this->count = other->count;
    this->_ptr = other->_ptr;
    return *this;            
}

int getCount(){
    return *count;
}

}

Hashset的使用总结:
LeetCode 1 Two sum
LeetCode 128 Longest Consecutive Sequence
167. Two Sum II - Input array is sorted

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值