剑指 Offer(第 2 版)笔记(简单篇)
- 简单篇
- 剑指 Offer 03. 数组中重复的数字
- 剑指 Offer 05. 替换空格
- 剑指 Offer 06. 从尾到头打印链表
- 剑指 Offer 09. 用两个栈实现队列
- 剑指 Offer 10- I. 斐波那契数列
- 剑指 Offer 10- II. 青蛙跳台阶问题
- 剑指 Offer 11. 旋转数组的最小数字
- 剑指 Offer 15. 二进制中1的个数
- 剑指 Offer 17. 打印从1到最大的n位数
- 剑指 Offer 18. 删除链表的节点
- 剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
- 剑指 Offer 22. 链表中倒数第k个节点
- 剑指 Offer 24. 反转链表
- 剑指 Offer 25. 合并两个排序的链表
- 剑指 Offer 27. 二叉树的镜像
- 剑指 Offer 28. 对称的二叉树
- 剑指 Offer 29. 顺时针打印矩阵
- 剑指 Offer 30. 包含min函数的栈
- 剑指 Offer 32 - II. 从上到下打印二叉树 II
- 剑指 Offer 39. 数组中出现次数超过一半的数字
- 剑指 Offer 40. 最小的k个数
- 剑指 Offer 42. 连续子数组的最大和
- 剑指 Offer 50. 第一个只出现一次的字符
- 剑指 Offer 52. 两个链表的第一个公共节点
- 剑指 Offer 53 - I. 在排序数组中查找数字 I
- 剑指 Offer 53 - II. 0~n-1中缺失的数字
- 剑指 Offer 54. 二叉搜索树的第k大节点
- 剑指 Offer 55 - I. 二叉树的深度
- 剑指 Offer 55 - II. 平衡二叉树
- 剑指 Offer 57. 和为s的两个数字
- 剑指 Offer 57 - II. 和为s的连续正数序列
- 剑指 Offer 58 - I. 翻转单词顺序
- 剑指 Offer 58 - II. 左旋转字符串
- 剑指 Offer 61. 扑克牌中的顺子
- 剑指 Offer 62. 圆圈中最后剩下的数字
- 剑指 Offer 65. 不用加减乘除做加法
- 剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
- 剑指 Offer 68 - II. 二叉树的最近公共祖先
- 总结
简单篇
剑指 Offer 03. 数组中重复的数字
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
class Solution {
public int findRepeatNumber(int[] nums) {
Set<Integer> set = new HashSet<>(); # 创建hash集合
int repeat = -1;
for(int num: nums){
if(!set.add(num)){ // 添加元素失败则赋值为重复
repeat = num;
break;
}
}
return repeat;
}
}
class Solution:
def findRepeatNumber(self, nums: List[int]) -> int:
nums.sort()
for i in range(len(nums) - 1):
if nums[i] == nums[i + 1]:
return nums[i]
剑指 Offer 05. 替换空格
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
输入:s = "We are happy."
输出:"We%20are%20happy."
class Solution {
public String replaceSpace(String s) {
int length = s.length();
char[] array = new char[length * 3]; // 保证可容纳
int size = 0;
for (int i = 0; i < length; i++) {
char c = s.charAt(i); // 取字符函数
if (c == ' ') {
array[size++] = '%';
array[size++] = '2';
array[size++] = '0';
} else {
array[size++] = c;
}
}
String newStr = new String(array, 0, size); // 新字符串0-size
return newStr;
}
}
剑指 Offer 06. 从尾到头打印链表
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
输入:head = [1,3,2]
输出:[2,3,1]
class Solution {
public int[] reversePrint(ListNode head) {
Stack<ListNode> stack = new Stack<ListNode>();
ListNode temp = head; // 创建栈,类型为节点
while (temp != null) {
stack.push(temp);
temp = temp.next;
}
int size = stack.size();
int[] print = new int[size];// 节点全进栈再出栈,实现逆序
for (int i = 0; i < size; i++) {
print[i] = stack.pop().val;
}
return print;
}
}
class Solution:
def reversePrint(self, head: ListNode) -> List[int]:
if head==None: return []
val = [] # 创建数组添加节点值,再逆序
current_node = head
while current_node is not None:
val.append(current_node.val)
current_node = current_node.next
return val[::-1] # 逆序
剑指 Offer 09. 用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和
deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。
(若队列中没有元素,deleteHead 操作返回 -1 )
class CQueue {
Deque<Integer> stack1;
Deque<Integer> stack2;
public CQueue() {
stack1 = new LinkedList<Integer>();
stack2 = new LinkedList<Integer>();
}
public void appendTail(int value) {
stack1.push(value);
}
public int deleteHead() {
// 如果第二个栈为空
if (stack2.isEmpty()) {
while (!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
if (stack2.isEmpty()) {
return -1;
} else {
int deleteItem = stack2.pop();
return deleteItem;
}
}
}
class CQueue:
def __init__(self):
self.stack_in = []
self.stack_out = []
def appendTail(self, value: int) -> None:
self.stack_in.append(value)
def deleteHead(self) -> int:
if not self.stack_out:
if not self.stack_in: # 都为空
return -1
else: # 把in栈中的东西全部倒入out栈中
while self.stack_in:
self.stack_out.append(self.stack_in.pop())
return self.stack_out.pop()
剑指 Offer 10- I. 斐波那契数列
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
class Solution:
def fib(self, n: int) -> int:
if n==0: # 递归。超出时间限制
return 0
if n==1 or n==2:
return 1
return self.fib(n-1) + self.fib(n-2)
class Solution:
def fib(self, n: int) -> int:
a, b = 0, 1 # 滚动窗口,滑动数组
for _ in range(n):
a, b = b, a + b
return a % 1000000007
class Solution { // 动态规划,滚动数组
public int fib(int n) {
final int MOD = 1000000007;
if (n < 2) {
return n;
}
int p = 0, q = 0, r = 1;
for (int i = 2; i <= n; ++i) {
p = q; // 动态规划,滚动数组
q = r;
r = (p + q) % MOD;
}
return r;
}
}
剑指 Offer 10- II. 青蛙跳台阶问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
输入:n = 2
输出:2
class Solution {
public int numWays(int n) {
int p=0, q=0, r=1;
for(int i=0; i<n; i++){
p=q;
q=r;
r=(q+p)%1000000007;
}
return r;
}
}
class Solution:
def numWays(self, n: int) -> int:
a, b = 1, 1 # 初始值
for _ in range(n):
a, b = b, a + b
return a % 1000000007
设跳上 n 级台阶有 f(n) 种跳法。在所有跳法中,青蛙的最后一步只有两种情况: 跳上 1 级或 2 级台阶。
当为 1 级台阶: 剩n−1个台阶,此情况共有f(n−1) 种跳法;
当为 2 级台阶: 剩n−2个台阶,此情况共有f(n−2) 种跳法。
剑指 Offer 11. 旋转数组的最小数字
给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为1。
输入:[3,4,5,1,2]
输出:1
class Solution:
def minArray(self, numbers: List[int]) -> int:
low, high = 0, len(numbers) - 1
while low < high: # 二分法
mid = low + (high - low) // 2
if numbers[mid] < numbers[high]:
high = mid
elif numbers[mid] > numbers[high]:
low = mid + 1
else:
high -= 1
return numbers[low]
class Solution {
public int minArray(int[] numbers) {
if ( numbers.length == 1) return numbers[0];
for ( int i = 1; i < numbers.length; i++){
if ( numbers[i] < numbers[i - 1]){
return numbers[i];
}
}
// 没有找到这个转折点, 则数组为升序
return numbers[0];
}
}
剑指 Offer 15. 二进制中1的个数
编写一个函数,输入是一个无符号整数(以二进制串的形式),
返回其二进制表达式中数字位数为 '1' 的个数(也被称为 汉明重量).)。
class Solution:
def hammingWeight(self, n: int) -> int:
ret = sum(1 for i in range(32) if n & (1 << i))
return ret
# 具体代码中,当检查第 i 位时,我们可以让 n 与 2^i
# 进行与运算,当且仅当 n 的第 i 位为 1 时,运算结果不为 0。
class Solution:
def hammingWeight(self, n: int) -> int:
ret = 0
while n:
n &= n - 1 # 恰好把最后一个1置为0
ret += 1
return ret
剑指 Offer 17. 打印从1到最大的n位数
输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。
比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
输入: n = 1
输出: [1,2,3,4,5,6,7,8,9]
class Solution:
def printNumbers(self, n: int) -> List[int]:
res = [] # 打印1 - 10^n-1
for i in range(1, 10 ** n):
res.append(i)
return res
剑指 Offer 18. 删除链表的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,
该链表应变为 4 -> 1 -> 9.
class Solution:
def deleteNode(self, head: ListNode, val: int) -> ListNode:
if head.val == val: return head.next
pre, cur = head, head.next # 设置前驱节点和后继节点
while cur and cur.val != val:
pre, cur = cur, cur.next
if cur: pre.next = cur.next
return head
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。
class Solution:
def exchange(self, nums: List[int]) -> List[int]:
# res = [] # 创建新数组保存,效率较低
# n = len(nums)
# for i in range(n):
# if nums[i] % 2 == 1:
# res.append(nums[i])
# for i in range(n):
# if nums[i] % 2 == 0:
# res.append(nums[i])
# return res
i, j = 0, len(nums)-1
while i<j: # 直接在数组上操作。搜索调换位置
while i<j and nums[i]&1 == 1:
i += 1
while i<j and nums[j]&1 == 0:
j -= 1
nums[i], nums[j] = nums[j], nums[i]
return nums
剑指 Offer 22. 链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个节点。
为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。
这个链表的倒数第 3 个节点是值为 4 的节点。
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode node = head;
int n = 0;
while(node!=null){
node=node.next;
n++;
}
for(node=head; n>k; n--){
node = node.next;
}
return node;
}
}
class Solution:
def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
node, n = head, 0
while node:
node = node.next
n += 1
node = head
for i in range(n-k):
node = node.next
return node
剑指 Offer 24. 反转链表
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
# if head==None or head.next==None:
# return head # 递归
# newhead = self.reverseList(head.next)
# head.next.next = head
# head.next = None
# return newhead
if head==None or head.next==None:
return head
pre, cur= None, head
while cur != None:
Next = cur.next # 指针变向
cur.next = pre
pre = cur # 右滑2个指针
cur = Next
return pre
剑指 Offer 25. 合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
cur = dum = ListNode(0)
while l1 and l2:
if l1.val < l2.val:
cur.next, l1 = l1, l1.next
else:
cur.next, l2 = l2, l2.next
cur = cur.next
cur.next = l1 if l1 else l2
return dum.next
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dum = new ListNode(0), cur = dum;
while(l1 != null && l2 != null) {
if(l1.val < l2.val) {
cur.next = l1;
l1 = l1.next;
}
else {
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
cur.next = l1 != null ? l1 : l2;
return dum.next;
}
}
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null){
return l2;
}
else if(l2==null){
return l1;
}
else if(l1.val < l2.val){
l1.next = mergeTwoLists(l1.next, l2);
return l1;
}
else{
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
}
剑指 Offer 27. 二叉树的镜像
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
例如输入:
4
/ \
2 7
/ \ / \
1 3 6 9
镜像输出:
4
/ \
7 2
/ \ / \
9 6 3 1
class Solution:
def mirrorTree(self, root: TreeNode) -> TreeNode:
if not root:
return root
left = self.mirrorTree(root.left)
right = self.mirrorTree(root.right)
root.left, root.right = right, left
return root
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if (root == null) {
return null;
}
TreeNode left = mirrorTree(root.left);
TreeNode right = mirrorTree(root.right);
root.left = right;
root.right = left;
return root;
}
}
剑指 Offer 28. 对称的二叉树
请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/ \
2 2
\ \
3 3
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
def check(p, q):
if p==None and q==None:
return True
if p==None or q==None:
return False
if p.val != q.val:
return False
return check(p.left, q.right) and check(p.right, q.left)
return check(root, root)
class Solution {
public boolean isSymmetric(TreeNode root) {
return root == null ? true : recur(root.left, root.right);
}
boolean recur(TreeNode L, TreeNode R) {
if(L == null && R == null) return true;
if(L == null || R == null || L.val != R.val) return false;
return recur(L.left, R.right) && recur(L.right, R.left);
}
}
剑指 Offer 29. 顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
res = [] # 转置矩阵,拼接,超清晰
while matrix:
res += matrix.pop(0)
matrix = list(zip(*matrix))[::-1]
return res
class Solution:
def spiralOrder(self, matrix:[[int]]) -> [int]:
if not matrix: return []
l, r, t, b, res = 0, len(matrix[0]) - 1, 0, len(matrix) - 1, []
while True:
for i in range(l, r + 1): res.append(matrix[t][i]) # left to right
t += 1
if t > b: break
for i in range(t, b + 1): res.append(matrix[i][r]) # top to bottom
r -= 1
if l > r: break
for i in range(r, l - 1, -1): res.append(matrix[b][i]) # right to left
b -= 1
if t > b: break
for i in range(b, t - 1, -1): res.append(matrix[i][l]) # bottom to top
l += 1
if l > r: break
return res
剑指 Offer 30. 包含min函数的栈
class MinStack:
def __init__(self):
self.A, self.B = [], []
def push(self, x: int) -> None:
self.A.append(x)
if not self.B or self.B[-1] >= x:
self.B.append(x)
def pop(self) -> None:
if self.A.pop() == self.B[-1]:
self.B.pop()
def top(self) -> int:
return self.A[-1]
def min(self) -> int:
return self.B[-1]
class MinStack {
Stack<Integer> A, B;
public MinStack() {
A = new Stack<>();
B = new Stack<>();
}
public void push(int x) {
A.add(x);
if(B.empty() || B.peek() >= x)
B.add(x);
}
public void pop() {
if(A.pop().equals(B.peek()))
B.pop();
}
public int top() {
return A.peek();
}
public int min() {
return B.peek();
}
}
剑指 Offer 32 - II. 从上到下打印二叉树 II
从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。
3
/ \
9 20
/ \
15 7
[
[3],
[9,20],
[15,7]
]
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
if not root: return []
res, queue = [], collections.deque()
queue.append(root)
while queue:
tmp = []
for i in range(len(queue)):
node = queue.popleft()
tmp.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
res.append(tmp)
return res
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root != null) queue.add(root);
while(!queue.isEmpty()) {
List<Integer> tmp = new ArrayList<>();
for(int i = queue.size(); i > 0; i--) {
TreeNode node = queue.poll();
tmp.add(node.val);
if(node.left != null) queue.add(node.left);
if(node.right != null) queue.add(node.right);
}
res.add(tmp);
}
return res;
}
}
剑指 Offer 39. 数组中出现次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
class Solution: # 投票法
def majorityElement(self, nums: List[int]) -> int:
votes, count = 0, 0
for num in nums:
if votes == 0: x = num
votes += 1 if num == x else -1 # 是就加一票,否则减一票
# 验证 x 是否为众数
for num in nums:
if num == x: count += 1
return x if count > len(nums) // 2 else 0 # 当无众数时返回 0
class Solution:
def majorityElement(self, nums: List[int]) -> int:
counts = collections.Counter(nums)
return max(counts.keys(), key=counts.get)
class Solution { // 排序法
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length / 2];
}
}
剑指 Offer 40. 最小的k个数
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
class Solution:
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
arr.sort()
return arr[:k] # 排序法
剑指 Offer 42. 连续子数组的最大和
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
pre, ans = 0, nums[0]
for i in range(len(nums)):
pre = max(pre + nums[i], nums[i])
ans = max(pre, ans)
return ans
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
for i in range(1, len(nums)):
nums[i] += max(nums[i - 1], 0)
return max(nums) # k神的动态规划
剑指 Offer 50. 第一个只出现一次的字符
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
输入:s = "abaccdeff"
输出:'b'
class Solution:
def firstUniqChar(self, s: str) -> str:
dic = {} # 创建字典,只有一个为1,多个为0
for c in s:
dic[c] = not c in dic
for c in s:
if dic[c]: return c
return ' '
class Solution {
public char firstUniqChar(String s) {
HashMap<Character, Boolean> dic = new HashMap<>();
char[] sc = s.toCharArray();
for(char c : sc)
dic.put(c, !dic.containsKey(c));
for(char c : sc)
if(dic.get(c)) return c;
return ' ';
}
}
class Solution:
def firstUniqChar(self, s: str) -> str:
dic = collections.OrderedDict()
for c in s:
dic[c] = not c in dic
for k, v in dic.items():
if v: return k
return ' '
class Solution:
def firstUniqChar(self, s: str) -> str:
dic = {}
for c in s:
dic[c] = not c in dic
for k, v in dic.items():
if v: return k
return ' '
剑指 Offer 52. 两个链表的第一个公共节点
输入两个链表,找出它们的第一个公共节点。
如下面的两个链表:在节点 c1 开始相交。
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
A, B = headA, headB # 双指针
while A != B:
A = A.next if A else headB
B = B.next if B else headA
return A
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode A = headA, B = headB; // 双指针
while (A != B) {
A = A != null ? A.next : headB;
B = B != null ? B.next : headA;
}
return A;
}
}
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
Set<ListNode> visited = new HashSet<ListNode>();
ListNode temp = headA; // 哈希集合
while (temp != null) {
visited.add(temp);
temp = temp.next;
}
temp = headB;
while (temp != null) {
if (visited.contains(temp)) {
return temp;
}
temp = temp.next;
}
return null;
}
}
剑指 Offer 53 - I. 在排序数组中查找数字 I
统计一个数字在排序数组中出现的次数。
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
class Solution:
def search(self, nums: [int], target: int) -> int:
def helper(tar):
i, j = 0, len(nums) - 1
while i <= j:
m = (i + j) // 2
if nums[m] <= tar: i = m + 1
else: j = m - 1
return i
return helper(target) - helper(target - 1)
# frequency = collections.Counter(nums)
# return frequency[target]
# return nums.count(target)
剑指 Offer 53 - II. 0~n-1中缺失的数字
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
输入: [0,1,3]
输出: 2
class Solution:
def missingNumber(self, nums: List[int]) -> int:
i, j = 0, len(nums) - 1
while i <= j:
m = (i + j) // 2
if nums[m] == m: i = m + 1
else: j = m - 1
return i
# return sum(range(len(nums) + 1)) - sum(nums)
剑指 Offer 54. 二叉搜索树的第k大节点
给定一棵二叉搜索树,请找出其中第 k 大的节点的值。
输入: root = [3,1,4,null,2], k = 1
3
/ \
1 4
\
2
输出: 4
class Solution:
def kthLargest(self, root: TreeNode, k: int) -> int:
# def inorder(root):
# if not root: return
# inorder(root.right)
# # data.append(root.val)
# if self.k == 0: return
# self.k -= 1
# if self.k == 0: self.res = root.val
# inorder(root.left)
# self.k = k
# inorder(root)
# return self.res
# 用数组存入中序遍历的节点值,然后res[-k]
res = []
def inorder(root):
if not root: return
inorder(root.left)
res.append(root.val)
inorder(root.right)
inorder(root)
return res[-k]
剑指 Offer 55 - I. 二叉树的深度
输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
3
/ \
9 20
/ \
15 7 返回它的最大深度 3 。
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if not root: return 0
return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1
剑指 Offer 55 - II. 平衡二叉树
输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
3
/ \
9 20
/ \
15 7
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
if not root: return True
def maxDepth(root):
if not root: return 0
return max(maxDepth(root.left), maxDepth(root.right)) + 1
return abs(maxDepth(root.left) - maxDepth(root.right)) <= 1 and self.isBalanced(root.left) and self.isBalanced(root.right)
剑指 Offer 57. 和为s的两个数字
输入一个递增排序的数组和一个数字s,在数组中查找两个数,
使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
i, j = 0, len(nums) - 1
while i < j:
s = nums[i] + nums[j]
if s > target: j -= 1
elif s < target: i += 1
else: return nums[i], nums[j]
return []
剑指 Offer 57 - II. 和为s的连续正数序列
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
输入:target = 9
输出:[[2,3,4],[4,5]]
class Solution:
def findContinuousSequence(self, target: int) -> List[List[int]]:
i, j, sum, res = 1, 1, 0, []
while i <= target // 2:
if sum < target:
sum += j
j += 1
elif sum > target:
sum -= i
i += 1
else:
arr = list(range(i, j))
res.append(arr)
# 左边界向右移动,右推滑窗
sum -= i
i += 1
return res
剑指 Offer 58 - I. 翻转单词顺序
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。
为简单起见,标点符号和普通字母一样处理。
例如输入字符串"I am a student. ",则输出"student. a am I"。
class Solution:
def reverseWords(self, s: str) -> str:
s = s.strip().split()
s.reverse()
return ' '.join(s)
# return ' '.join(s.strip().split()[::-1])
剑指 Offer 58 - II. 左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。
请定义一个函数实现字符串左旋转操作的功能。
比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
输入: s = "abcdefg", k = 2
输出: "cdefgab"
class Solution:
def reverseLeftWords(self, s: str, n: int) -> str:
return s[n:] + s[:n] # 字符串切片
class Solution:
def reverseLeftWords(self, s: str, n: int) -> str:
res = "" # 字符串遍历拼接
for i in range(n, n + len(s)):
res += s[i % len(s)]
return res
剑指 Offer 61. 扑克牌中的顺子
从若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。
输入: [1,2,3,4,5]
输出: True
class Solution:
def isStraight(self, nums: List[int]) -> bool:
nums.sort()
repeat = 0
for i in range(4):
if nums[i] == 0: repeat += 1
elif nums[i]==nums[i+1]: return False
return nums[4] - nums[repeat] <5
剑指 Offer 62. 圆圈中最后剩下的数字
0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除
第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,
则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
class Solution:
def lastRemaining(self, n: int, m: int) -> int:
def f(n, m):
if n == 0: return 0
x = f(n-1, m)
return (m+x)%n
return f(n, m)
# arr = [i for i in range(n)]
# tmp = 0
# for i in range(n, 1, -1):
# tmp = (tmp + m - 1) % i
# arr.remove(arr[tmp])
# return arr[0]
class Solution:
def lastRemaining(self, n: int, m: int) -> int:
x = 0
for i in range(2, n + 1):
x = (x + m) % i
return x
剑指 Offer 65. 不用加减乘除做加法
写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。
输入: a = 1, b = 1
输出: 2
class Solution {
public int add(int a, int b) {
while(b != 0) { // 当进位为 0 时跳出
int c = (a & b) << 1; // c = 进位
a ^= b; // a = 非进位和
b = c; // b = 进位
}
return a;
}
}
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
while root:
if root.val < p.val and root.val < q.val:
root = root.right
elif root.val > p.val and root.val > q.val:
root = root.left
else: break
return root
剑指 Offer 68 - II. 二叉树的最近公共祖先
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
class Solution:
def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
if not root or root==p or root==q: return root
left = self.lowestCommonAncestor(root.left, p ,q)
right = self.lowestCommonAncestor(root.right, p, q)
if not left and not right: return
if not left: return right
if not right: return left
return root
总结
学习笔记,用于睡前方便阅读复习。答案来自力扣和Krahets大佬。