面试常考算法
前言
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为开区间
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
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)
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])
从大到小排序中的第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]
先通过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]
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
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
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
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
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))
要求:不能使用额外空间,时间复杂度为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
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
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
把小于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)
分割一个整数数组,使得奇数在前偶数在后。
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
给出一个含有正整数和负整数的数组,重新排列成一个正负数交错的数组
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
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
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
求子数组之和等于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
找到和为 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 []
一个数组中,只有一个元素出现一次,其余元素都出现了两次,找出那个出现一次的元素
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
除了一个元素出现一次,其余元素出现三次
要求不适用额外空间,线性时间复杂度
# 解法一:数学方法,使用了额外的空间,不符合题意要求!
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是出现一次数字的
*/
数组中有两个数只出现一次,其余数都出现两次,找出这两个只出现一次的数
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
因子只能是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
求第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]
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
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
求最长的连续序列
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
可能并不存在那个超过半数的数,此时需要特殊判断!
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
判断字符串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
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]
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 []
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
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 # 三数之和
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
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
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
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
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
可以用栈来做!
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]
翻转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)
'''
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
划分链表,把小于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
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
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
class Solution(object):
def deleteNode(self, node):
tmp = node.next.val
node.next.val = node.val
node.val = tmp
node.next = node.next.next
面试还问你特殊情况怎么处理,也就是空链表,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
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
链表右移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
两个链表存的数,左对齐了,把链表相加,获得结果也是个链表,并且进位是从左向右进位的,最高位在最右边!
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
右对齐,进位从右到左边,但是链表只能从前往后遍历,因此需要栈来存链表中的数!
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
判断 链表是否是回文子串
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
成对交换链表中的节点,不是修改值
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
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
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
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
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
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]
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
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]
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
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
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
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)]
剑指上的旋转数组的最小数字:判断条件为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
(方法一:和右上角元素比,每次去掉一行或一列,
方法二:也可以看做一个一维数组啊,因为整体是一个有序数组,与下一题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
时间复杂度为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
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]
突然想起来,找到最小值的下标后,可以通过切片来还原旋转前的数组,然后变成了整体有序的数组了!
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
(二维矩阵、每行每列都有序,但整体不是有序的,找到第k个大,搜索空间为二维数组的最小值和最大值,check()函数来统计数组中比mid小的数字的个数啊!
时间复杂度:O(N∗log(数组max−数组min)))
(变型:一个整型有序数组,数组里除了一个数字之外,其他的数字都出现了两次。请写程序找出这个只出现一次的数字) 异或操作
(面试题33:二叉搜索树的后序遍历序列)
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
# 方法一:中序遍历的顺序单调递增, 先左再根再右
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'))
(面试题36:二叉搜索树与双向链表)
(这道题我面实习时腾讯微信和微软都问过,好好掌握下,属于记忆化搜索,即DFS+剪枝)
贪心算法考的非常少,面试中非常少见,因为贪心算法的数学证明是很难的,短时间你不太可能证明出来,而且面试官也大概率证不出来,只有机试中才小概率会出贪心算法题,比如头条这种。所以做了以下这几个题就行。具体原因可以看一下这个链接:https://www.jiuzhang.com/qa/2100/
(面试题45:把数组排成最小的数)(leetcode 179. Largest Number)
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 & operator=(Shared_ptr &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