使用大小为 256 的数组记录每个字符出现的次数。遍历两遍即可。
# -*- coding:utf-8 -*-
class Solution:
def FirstNotRepeatingChar(self, s):
# write code here
dic = [0] * 256
for i in range(len(s)):
dic[ord(s[i])] += 1
for i in range(len(s)):
if dic[ord(s[i])] == 1:
return i
return -1
# -*- coding:utf-8 -*-
class Solution:
def InversePairs(self, data):
# write code here
self.ans = 0 # 逆序对数量
self.mergeSort(data) # 归并排序
return self.ans % 1000000007
def mergeSort(self, nums):
# 递归过程
if len(nums) <= 1:
return nums
mid = len(nums) // 2
left = self.mergeSort(nums[:mid])
right = self.mergeSort(nums[mid:])
return self.merge(left, right)
# 归并过程 + 统计逆序对数量
def merge(self, left, right):
result = [] # 保存归并后的结果
i = j = 0
left_len = len(left)
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else: # left[i] > right[j]
self.ans += (left_len - i) # 核心:说明 nums[i:] 都大于 nums[j]
result.append(right[j])
j += 1
result = result + left[i:] + right[j:] # 剩余的元素直接添加到末尾
return result
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def FindFirstCommonNode(self, pHead1, pHead2):
# write code here
l1, l2 = pHead1, pHead2
while l1 != l2:
l1 = l1.next if l1 != None else pHead2
l2 = l2.next if l2 != None else pHead1
return l1 # 或者 l2
排序数组,很明显二分查找,找到第一个 >= k
的元素索引以及第一个 > k
的元素索引,两者相减即为答案,即 lowerBound - upperBound
**。时间复杂度为 O(logn),空间复杂度为 O(1)。**
# -*- coding:utf-8 -*-
class Solution:
def GetNumberOfK(self, data, k):
# write code here
if not data:
return 0
lo, hi = 0, len(data) - 1
while lo < hi:
mid = lo + (hi - lo) // 2
if data[mid] >= k:
hi = mid
elif data[mid] < k:
lo = mid + 1
ind1 = lo if data[lo] >= k else len(data) # >=k 的第一个元素
lo, hi = 0, len(data) - 1
while lo < hi:
mid = lo + (hi - lo) // 2
if data[mid] > k:
hi = mid
elif data[mid] <= k:
lo = mid + 1
ind2 = lo if data[lo] > k else len(data) # # >k 的第一个元素
return ind2 - ind1 # 相减即为答案
更简洁的,可以使用 Python 的 bisect 模块中的函数实现。可以参考博客:二分查找及其变形与Python的bisect模块的关系。
# -*- coding:utf-8 -*-
import bisect
class Solution:
def GetNumberOfK(self, data, k):
# write code here
return bisect.bisect_right(data, k) - bisect.bisect_left(data, k)
中序遍历,找到第 k 个数即可。
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回对应节点TreeNode
def __init__(self):
self.cnt = 0
def KthNode(self, pRoot, k):
# write code here
if not pRoot or self.cnt >= k: # self.cnt >= k 剪枝
return None
left = self.KthNode(pRoot.left, k)
if left:
return left
self.cnt += 1
if self.cnt == k:
return pRoot
right = self.KthNode(pRoot.right, k)
if right:
return right
return None
递归左右子树找最大高度。注意:根的高度为 1。
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def TreeDepth(self, pRoot):
# write code here
if not pRoot:
return 0
return max(self.TreeDepth(pRoot.left), self.TreeDepth(pRoot.right)) + 1
平衡二叉树(AVL)的左右子树的高度差不超过 1,因此引入求高度的函数。并且,判断该树的每个结点的左右子树是否也满足 AVL 的定义。
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def IsBalanced_Solution(self, pRoot):
# write code here
if not pRoot:
return True
if abs(self.TreeDepth(pRoot.left) - self.TreeDepth(pRoot.right)) > 1: # 不满足 AVL 的定义
return False
return self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)
def TreeDepth(self, pRoot):
if not pRoot:
return 0
return max(self.TreeDepth(pRoot.left), self.TreeDepth(pRoot.right)) + 1
改进:自顶向下在调用求树的高度的函数时,有很多重复的操作。因此,可以使用自底向上的方法,一边计算树的高度,一边判断是否是 AVL 树。
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def IsBalanced_Solution(self, pRoot):
# write code here
self.isBalance = True
self.TreeDepth(pRoot) # 在求树的高度过程中判断 AVL
return self.isBalance
def TreeDepth(self, pRoot):
if not pRoot:
return 0
left = self.TreeDepth(pRoot.left)
right = self.TreeDepth(pRoot.right)
if abs(left - right) > 1: # 不满足 AVL 定义,修改 self.isBalance 的值
self.isBalance = False
return max(left, right) + 1
如果只出现一次的数字只有一个,很好做,就是全部异或即可。但是,只出现一次的数字有两个怎么做呢?
# -*- coding:utf-8 -*-
class Solution:
# 返回[a,b] 其中ab是出现一次的两个数字
def FindNumsAppearOnce(self, array):
# write code here
xor = 0
ans = [0, 0]
for num in array:
xor ^= num
bits = 0 # 找到倒数第 i 位为 1
while xor & 1 << bits == 0:
bits += 1
for num in array:
if num >> bits & 1 == 1: # num 的倒数第 i 位为 1
ans[0] ^= num
else: # num 的倒数第 i 位为 0
ans[1] ^= num
return ans
双指针指向首尾,往中间走,碰到第一对和为 S 的就是答案。
# -*- coding:utf-8 -*-
class Solution:
def FindNumbersWithSum(self, array, tsum):
# write code here
lo, hi = 0, len(array) - 1
while lo < hi: # 不能指向相同的数字
if array[lo] + array[hi] == tsum:
return [array[lo], array[hi]]
elif array[lo] + array[hi] < tsum:
lo += 1
else:
hi -= 1
return []
经典的滑动窗口例题,只不过该题的窗口大小不确定。用一个遍历 left 记录窗口左侧的值,window_sum 将窗口中数进行累加。当发现 window_sum >= S 时,相等时输出结果,更新 left 和 window_sum。
# -*- coding:utf-8 -*-
class Solution:
def FindContinuousSequence(self, tsum):
# write code here
if tsum <= 1: # 特殊情况
return []
ans = []
left, window_sum = 1, 0 # left 记录窗口左界
for i in range(1, (tsum + 1) // 2 + 1): # 窗口中至少两个数
window_sum += i # 窗口中的累加和
while window_sum >= tsum:
if window_sum == tsum: # 输出一组结果
ans.append([j for j in range(left, i + 1)])
window_sum -= left # 修改窗口中的累加和
left += 1 # 修改窗口的左界
return ans
如果要求空间复杂度为 O(1),即只能使用字符串本身,该怎么操作呢?
这样就可以在字符串本身上做修改,使得空间复杂度为 O(1) 了。
注意:但是使用 Python 实现得话,由于不能修改字符串本身,所以还是先要将字符串转化为列表。但是如果使用 C++ 的字符数组,就不用开辟空间了。
# -*- coding:utf-8 -*-
class Solution:
def ReverseSentence(self, s):
# write code here
def reverseWord(s, i, j):
while i < j:
s[i], s[j] = s[j], s[i]
i += 1
j -= 1
s = list(s) # python 中 s 不能修改,先转化为 list
start = 0 # start 指向当前单词的起始位置
lens = len(s)
for i in range(lens + 1):
if i == lens or s[i] == ' ':
reverseWord(s, start, i - 1) # 先翻转每个单词
start = i + 1 # 下一个单词的起始位置
reverseWord(s, 0, lens - 1) # 再将整个句子翻转
return "".join(s)
如果也不能使用空间,怎么做呢?参考上面的 58.1,如 s = "abcXYZdef",n = 3,先将 "abc" 和 "XYZdef" 分别翻转,得到 "cbafedZYX",然后再把整个字符串翻转得到 "XYZdefabc"。这样就可以空间复杂度为 O(1) 了。
# -*- coding:utf-8 -*-
class Solution:
def LeftRotateString(self, s, n):
# write code here
def reverseWord(s, i, j):
while i < j:
s[i], s[j] = s[j], s[i]
i += 1
j -= 1
if not s:
return ""
s = list(s)
lens = len(s)
n %= lens # 循环左移
reverseWord(s, 0, n - 1) # 先将前 n 个字符翻转
reverseWord(s, n, lens - 1) # 再将后面的字符翻转
reverseWord(s, 0, lens - 1) # 最后将整个字符串翻转
return "".join(s) # 再转化回字符串
使用双向递减队列,队列中始终维护的是窗口中的递减值。
例如,num = 2,3,4,2,6,2,5,1,size = 3,双向队列的变化情况为 dq: 2 -> 3 -> 4 -> 4,2 -> 6 -> 6,2 -> 6,5 -> 5,1。
# -*- coding:utf-8 -*-
import collections
class Solution:
def maxInWindows(self, num, size):
# write code here
if size > len(num) or size < 1:
return []
dq = collections.deque() # [num[i], i]
ans = []
for i in range(len(num)):
if dq and i - dq[0][1] >= size:
dq.popleft() # O(1)
while dq and num[i] > dq[-1][0]: # 从后往前删除比 num[i] 大的数
dq.pop() # O(1)
dq.append([num[i], i])
if i >= size - 1:
ans.append(dq[0][0]) # 队列首部始终最大
return ans
剑指 offer 终于过了一遍,大多数题目还是很简单的,但是题目具有代表性,涉及链表、数组、深搜回溯、字符串、数组、数学、位运算、动态规划等。