4.12
102. 二叉树的层序遍历 - 力扣(LeetCode)
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
queue<TreeNode*> que;
if(!root) return res;
que.push(root);
while(!que.empty()){
int size = que.size();
vector<int> vec;
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
vec.push_back(node->val);
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
res.push_back(vec);
}
return res;
}
};
153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)
设 x=nums[mid] 是现在二分取到的数。
我们需要判断 x 和数组最小值的位置关系,谁在左边,谁在右边?
把 x 与最后一个数 nums[n−1] 比大小:
- 如果 x > nums[n−1],那么可以推出以下结论:
- nums 一定被分成左右两个递增段;
- 第一段的所有元素均大于第二段的所有元素;
- x 在第一段。
- 最小值在第二段。
- 所以 x 一定在最小值的左边。
- 如果 x≤nums[n−1],那么 x 一定在第二段。(或者 nums 就是递增数组,此时只有一段。)
- x 要么是最小值,要么在最小值右边。
所以,只需要比较 x 和 nums[n−1] 的大小关系,就间接地知道了 x 和数组最小值的位置关系,从而不断地缩小数组最小值所在位置的范围,二分找到数组最小值。
//开区间二分(-1 , n-1)
class Solution {
public:
int findMin(vector<int>& nums) {
int n = nums.size();
int l = -1 , r = n - 1;//开区间(-1 , n - 1)
while(l + 1 < r){
int mid = (l + r) >> 1;
if(nums[mid] >= nums[n - 1]) l = mid;
else r = mid;
//这一段也可写为:
//(nums[mid] < nums.back() ? r : l) = mid;
}
return nums[r];
}
};
2. 两数相加 - 力扣(LeetCode)
本题的链表是从数字的最低位开始的
方法一:递归
把虚线内要计算的内容,可以理解为一个和原问题相似的,规模更小的子问题,所以非常适合用递归解决。
每次把两个节点值 l1.val, l2.val 与进位值 carry 相加,除以 10 的余数即为当前节点需要保存的数位,除以 10 的商即为新的进位值。
代码实现时,有一个简化代码的小技巧:如果递归中发现 l2 的长度比 l1 更长,那么可以交换 l1 和 l2,保证 l1 不是空节点,从而简化代码逻辑。
class Solution {
public:
// l1 和 l2 为当前遍历的节点,carry 为进位
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2, int carry = 0) {
if (l1 == nullptr && l2 == nullptr) { // 递归边界:l1 和 l2 都是空节点
return carry ? new ListNode(carry) : nullptr; // 如果进位了,就额外创建一个节点
}
if (l1 == nullptr) { // 如果 l1 是空的,那么此时 l2 一定不是空节点
swap(l1, l2); // 交换 l1 与 l2,保证 l1 非空,从而简化代码
}
int sum = carry + l1->val + (l2 ? l2->val : 0); // 节点值和进位加在一起
l1->val = sum % 10; // 每个节点保存一个数位(直接修改原链表)
l1->next = addTwoNumbers(l1->next, (l2 ? l2->next : nullptr), sum / 10); // 进位
return l1;
}
};
方法二:迭代
迭代的思路是,初始化答案为一个「空链表」,每次循环,向该链表末尾添加一个节点(保存一个数位)。
循环即遍历链表 l1 和 l2,每次把两个节点值 l1.val, l2.val 与进位值 carry 相加,除以 10 的余数即为当前节点需要保存的数位,除以 10 的商即为新的进位值。
需要注意的是,在第一次循环时,我们无法往一个空节点的末尾添加节点。这里的技巧是,创建一个哨兵节点(dummy node),当成初始的「空链表」。循环结束后,哨兵节点的下一个节点就是最终要返回的链表头节点。
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode dummy; // 哨兵节点
ListNode* cur = &dummy;
int carry = 0; // 进位
while (l1 || l2 || carry) { // 有一个不是空节点,或者还有进位,就继续迭代
if (l1) {
carry += l1->val; // 节点值和进位加在一起
l1 = l1->next; // 下一个节点
}
if (l2) {
carry += l2->val; // 节点值和进位加在一起
l2 = l2->next; // 下一个节点
}
cur = cur->next = new ListNode(carry % 10); // 每个节点保存一个数位
carry /= 10; // 新的进位
}
return dummy.next; // 哨兵节点的下一个节点就是头节点
}
};
补充题6. 手撕堆排序
class Solution {
// 维护最大堆性质(正确实现)
void maxHeapify(vector<int>& a, int i, int heapSize) {
int l = i * 2 + 1; // 左子节点索引
int r = i * 2 + 2; // 右子节点索引
int largest = i; // 记录最大值的索引
// 比较左子节点
if (l < heapSize && a[l] > a[largest])
largest = l;
// 比较右子节点
if (r < heapSize && a[r] > a[largest])
largest = r;
// 若子节点更大,交换并递归调整
if (largest != i) {
swap(a[i], a[largest]);
maxHeapify(a, largest, heapSize);
}
}
// 构建最大堆
void buildMaxHeap(vector<int>& a, int heapSize) {
// 从最后一个非叶子节点开始调整
for (int i = heapSize / 2 - 1; i >= 0; --i) {
maxHeapify(a, i, heapSize);
}
}
public:
vector<int> sortArray(vector<int>& nums) {
int n = nums.size();
// 1. 构建初始最大堆
buildMaxHeap(nums, n);
// 2. 逐步提取最大值并调整堆
for (int i = n - 1; i > 0; --i) {
swap(nums[0], nums[i]); // 将当前最大值移到末尾
maxHeapify(nums, 0, i); // 调整剩余元素的堆结构
}
return nums;
}
};
46. 全排列 - 力扣(LeetCode)
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> res;
vector<int> vec;
vector<int> used(nums.size() , false);
auto dfs = [&](this auto&& dfs){
if(vec.size() == nums.size()){
res.emplace_back(vec);
return;
}
for (int i = 0; i < nums.size(); i++) {
if(used[i]) continue;
used[i] = true;
vec.push_back(nums[i]);
dfs();
vec.pop_back();
used[i] = false;
}
};
dfs();
return res;
}
};

浙公网安备 33010602011771号