文章目录
- 1、06 从尾到头打印链表
- 2、24 反转链表
- 3、35 复杂链表的复制
- 4、09 用两个栈实现队列
- 5、30 包含min函数的栈
- 6、5 替换空格
- 7、58 - II 左旋转字符串
- 8、03 数组中重复的数字
- 9、53 - I 在排序数组中查找数字
- 10、53 - II 0~n-1中缺失的数字
- 11、04 二维数组中的查找
- 12、11 旋转数组的最小数字
- 13、50 第一个只出现一次的字符
- 14、32 - I 从上到下打印二叉树
- 15、32 - II. 从上到下打印二叉树 II
- 16、32 - III 从上到下打印二叉树 III
- 17、26 树的子结构
- 18、27 二叉树的镜像
- 19、28 对称的二叉树
- 20、10- I 斐波那契数列 & 10- II. 青蛙跳台阶问题
- 21、63 股票的最大利润
- 22、42 连续子数组的最大和
- 23、47 礼物的最大价值
- 24、46 把数字翻译成字符串
- 25、48. 最长不含重复字符的子字符串
- 26、18 删除链表的节点
- 27、22 链表中倒数第k个节点
- 28、25 合并两个排序的链表
- 29、52 两个链表的第一个公共节点
- 30、21 调整数组顺序使奇数位于偶数前面
- 31、57 和为s的两个数字
- 32、58 - I 翻转单词顺序
- 33、12 矩阵中的路径
- 34、13 机器人的运动范围
- 35、34 二叉树中和为某一值的路径
- 36、36 二叉搜索树与双向链表
- 37、54 二叉搜索树的第k大节点
- 38、45 把数组排成最小的数
- 39、61 扑克牌中的顺子
- 40、40 最小的k个数
- 41、41 数据流中的中位数
- 42、55 - I 二叉树的深度
- 43、55 - II 平衡二叉树
- 44、 64 求1+2+…+n
- 45、68 - I 二叉搜索树的最近公共祖先
- 46、68 - II 二叉树的最近公共祖先
- 47、07 重建二叉树
- 48、16 数值的整数次方
- 49、33 二叉搜索树的后序遍历序列
- 50、15 二进制中1的个数
- 51、65 不用加减乘除做加法
- 52、56 - I 数组中数字出现的次数
- 53、56 - II 数组中数字出现的次数 II
- 54、39 数组中出现次数超过一半的数字
- 55、66 构建乘积数组
- 56、57 - II 和为s的连续正数序列
- 57、62 圆圈中最后剩下的数字
- 58、14- I 剪绳子
- 59、29 顺时针打印矩阵
- 60、31 栈的压入、弹出序列
- 61、20 表示数值的字符串
- 62、67 把字符串转换成整数
- 63、59 - I 滑动窗口的最大值
- 64、59 - II 队列的最大值
- 65、38 字符串的排列
- 66、37 序列化二叉树
- 67、19 正则表达式匹配
- 68、49 丑数
- 69、60 n个骰子的点数
- 70、17 打印从1到最大的n位数
- 71、51 数组中的逆序对
- 72、14- II 剪绳子 II
- 73、43 1~n 整数中 1 出现的次数
- 74、44 数字序列中某一位的数字
- Hot 100
- 75、1. 两数之和
- 76、2. 两数相加
- 77、3. 无重复字符的最长子串
- 78、4. 寻找两个正序数组的中位数
- 79、5. 最长回文子串
- 80、6. N 字形变换
- 81、7. 整数反转
- 82、8. 字符串转换整数 (atoi)
- 83、11. 盛最多水的容器
- 84、15. 三数之和
- 85、17. 电话号码的字母组合
- 86、19. 删除链表的倒数第 N 个结点
- 87、21. 合并两个有序链表
- 88、22. 括号生成
- 89、23. 合并 K 个升序链表
- 90、31. 下一个排列
- 91、32. 最长有效括号
- 92、33. 搜索旋转排序数组
- 93、34. 在排序数组中查找元素的第一个和最后一个位置
- 94、39. 组合总和
- 95、42. 接雨水
- 96、46. 全排列
- 97、48. 旋转图像
- 98、49. 字母异位词分组
- 99、55. 跳跃游戏
- 100、56. 合并区间
- 101、62. 不同路径
- 102、64. 最小路径和
- 103、70. 爬楼梯
- 104、72. 编辑距离
- 105、75. 颜色分类
- 106、76. 最小覆盖子串
- 107、78. 子集
- 108、79. 单词搜索
- 109、84. 柱状图中最大的矩形
- 110、85. 最大矩形
- 111、94. 二叉树的遍历
- 112、96. 不同的二叉搜索树
- 113、98. 验证二叉搜索树
- 114、101. 对称二叉树
- 115、102. 二叉树的层序遍历
- 116、114. 二叉树展开为链表
- 117、124. 二叉树中的最大路径和
- 118、128. 最长连续序列
- 119、136. 只出现一次的数字
- 120、139. 单词拆分
- 121、141. 环形链表
- 122、142. 环形链表 II
- 123、146. LRU 缓存
- 124、148. 排序链表
- 125、152. 乘积最大子数组
- 126、155. 最小栈
1、06 从尾到头打印链表
方法一:先用栈逆序,再用vector返回
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
stack<int> ans;
vector<int> realans;
ListNode* cur = head;
while(cur!=NULL){
ans.push(cur->val);
cur = cur->next;
}
while(!ans.empty()){
realans.push_back(ans.top());
ans.pop();
}
return realans;
}
};
方法二:只需一个vector,用reverse函数
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
vector<int> ans;
//vector<int> realans;
ListNode* cur = head;
while(cur!=NULL){
ans.push_back(cur->val);
cur = cur->next;
}
reverse(ans.begin(),ans.begin()+ans.size());
return ans;
}
};
方法三:两个vector
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
vector<int> ans;
vector<int> realans;
ListNode* cur = head;
while(cur!=NULL){
ans.push_back(cur->val);
cur = cur->next;
}
int length = ans.size();
realans.resize(length);
for(int i = 0 ; i< length ; ++i){
realans[i] = ans[length -i-1];
}
return realans;
}
};
方法四:得到链表长度,倒着写入vector
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
// PART1 得到链表长度
int cnt = 0;
ListNode *q = head;
while (q != NULL) {
cnt++;
q = q->next;
}
// PART2 倒着写入vector
vector<int> arr(cnt);
q = head;
for (int i = cnt - 1; i >= 0; i--){
arr[i] = q->val;
q = q->next;
}
return arr;
}
};
方法五:递归(回溯)
class Solution {
public:
vector<int>ans;
vector<int> reversePrint(ListNode* head) {
if(!head)
return ans;
reversePrint(head->next);
ans.push_back(head->val);
return ans;
}
};
class Solution {
int[] res;
int i = 0;
int j = 0;
public int[] reversePrint(ListNode head) {
solve(head);
return res;
}
public void solve(ListNode head){
if(head == null){
res = new int[i];
return;
}
i++;
solve(head.next);
res[j] = head.val;
j++;
}
}
方法六:vector的insert()函数
2、24 反转链表
方法一:迭代(双指针)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* cur = head;
ListNode* pre = nullptr;
ListNode* tmp;
while(cur!=nullptr){
tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
};
方法二:递归
class Solution {
public:
ListNode* reverseList(ListNode* head) {
while(head==nullptr || head->next == nullptr){
return head;
}
ListNode* node = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return node;
}
};
3、35 复杂链表的复制
补充:简单链表的复制
#include <iostream>
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node(int _val) {
val = _val;
next = NULL;
}
};
class Solution {
public:
Node* copyRandomList(Node* head) {
Node* cur = head;
Node* dum = new Node(0), * pre = dum;
while (cur != nullptr) {
Node* node = new Node(cur->val); // 复制节点 cur
pre->next = node; // 新链表的 前驱节点 -> 当前节点
cur = cur->next; // 遍历下一节点
pre = node; // 保存当前新节点
}
return dum->next;
}
};
int main() {
//定义一个链表 : a->b->c
Node* a = new Node(5);
Node* b = new Node(3);
Node* c = new Node(4);
a->next = b;
b->next = c;
Solution solution;
Node* new_list = solution.copyRandomList(a);
std::cout << new_list->val << std::endl;
std::cout << new_list->next->val << std::endl;
std::cout << new_list->next->next->val << std::endl;
}
方法一:哈希表添加键值对复制链表,再构建新节点的 next 和 random 引用指向
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head == nullptr) return nullptr;
Node* cur = head;
unordered_map<Node*, Node*> map;
while(cur!=nullptr){
map[cur] = new Node(cur->val);
cur = cur->next;
}
cur = head;
while(cur!=nullptr){
map[cur]->next = map[cur->next] ;
map[cur]->random = map[cur->random];
cur = cur->next;
}
return map[head];
}
};
方法二:拼接 + 拆分
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head == nullptr) return nullptr;
Node* cur = head;
//复制节点,拼接链表
while(cur!=nullptr){
Node* dum = new Node(cur->val);
dum->next = cur->next;
cur->next = dum;
cur = dum->next;
}
//完善random指针
cur = head;
while(cur!=nullptr){
if(cur->random != nullptr)
cur->next->random = cur->random->next;
cur = cur->next->next;
}
// 拆分两链表
cur = head->next;
Node* pre = head, *res = head->next;
while(cur->next != nullptr) {
pre->next = pre->next->next;
cur->next = cur->next->next;
pre = pre->next;
cur = cur->next;
}
pre->next = nullptr; // 单独处理原链表尾节点
return res;
}
};
4、09 用两个栈实现队列
方法一:
每次调用deleteHead()时,把A中所有元素移到B中,实现逆序,弹出B的栈顶;再把B中的剩余元素移回A内。
class CQueue {
public:
stack<int> stack1;
stack<int> stack2;
CQueue() {}
void appendTail(int value) {
stack1.push(value);
return;
}
int deleteHead() {
if(stack1.empty()==true&&stack2.empty()==true)
return -1;
if(stack2.empty()!=true){
int tmp = stack2.top();
stack2.pop();
return tmp;
}else{
while(stack1.empty()!=true){
int tmp2 = stack1.top();
stack2.push(tmp2);
stack1.pop();
}
int ans = stack2.top();
stack2.pop();
return ans;
}
}
};
/**
* Your CQueue object will be instantiated and called as such:
* CQueue* obj = new CQueue();
* obj->appendTail(value);
* int param_2 = obj->deleteHead();
*/
方法二:(更优)
stack1作为输入栈,stack2为输出栈。
输出时有三种情况:
1、当栈 B 不为空: B中仍有已完成倒序的元素,因此直接返回 B 的栈顶元素。
2、B空 A 为空: 即两个栈都为空,无元素,因此返回 -1
3、将栈 A 元素全部转移至栈 B 中,实现元素倒序,并返回栈 B 的栈顶元素。(B空A不空)
class CQueue {
public:
stack<int> stack1;
stack<int> stack2;
CQueue() {}
void appendTail(int value) {
stack1.push(value);
return;
}
int deleteHead() {
while(stack1.empty()!=true){
int tmp = stack1.top();
stack2.push(tmp);
stack1.pop();
}
if(stack2.empty()==true)
return -1;
int ans = stack2.top();
stack2.pop();
while(stack2.empty()!=true){
int tmp3 = stack2.top();
stack1.push(tmp3);
stack2.pop();
}
return ans;
}
};
5、30 包含min函数的栈
在B栈中把当前栈的最小值存起来。
class MinStack {
public:
/** initialize your data structure here. */
stack <int> A;
stack <int> B;
MinStack() {
B.push(INT_MAX);
}
void push(int x) {
A.push(x);
B.push(::min(x, B.top()));
}
void pop() {
A.pop();
B.pop();
}
int top() {
return A.top();
}
int min() {
return B.top();
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack* obj = new MinStack();
* obj->push(x);
* obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->min();
*/
6、5 替换空格
class Solution {
public:
string ans;
string replaceSpace(string s) {
for(char c : s){
if (c == ' '){
ans.append("%20");
}else{
ans += c;
}
}
return ans;
}
};
7、58 - II 左旋转字符串
方法一:
class Solution {
public:
string reverseLeftWords(string s, int n) {
string ans;
for(int i = n; i<s.size(); i++){
ans += s[i];
}
for(int i = 0; i<n; i++){
ans += s[i];
}
return ans;
}
};
// 取余简化
// for(int i = n; i<n + s.size(); i++){
// ans += s[ i % s.size()];
// }
方法二:substr()
string s;
s.substr(i,len);
//从s的i位开始截取长度为len的串
class Solution {
public:
string reverseLeftWords(string s, int n) {
string ans;
return ans.append(s.substr(n,s.size()-1)).append(s.substr(0,n));
}
};
8、03 数组中重复的数字
方法一:哈希表
class Solution {
public:
int ans;
int findRepeatNumber(vector<int>& nums) {
unordered_map<int, int> map;
for(int num : nums){
map[num]++;
if(map[num]==2){
return num;
break;
}
}
return -1;
}
};
class Solution {
public:
int ans;
int findRepeatNumber(vector<int>& nums) {
unordered_map<int, bool> map;
for(int num : nums){
if(map[num] == true) return num;
map[num] = true;
}
return -1;
}
};
方法二:特解,交换
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
int i = 0;
while(i < nums.size()) {
if(nums[i] == i) {
i++;
continue;
}
if(nums[nums[i]] == nums[i])
return nums[i];
swap(nums[i],nums[nums[i]]);
}
return -1;
}
};
作者:jyd
链接:https://leetcode.cn/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/solution/mian-shi-ti-03-shu-zu-zhong-zhong-fu-de-shu-zi-yua/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
9、53 - I 在排序数组中查找数字
方法一 哈希表
class Solution {
public:
map<int, int> map;
int search(vector<int>& nums, int target) {
for(int num : nums){
map[num]++;
}
return map[target];
}
};
方法二:二分查找1
class Solution {
public:
map<int, int> map;
int search(vector<int>& nums, int target) {
return(upper_bound(nums.begin(),nums.end(),target) - lower_bound(nums.begin(),nums.end(),target));
}
};
lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
#include <iostream>
#include <vector>
#include <algorithm>
std::vector<int> v = { 0 ,2 ,1 ,4 ,7 ,6 };
std::vector<int>::iterator up1;
up1 = lower_bound(v.begin(), v.end(), 4);
std::cout << up1 - v.begin() << std::endl;
原文链接:https://blog.csdn.net/weixin_53225765/article/details/122798643
方法三:二分查找2
class Solution {
public:
int search(vector<int>& nums, int target) {
// 找第一个比target大的
int i = 0, j = nums.size()-1;
while(i <= j){
int m = (i + j) / 2;
if(nums[m] <= target){
i = m + 1;
}else{
j = m - 1;
}
}
int right = i;
// 若数组中无 target ,则提前返回
if(j >= 0 && nums[j] != target)
return 0;
// 找最后一个比target小的
i = 0, j = nums.size()-1; // 可以不重设j
while(i <= j){
int m = (i + j) / 2;
if(nums[m] < target){
i = m + 1;
}else{
j = m - 1;
}
}
int left = j;
return right - left - 1;
}
};
class Solution {
public:
int helper(vector<int>& nums, int target){
int i = 0, j = nums.size()-1;
while(i <= j){
int m = (i + j) / 2;
if(nums[m] <= target){
i = m + 1;
}else{
j = m - 1;
}
}
return i;
}
int search(vector<int>& nums, int target) {
return(helper(nums, target)- helper(nums, target-1));
}
};
10、53 - II 0~n-1中缺失的数字
方法一:二分查找
while结束时,变量 i 和 j 分别指向 “右子数组的首位元素” 和 “左子数组的末位元素” 。因此返回 i 。
左子数组元素==下标,右子数组元素比下表大1。
class Solution {
public:
int missingNumber(vector<int>& nums) {
int i = 0, j = nums.size()-1;
while(i<=j){
int m = (i + j) / 2;
if(nums[m] - m == 0){
i = m + 1;
}else{
j = m - 1;
}
}
return i;
}
};
方法二:其他O(n)方法
1、都加入哈希集合再遍历
2、直接遍历
11、04 二维数组中的查找
方法一:对每一行二分,逐行查找
复杂度:O(nlogm)
class Solution {
public:
int helper(vector<int> nums, int target){
int i = 0, j = nums.size() -1;
while(i <= j){
int m = (i + j) / 2;
if(nums[m] <= target){
i = m + 1;
}else{
j = m - 1;
}
}
if(j >= 0 && nums[j] != target){
return 0;
}else if(j < 0){
return 0;
}else{
return 1;
}
}
int ans;
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
for(int index = 0; index < matrix.size(); index++){
ans += helper(matrix[index], target);
if(ans > 0){return true;}
}
return false;
}
};
也可写作
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
for (const auto& row: matrix) {
auto it = lower_bound(row.begin(), row.end(), target);
if (it != row.end() && *it == target) {
return true;
}
}
return false;
}
};
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/solution/mian-shi-ti-04-er-wei-shu-zu-zhong-de-cha-zhao-b-3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
方法二
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
int i = matrix.size()-1, j = 0;
while(i>-1 && j < matrix[0].size()){
if(matrix[i][j] == target){
return true;
}else if(matrix[i][j] > target){
i--;
}else{
j++;
}
}
return false;
}
};
12、11 旋转数组的最小数字
class Solution {
public:
int minArray(vector<int>& numbers) {
int i = 0, j = numbers.size() - 1;
while (i < j) {
int m = (i + j) / 2;
if (numbers[m] > numbers[j]) i = m + 1;
else if (numbers[m] < numbers[j]) j = m;
else j--;
}
return numbers[i];
}
};
作者:jyd
链接:https://leetcode.cn/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/solution/mian-shi-ti-11-xuan-zhuan-shu-zu-de-zui-xiao-shu-3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
13、50 第一个只出现一次的字符
哈希表
class Solution {
public:
char firstUniqChar(string s) {
unordered_map<char, int> map;
for(char c : s){
map[c] ++;
}
for(char c : s){
if(map[c] == 1)
return c;
}
return ' ';
}
};
储存真值,而不是次数,更合理。
class Solution {
public:
char firstUniqChar(string s) {
unordered_map<char, bool> dic;
for(char c : s)
dic[c] = dic.find(c) == dic.end();
for(char c : s)
if(dic[c]) return c;
return ' ';
}
};
作者:jyd
链接:https://leetcode.cn/problems/di-yi-ge-zhi-chu-xian-yi-ci-de-zi-fu-lcof/solution/mian-shi-ti-50-di-yi-ge-zhi-chu-xian-yi-ci-de-zi-3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
14、32 - I 从上到下打印二叉树
题目要求的二叉树从上至下打印(即按层打印),又称为二叉树的广度优先搜索(BFS)。
BFS 通常借助队列的先入先出特性来实现。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> ans;
queue<TreeNode*> q;
vector<int> levelOrder(TreeNode* root) {
if(root != nullptr) q.push(root);
while(!q.empty()){
int cnt = q.size();
for(int i = 0; i < cnt ; i++){
TreeNode* tmp = q.front();
ans.push_back(tmp->val);
q.pop();
if(tmp->left != NULL) q.push(tmp->left);
if(tmp->right!= NULL) q.push(tmp->right);
}
}
return ans;
}
};
15、32 - II. 从上到下打印二叉树 II
和上一道题非常类似
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ans;
queue<TreeNode*> q;
if(root != nullptr) q.push(root);
while(!q.empty()){
vector<int> tmpVec;
int cnt = q.size();
for(int i = 0; i < cnt; i++){
TreeNode* tmp = q.front();
q.pop();
tmpVec.push_back(tmp->val);
if(tmp->left!=NULL) q.push(tmp->left);
if(tmp->right!=NULL) q.push(tmp->right);
}
ans.push_back(tmpVec);
}
return ans;
}
};
16、32 - III 从上到下打印二叉树 III
方法一:使用flag
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ans;
queue<TreeNode*> q;
int cnt= 0;
if(root != nullptr) q.push(root);
while(!q.empty()){
vector<int> tmpVec;
int size = q.size();
for(int i = 0; i < size; i++){
TreeNode* tmp = q.front();
tmpVec.push_back(tmp->val);
q.pop();
if(tmp->left!=NULL) q.push(tmp->left);
if(tmp->right!=NULL) q.push(tmp->right);
}
if(cnt%2==1)
reverse(tmpVec.begin(), tmpVec.end());
ans.push_back(tmpVec);
cnt++;
}
return ans;
}
};
方法二:使用deque
可以把储存节点指针的栈或方法一中的tmpVec换成双向栈,以实现反向。
17、26 树的子结构
好难。。。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool helper(TreeNode* node1, TreeNode* node2){
//此方法判断以node2为根节点的树是否为以node1为根节点的子树
if(node2 == nullptr) return true;
if(node1 == nullptr || node1->val != node2->val) return false;
return helper(node1->left, node2->left) && helper(node1->right, node2->right);
}
bool isSubStructure(TreeNode* A, TreeNode* B) {
if (B == nullptr) return false;
if (A == nullptr) return false;
return helper(A, B) || isSubStructure(A->left, B) || isSubStructure(A->right, B);
}
};
18、27 二叉树的镜像
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* mirrorTree(TreeNode* root) {
if(root == nullptr) return nullptr;
TreeNode* tmp = root->left;
root->left = mirrorTree(root->right);
root->right = mirrorTree(tmp);
return root;
}
};
19、28 对称的二叉树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool helper(TreeNode* a, TreeNode* b){
if(a==nullptr && b==nullptr) return true;
if(a==nullptr || b==nullptr) return false;
return a->val == b->val && helper(a->left, b->right) && helper(a->right, b->left);
}
bool isSymmetric(TreeNode* root) {
return helper(root,root);
}
};
20、10- I 斐波那契数列 & 10- II. 青蛙跳台阶问题
动态规划
class Solution {
public:
int fib(int n) {
long int p = 0;
long int q = 1;
for(int i=0; i<n; ++i){
long int tmp = (p + q)% 1000000007;
p = q;
q = tmp;
}
return p;
}
};
21、63 股票的最大利润
方法一:暴力遍历
class Solution {
public:
int maxProfit(vector<int>& prices) {
int size = prices.size();
int ans = 0;
for(int i = 0; i < size; i++){
for(int j = i+1; j < size; j++){
if(prices[j] - prices[i] > ans)
ans = prices[j] - prices[i];
}
}
return ans;
}
};
方法二:动态规划
class Solution {
public:
int maxProfit(vector<int>& prices) {
int size = prices.size();
int minPrice = INT_MAX;
int ans = 0;
for(int i = 0; i < size; i++){
if(minPrice > prices[i])
minPrice = prices[i];
if(prices[i]-minPrice > ans)
ans = prices[i]-minPrice;
}
return ans;
}
};
class Solution {
public:
int maxProfit(vector<int>& prices) {
int size = prices.size();
if (!size) {
return 0;
} // 如果为空,返回0
vector<int> ans;
// ans[i]表示第i天卖出的最大收益,=max(第i天卖出的收益,第i-1天卖出的收益)
// 第i天卖出的收益 = prices[i] - 有史以来的最低价
int minPrice = prices[0];
ans.push_back(0);
for(int i = 1; i < size; i++){
minPrice = min(minPrice, prices[i]);
ans.push_back( max(ans[i-1], prices[i] - minPrice) );
}
return ans[size-1];
}
};
22、42 连续子数组的最大和
方法一:动态规划(最优)
状态转移:以第i位结尾的最大数组和 = max(nums[i],以第i-1位结尾的最大数组和+nums[i])
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int size = nums.size();
vector<int> ans;
ans.push_back(nums[0]);
for(int i = 1; i < size; i++){
ans.push_back(max(nums[i], nums[i]+ans[i-1]));
}
sort(ans.begin(), ans.end(),greater<int>());
return ans[0];
}
};
节省stl扩容开销:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int size = nums.size();
int ans = nums[0];
int maxAns = nums[0];
for(int i = 1; i < size; i++){
ans = max(nums[i]+ans, nums[i]);
maxAns = max(maxAns, ans);
}
return maxAns;
}
};
方法二:前缀和
ans = 所有“当前前缀和 - 之前出现过的最小的前缀和”的最大值
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int ans = INT_MIN;
int presum = 0;
int min = 0;
for(int i = 0; i < nums.size(); i++){
presum += nums[i];
ans = max(ans, presum - min);
if(presum < min) min = presum;
}
return ans;
}
};
23、47 礼物的最大价值
更新grid每个元素的值为到这个位置的最大价值。
class Solution {
public:
int maxValue(vector<vector<int>>& grid) {
for(int j = 1; j < grid[0].size(); j++){
grid[0][j] += grid[0][j-1];
}//处理第一行
for(int i = 1; i<grid.size(); i++){
grid[i][0] += grid[i-1][0];
}//处理第一列
for(int i = 1; i<grid.size(); i++){
for(int j = 1; j<grid[0].size(); j++){
grid[i][j] = max(grid[i-1][j] +grid[i][j], grid[i][j-1] +grid[i][j]);
}
}
return grid[grid.size()-1][grid[0].size()-1];
}
};
24、46 把数字翻译成字符串
class Solution {
public:
int translateNum(int num) {
string str = to_string(num);
int len = str.size();
if(len < 2) return len;
vector<int> dp(len+1);
dp[1] = 1;//第一个数字有一种翻译方法
dp[0] = 1;//无数字有一种翻译方法
for(int i = 2;i <= len;i++){
if(str[i-2] == '1' || (str[i-2] == '2' && str[i-1] <= '5')) dp[i] = dp[i-2]+dp[i-1];
else dp[i] = dp[i-1];
}
return dp[len];
}
};
节省内存:
class Solution {
public:
int translateNum(int num) {
string str = to_string(num);
int len = str.size();
int pre = 1;
int cur = 1;
for(int i = 2; i < len + 1; i++){
string tmpStr = str.substr(i-2, 2);
if(tmpStr <= "25" && tmpStr >= "10"){
auto t = cur;
cur = pre + cur;
pre = t;
}
else{
//cur = cur;
pre = cur;
}
}
return cur;
}
};
25、48. 最长不含重复字符的子字符串
双指针哈希表
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int ans = 0;
unordered_map<int,int> hash;
for (int i=0,j=0;i<s.size();++i)
{
hash[s[i]]++;
while (hash[s[i]] > 1) hash[s[j]]--,j++;
ans = max(ret,i-j+1);
}
return ans;
}
};
26、18 删除链表的节点
双指针
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
ListNode* i;
ListNode* j = head;
while(head->val == val){
return head->next;
}
while(j){
if(j->val == val)
i->next = j->next;
i = j;
j = j->next;
}
return head;
}
};
27、22 链表中倒数第k个节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* helper(ListNode* a, int k){
ListNode* help = a;
for(int i = 0; i < k; i++){
help = help->next;
}
return help;
}
ListNode* getKthFromEnd(ListNode* head, int k) {
ListNode* i = head;
ListNode* j = helper(i, k);
while(j != nullptr){
i = i->next;
j = j->next;
}
return i;
}
};
28、25 合并两个排序的链表
方法一:
新建一个虚拟头节点,以及cur指针。当遍历完一个链表时,把另一个链表的剩余部分直接加在最后面。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
// if(!l1) return l2;
// if(!l2) return l1;
ListNode* ans = new ListNode(0);
ListNode* cur = ans;
while(l1 && l2){
if(l1->val > l2->val){
cur->next = l2;
l2 = l2->next;
}else{
cur->next = l1;
l1 = l1->next;
}
cur = cur->next;
}
if(!l1) cur->next = l2;
else cur->next = l1;
return ans->next;
}
};
方法二:递归
如果l1.val <= l2.val,那么头结点的值为l1.head的值,然后开始递归剩下的部分
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if(!l1) return l2;
if(!l2) return l1;
if(l1->val > l2->val){
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}else{
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
}
};
29、52 两个链表的第一个公共节点
方法一:hashset
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_set<ListNode*> set;
while(headA){
set.insert(headA);
headA = headA->next;
}
while(headB){
if(set.find(headB) != set.end()) return headB;
headB = headB->next;
}
return nullptr;
}
};
方法二:浪漫相遇
class Solution {
public:
ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
ListNode* p1 = headA, *p2 = headB;
while(p1 != p2){
p1 = p1 == nullptr ? headB : p1->next;
p2 = p2 == nullptr ? headA : p2->next;
}
return p1;
}
};
作者:LaoGanMaIsEverything
链接:https://leetcode.cn/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof/solution/yi-zhang-tu-jiu-ming-bai-ai-qing-jie-shi-up3a/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
30、21 调整数组顺序使奇数位于偶数前面
双指针
class Solution {
public:
vector<int> exchange(vector<int>& nums) {
int i = 0, j = nums.size()-1;
while(i < j){
while(i < j && nums[i] % 2 == 1) i++;
while(i < j && nums[j] % 2 == 0) j--;
swap(nums[i], nums[j]);
}
return nums;
}
};
31、57 和为s的两个数字
方法一:双指针
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
//经典双指针
vector<int> ans;
int l = 0,r = nums.size() - 1;
while(l < r){
if(nums[l] + nums[r] > target) r -- ;
else if(nums[l] + nums[r] < target) l ++ ;
else{
break;
}
}
ans.push_back(nums[l]);
ans.push_back(nums[r]);
return ans;
}
};
方法二:哈希表
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> ans;
unordered_map<int, int> map;
int size = nums.size();
for(auto num : nums){
map[num] = target - num;
if(map[target-num] && target-num != num){
ans.push_back(num);
ans.push_back(target - num);
return ans;
}
}
return ans;
}
};
32、58 - I 翻转单词顺序
双指针
class Solution {
public:
string reverseWords(string s) {
string res;
int j = s.size() - 1;
while (j >= 0) {
while (j >= 0 && s[j] == ' ') j--;
if (j == - 1) break;
int i = j;
while (i >= 0 && s[i] != ' ') i--;
res.append(s.substr(i + 1, j - i));
res.push_back(' ');
j = i;
}
res.pop_back();
return res;
}
};
33、12 矩阵中的路径
难
class Solution {
public:
int rows, cols;
bool dfs(vector<vector<char>>& board, string word, int i, int j, int k){
if(i >= rows || i < 0 || j >= cols || j < 0 || board[i][j] != word[k]) return false;
if(k == word.size() - 1) return true;
board[i][j] = '\0';
bool res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) ||
dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
board[i][j] = word[k];
return res;
}
bool exist(vector<vector<char>>& board, string word) {
rows = board.size();
cols = board[0].size();
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
if(dfs(board, word, i, j, 0)) return true;
}
}
return false;
}
};
34、13 机器人的运动范围
方法一:DFS
// 回溯法
class Solution {
public:
int movingCount(int m, int n, int k) {
vector<vector<bool>> visited(m, vector<bool>(n, false));
return dfs(0, 0, m, n, k, visited);
}
private:
int dfs(int i, int j, int m, int n, int k, vector<vector<bool>>& visited) {
if (i >= m || j >= n || i / 10 + i % 10 + j / 10 + j % 10 > k || visited[i][j])
return 0;
visited[i][j] = true;
return 1 + dfs(i + 1, j, m, n, k, visited) + dfs(i, j + 1, m, n, k, visited);
}
};
方法二:BFS
class Solution {
public:
int movingCount(int m, int n, int k) {
vector<vector<bool>> visited(m, vector<bool>(n, false));
int res = 0;
queue<vector<int>> que;
que.push({0, 0});
while(que.size() > 0) {
vector<int> x = que.front();
que.pop();
int i = x[0], j = x[1];
if(i >= m || j >= n || k < i / 10 + i % 10 + j / 10 + j % 10 || visited[i][j]) continue;
visited[i][j] = true;
res++;
que.push({ i + 1, j});
que.push({ i, j + 1});
}
return res;
}
};
35、34 二叉树中和为某一值的路径
DFS
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> ans;
vector<int> subans;
void helper(TreeNode* root, int target){
if(root == nullptr)
return;
subans.push_back(root->val);
target = target - root->val;
if(root->left==nullptr && root->right==nullptr && target==0)
ans.push_back(subans);
helper(root->left, target);
helper(root->right, target);
subans.pop_back();
}
vector<vector<int>> pathSum(TreeNode* root, int target) {
helper(root, target);
return ans;
}
};
36、36 二叉搜索树与双向链表
中序遍历加递归
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node() {}
Node(int _val) {
val = _val;
left = NULL;
right = NULL;
}
Node(int _val, Node* _left, Node* _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
public:
Node*head, *pre;
void helper(Node* cur){
if(cur == nullptr) return;
helper(cur->left);
if(pre == nullptr){
head = cur;
pre = head;
}else{
cur->left = pre;
pre->right = cur;
pre = cur;
}
helper(cur->right);
}
Node* treeToDoublyList(Node* root) {
if(root == nullptr) return nullptr;
helper(root);
head -> left = pre;
pre -> right = head;
return head;
}
};
37、54 二叉搜索树的第k大节点
方法一:
易错点:如果把k放到参数中,每个递归函数中的 k 都是独立的(k 是数字,传入函数是值传递),这样的话只要回溯就会出错,因此要保持k的一致性,也就是对于每一个递归函数,k都是全局的。例如传入引用。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int ans;
void helper(TreeNode* root, int& k){
if(root == nullptr || k == 0)
return;
helper(root->right, k);
k = k-1;
if(k == 0) ans = root->val;
helper(root->left, k);
}
int kthLargest(TreeNode* root, int k) {
helper(root, k);
return ans;
}
};
或
int k1;
void helper(TreeNode* root, int k){
k1 = k;
if(root == nullptr || k1 == 0) return;
helper(root->right, k1);
k1 = k1-1;
if(k1 == 0) ans = root->val;
helper(root->left, k1);
}
方法二:
中序遍历存入vector,输出第k个。
38、45 把数组排成最小的数
补充:快速排序
class Solution {
public:
void quicksort(vector<int>& nums, int l, int r){
int i = l , j = r;
if(l>=r) return;
while(i<j){
while(i<j && nums[j] >= nums[l]) j--;
while(i<j && nums[i] <= nums[l]) i++;
swap(nums[i],nums[j]);
}
swap(nums[l], nums[i]);
quicksort(nums,l,i-1);
quicksort(nums, i+1,r);
}
vector<int> sortArray(vector<int>& nums) {
quicksort(nums,0, nums.size()-1);
return nums;
}
};
方法一:基于特殊规则的排序算法
class Solution {
public:
void quicksort(vector<string>& strs, int l, int r){
if(l >= r) return;
int i = l, j = r;
while(i<j){
while(strs[j] + strs[l] >= strs[l] + strs[j] && i<j) j--;
while(strs[i] + strs[l] <= strs[l] + strs[i] && i<j) i++;
swap(strs[i], strs[j]);
}
swap(strs[i], strs[l]);
quicksort(strs, l, i-1);
quicksort(strs, i+1, r);
}
string minNumber(vector<int>& nums) {
vector<string> strs;
for(int num : nums){
strs.push_back(to_string(num));
}
quicksort(strs, 0, strs.size()-1);
string ans;
for(auto var : strs){
ans.append(var);
}
return ans;
}
};
方法二:匿名函数
class Solution {
public:
string minNumber(vector<int>& nums) {
vector<string> strs;
string res;
for(int i = 0; i < nums.size(); i++)
strs.push_back(to_string(nums[i]));
sort(strs.begin(), strs.end(), [](string& x, string& y){ return x + y < y + x; });
for(int i = 0; i < strs.size(); i++)
res.append(strs[i]);
return res;
}
};
39、61 扑克牌中的顺子
class Solution {
public:
bool isStraight(vector<int>& nums) {
int cnt = 0;
sort(nums.begin(), nums.end());
for(int i = 0; i<nums.size()-1; i++){
if(nums[i] == 0) {
cnt++;
continue;
}
if(nums[i+1] == nums[i]) return false;
cnt = cnt - nums[i+1] + nums[i]+1;
}
if(cnt<0) return false;
else return true;
}
};
40、40 最小的k个数
方法一:API选手
class Solution {
public:
vector<int> ans;
vector<int> getLeastNumbers(vector<int>& arr, int k) {
if(arr.size() == 0) return ans;
sort(arr.begin(), arr.end());
for(int i = 0; i< k; i++){
ans.push_back(arr[i]);
}
return ans;
}
};
方法二:快速排序减少不必要的排序
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
if(k >= arr.size()) return arr;
return quicksort(arr, k, 0, arr.size()-1);
}
vector<int> quicksort(vector<int>& arr, int k, int l, int r){
int i = l, j = r;
while(i<j){
while(i<j && arr[j] >= arr[l]) j--;
while(i<j && arr[i] <= arr[l]) i++;
swap(arr[i], arr[j]);
}
swap(arr[i], arr[l]);
if(k<i) return quicksort(arr, k, l, i-1);
if(k>i) return quicksort(arr, k, i+1, r);
vector<int> ans;
ans.assign(arr.begin(), arr.begin()+k);
return ans;
}
};
41、41 数据流中的中位数
大小顶堆,优先队列
class MedianFinder {
public:
priority_queue<int, vector<int>, less<int>> queMin;
priority_queue<int, vector<int>, greater<int>> queMax;
MedianFinder() {}
void addNum(int num) {
if (queMin.empty() || num <= queMin.top()) {
queMin.push(num);
if (queMax.size() + 1 < queMin.size()) {
queMax.push(queMin.top());
queMin.pop();
}
} else {
queMax.push(num);
if (queMax.size() > queMin.size()) {
queMin.push(queMax.top());
queMax.pop();
}
}
}
double findMedian() {
if (queMin.size() > queMax.size()) {
return queMin.top();
}
return (queMin.top() + queMax.top()) / 2.0;
}
};
42、55 - I 二叉树的深度
方法一:后序遍历
ans = max(左子树最大深度,右子树最大深度) + 1
class Solution {
public:
int maxDepth(TreeNode* root) {
if(!root) return 0;
return max(maxDepth(root->left), maxDepth(root->right))+1;
}
};
方法二:利用队列层序遍历
class Solution {
public:
int maxDepth(TreeNode* root) {
if(!root) return 0;
int ans = 0;
queue<TreeNode*> q;
q.push(root);
while(!q.empty()){
int len = q.size();
for(int i = 0; i < len; ++i){
auto tmp = q.front();
q.pop();
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
++ans;
}
return ans;
}
};
43、55 - II 平衡二叉树
方法一:
当前root的 |左子树的最大深度 - 右子树最大深度 | <= 1 且 左子树和右子树都为平衡二叉树,那么这个root经受了平衡二叉树的考验。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isBalanced(TreeNode* root) {
if(!root) return true;
return abs(maxDepth(root->left) - maxDepth(root->right)) < 2
&&
isBalanced(root->left)
&&
isBalanced(root->right);
}
int maxDepth(TreeNode* root){
if (!root) return 0;
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
};
方法二:
不好想,helper()为每个节点赋一个值:如果不是平衡,那么就是-1,如果是平衡,那就是这个节点的最大深度。
class Solution {
public:
bool isBalanced(TreeNode* root) {
return helper(root) != -1;
}
int helper(TreeNode* root){
if (!root) return 0;
int left = helper(root->left);
int right = helper(root->right);
if(left == -1 || right == -1)
return -1;
if(abs(left - right) < 2)
return max(left, right) + 1;
else
return -1;
}
};
44、 64 求1+2+…+n
短路效应
class Solution {
public:
int sumNums(int n) {
n > 1 && (n += sumNums(n -1));
return n;
}
};
45、68 - I 二叉搜索树的最近公共祖先
方法一:利用二叉搜索树的性质
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
TreeNode* tmp = root;
while(1){
if(p->val < tmp->val && q->val < tmp->val)
tmp = tmp->left;
else if(p->val > tmp->val && q->val > tmp->val)
tmp = tmp->right;
else
break;
}
return tmp;
}
};
方法二:记录路线
第一个函数得到root到p的路径(包含root和p),第二个函数比较路径。
class Solution {
public:
vector<TreeNode*> getPath(TreeNode* root, TreeNode* p){
vector<TreeNode*> res;
//if(!root) return;
while(root != p){
res.push_back(root);
if(p->val > root->val)
root = root->right;
else
root = root->left;
}
res.push_back(root);
return res;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
vector<TreeNode*> pathP = getPath(root, p);
vector<TreeNode*> pathQ = getPath(root, q);
TreeNode* ans;
for(int i = 0; i < min(pathP.size(), pathQ.size()); i++){
if(pathP[i] == pathQ[i])
ans = pathP[i];
else
break;
}
return ans;
}
};
46、68 - II 二叉树的最近公共祖先
方法一:递归
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
//如果树为空,返回nullptr,如果p或q为root,返回root
if(root == nullptr) return nullptr;
if(p == root|| q == root) return root;
//递归遍历左子树,只要在左子树中找到了p或q,则先找到谁就返回谁
//递归遍历右子树
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
//如果在左子树中 p和 q都找不到,则 p和 q一定都在右子树中,右子树中先遍历到的那个就是最近公共祖先
if(left == nullptr) return right;
//否则,如果 left不为空,在左子树中有找到节点(p或q),这时候要再判断一下右子树中的情况,
//如果在右子树中,p和q都找不到,则 p和q一定都在左子树中,左子树中先遍历到的那个就是最近公共祖先
else if(right == nullptr) return left;
//否则,当 left和 right均不为空时,说明 p、q节点分别在 root异侧, 最近公共祖先即为 root
else return root;
}
};
方法二:记录路线
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
unordered_map<int, TreeNode*> path;
unordered_map<int, bool> visit;
//记录val与其父亲节点的键值对
void dfs(TreeNode* root){
if (root->left != nullptr) {
path[root->left->val] = root;
dfs(root->left);
}
if (root->right != nullptr) {
path[root->right->val] = root;
dfs(root->right);
}
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
path[root->val] = nullptr;
dfs(root);
// 将 p到 root的路径记录在visited中
while (p != nullptr) {
visit[p->val] = true;
p = path[p->val];
}
while (q != nullptr) {
if (visit[q->val]) return q;
q = path[q->val];
}
return nullptr;
}
};
47、07 重建二叉树
从前序中得到root(前序的第一个就是root),在中序查找root(存在map里提高查找效率),找到之后左边为左子树,右边为右子树,接着进行递归。
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return build(0, 0, preorder.size()-1, preorder, inorder);
}
int find(vector<int>& v, int x){
for(int i = 0; i < v.size(); ++i)
if(v[i] == x) return i;
return -1;
}
TreeNode* build(int idx, int l, int r, vector<int>& preorder, vector<int>& inorder){
if(l > r) return nullptr;
TreeNode* root = new TreeNode(preorder[idx]);
int helper = find(inorder, preorder[idx]);
root->left = build(idx+1, l, helper-1, preorder, inorder);
root->right = build(idx+helper-l+1, helper+1, r, preorder, inorder);
return root;
}
};
48、16 数值的整数次方
方法一:快速幂 + 递归
class Solution {
public:
double myPow(double x, int n) {
long long N = n;
if(n >= 0)
return helper(x, N);
else
return 1 / helper(x, -N);
}
double helper(double x, long long N){
if(N == 0) return 1;
double y = helper(x, N/2);
if(N % 2 == 0)
return y*y;
else
return y*y*x;
}
};
方法二:快速幂 + 迭代
class Solution {
public:
double myPow(double x, int n) {
long long N = n;
if(n >= 0)
return helper(x, N);
else
return 1 / helper(x, -N);
}
double helper(double x, long long N){
double ans = 1.0;
double tmp = x;
while(N > 0){
if(N % 2 == 1)
ans *= tmp;
tmp *= tmp;
N /= 2;
}
return ans;
}
};
49、33 二叉搜索树的后序遍历序列
对于索引为left到right的数组,nums[right]为根,因为是二叉搜索树,接下来在left到right-1中找到第一个比nums[right]大的,左边为左子树,右边为右子树,要求右子树中所有元素必须比nums[right]大,不然就为false,左子树已经满足。
class Solution {
public:
bool verifyPostorder(vector<int>& postorder) {
return helper(postorder, 0, postorder.size() - 1);
}
bool helper(vector<int>& nums, int left, int right){
if(left>=right) return true;
int index;
for(int i = left; i < right; i++){
if(nums[i] > nums[right]){
index = i;
break;
}
}
for(int j = index; j < right; j++){
if(nums[j] < nums[right])
return false;
}
return helper(nums, left, index-1) && helper(nums, index, right-1);
}
};
50、15 二进制中1的个数
方法一:
把n右移一个单位:n >>= 1
位运算:& 与;| 或; ^ 异或
class Solution {
public:
int hammingWeight(uint32_t n) {
int ans=0;
while(n){
if(n & 1) ans++;
n >>= 1;
}
return ans;
}
};
方法二:
n/2相当于右移一位
class Solution {
public:
int hammingWeight(uint32_t n) {
int ans=0;
while (n) {
ans += n % 2;
n /= 2;
}
return ans;
}
};
51、65 不用加减乘除做加法
(a & b) << 1为进位,a^b为本位计算结果
class Solution {
public:
int add(int a, int b) {
while (b != 0) {
unsigned int carry = (unsigned int)(a & b) << 1;
a = a ^ b;
b = carry;
}
return a;
}
};
52、56 - I 数组中数字出现的次数
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
//假设 ans = {x, y}
//因为相同的数字异或为0,任何数字与0异或结果是其本身。
//所以遍历异或整个数组最后得到的结果就是两个只出现一次的数字异或的结果:即 z = x ^ y
int z = 0;
for(int i : nums) z ^= i;
//z中至少有一位是1,因为存在不相等的x和y。
//我们通过一个辅助变量m来保存z中哪一位为1,可能有多个位都为1,我们找到最低位的1即可。
//举个例子:z = 10 ^ 2 = 1010 ^ 0010 = 1000,第四位为1.
//我们将m初始化为1,如果(z & m)的结果等于0说明z的最低为是0
//我们每次将m左移一位然后跟z做与操作,直到结果不为0.
//此时m应该等于1000,同z一样,第四位为1.
int m = 1;
while((z & m) == 0) m <<= 1;
//我们遍历数组,将每个数跟m进行与操作,结果为0的作为一组,结果不为0的作为一组
//目的是:将x, y分开到两个数组中,并且重复的数字还在同一个数组中,那么对两个子数组分别遍历异或得到的两个数字就是ans。
//例如对于数组:[1,2,10,4,1,4,3,3],我们把每个数字跟1000做与操作,可以分为下面两组:
//nums1存放结果为0的: [1, 2, 4, 1, 4, 3, 3]
//nums2存放结果不为0的: [10] (碰巧nums2中只有一个10,如果原数组中的数字再大一些就不会这样了)
//此时我们发现问题已经退化为数组中有一个数字只出现了一次
//分别对nums1和nums2遍历异或就能得到我们预期的x和y
vector<int>ans;
int x = 0, y = 0;
for(int i : nums) {
//这里我们是通过if...else将nums分为了两组,一边遍历一遍异或。
//跟我们创建俩数组nums1和nums2原理是一样的。
if((i & m) == 0) x ^= i;
else y ^= i;
}
ans.push_back(x);
ans.push_back(y);
return ans;
}
};
53、56 - II 数组中数字出现的次数 II
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res = 0;
for (int i = 0, sub = 0; i < 32; ++i, sub = 0) {
for (auto &n : nums) sub += ((n >> i) & 1);
if (sub % 3) res |= (1 << i);
}
return res;
}
};
作者:fengziL
链接:https://leetcode.cn/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-ii-lcof/solution/3chong-jie-fa-ji-jian-cdai-ma-by-fengzil-fmlr/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
54、39 数组中出现次数超过一半的数字
方法一:哈希表
复杂度:O(n)
class Solution {
public:
int majorityElement(vector<int>& nums) {
unordered_map<int, int> map;
int size = nums.size();
for(auto num : nums){
map[num]++;
if(map[num] > size /2) return num;
}
return 0;
}
};
方法二:排序
复杂度:O(nlogn)
class Solution {
public:
int majorityElement(vector<int>& nums) {
sort(nums.begin(), nums.end());
return nums[nums.size()/2];
}
};
方法三:Boyer-Moore 投票算法
复杂度:O(n)
维护一个candidate 和它出现的次数 count。初始时 candidate 可以为任意值,count 为 0。
如果 num 与 candidate 相等,那么count++;如果 num 与 candidate 不等,cnt–,cnt不能为负,否则更换candidate。
在遍历完成后,candidate 即为整个数组的众数。
class Solution {
public:
int majorityElement(vector<int>& nums) {
int candidate = -1;
int cnt = 0;
for(auto num : nums){
if(num == candidate)
cnt++;
else if(--cnt < 0){
candidate = num;
cnt = 1;
}
}
return candidate;
}
};
55、66 构建乘积数组
方法一:
建立left和right两个数组,left[i]表示a[i]左侧元素的乘积,right[i]为a[i]右侧元素的乘积。
class Solution {
public:
vector<int> constructArr(vector<int>& a) {
int size = a.size();
vector<int> left(size), right(size), ans;
if(!size) return ans;
left[0] = 1; right[size-1] = 1;
for(int i = 1; i < size; i++){
left[i] = left[i-1] * a[i-1];
}
for(int i = size-2; i > -1; i--){
right[i] = right[i+1] * a[i+1];
}
for(int i = 0; i < size; i++){
ans.push_back(left[i] * right[i]);
}
return ans;
}
};
方法二:
节省空间写法
class Solution {
public:
vector<int> constructArr(vector<int>& a) {
int size = a.size();
vector<int> left(size), ans(size);
if(!size) return ans;
left[0] = 1;
for(int i = 1; i < size; i++){
left[i] = left[i-1] * a[i-1];
}
int right = 1;
for(int i = size-1; i > -1; i--){
ans[i] = right * left[i];
right = right * a[i];
}
return ans;
}
};
56、57 - II 和为s的连续正数序列
双指针滑动窗口。计算 [i,j) 内的和为sum,左闭右开。
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target) {
int i = 1, j = 1, sum = 0;
vector<vector<int>> ans;
while(i <= target / 2){
if(sum < target){
sum += j;
j++;
}else if(sum > target){
sum -= i;
i++;
}else{
vector<int> tmp;
for(int k = i; k < j; k++){
tmp.push_back(k);
}
ans.push_back(tmp);
sum -= i;
i++;
}
}
return ans;
}
};
57、62 圆圈中最后剩下的数字
递归
class Solution {
int f(int n, int m) {
if (n == 1) {
return 0;
}
int x = f(n - 1, m);
return (m + x) % n;
}
public:
int lastRemaining(int n, int m) {
return f(n, m);
}
};
迭代
class Solution {
public:
int lastRemaining(int n, int m) {
int ans = 0;
for(int i = 2; i < n+1; i++){
ans = (ans + m) % i;
}
return ans;
}
};
58、14- I 剪绳子
方法一:递归
class Solution {
public:
int cuttingRope(int n) {
int ans = 0;
for(int i = 1; i < n; i++){
ans = max(ans,max( i*(n-i), i*cuttingRope(n-i)));
}
return ans;
}
};
方法二:动态规划
将 i 剪成 j 和 i-j 长度的绳子:且 i−j 不再继续剪,此时的乘积是 j×(i−j) ;且 i−j 继续剪成多段长度的绳子,此时的乘积是 j×dp[i−j] 。
因此,当 j 固定时,有 dp[i]=max(j×(i−j),j×dp[i−j])。j 的取值范围是 1 到 i ,遍历所有的 j 得到dp[i]。
class Solution {
public:
int cuttingRope(int n) {
vector<int> dp(n+1);
dp[0] = dp[1] = 0;
for(int i = 2; i <= n; i++){
for(int j = 1; j < i; j++){
dp[i] = max(dp[i],max( j*(i-j), j*dp[i-j]));
}
}
return dp[n];
}
};
方法三:贪心
一定是2和3的划分
class Solution {
public:
int cuttingRope(int n) {
if(n <= 3)
return n-1;
vector<int> dp(n+1);
dp[1]=1; dp[2]=2; dp[3]=3;
for(int i = 4; i <= n; i++){
dp[i] = max( 2*dp[i-2], 3*dp[i-3] );
}
return dp[n];
}
};
class Solution {
public:
int cuttingRope(int n) {
if (n <= 3)
return n - 1;
int a = n / 3;
int b = n % 3;
if (b == 0)
return pow(3, a);
else if (b == 1)
return pow(3, a -1) * 4;
else
return pow(3, a) * 2;
}
};
59、29 顺时针打印矩阵
方法一:
把一圈分为四段,连续输出,注意终止条件。
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if(matrix.empty()) return {};
vector<int> ans;
int left = 0;
int right = matrix[0].size() - 1;
int top = 0;
int bottom = matrix.size() - 1;
while(true){
for(int i = left; i <= right; i++) ans.push_back(matrix[top][i]);
top++;
if(top > bottom) break;
for(int i = top; i <= bottom; i++) ans.push_back(matrix[i][right]);
right--;
if(right < left) break;
for(int i = right; i >= left; i--) ans.push_back(matrix[bottom][i]);
bottom--;
if(bottom < top) break;
for(int i = bottom; i >= top; i--) ans.push_back(matrix[i][left]);
left++;
if(left > right) break;
}
return ans;
}
};
方法二:
逐层。
判断left < right && top < bottom是为了防止最终只剩下一行或一列的时候重复遍历添加。
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if(matrix.empty()) return {};
vector<int> ans;
int left = 0;
int right = matrix[0].size() - 1;
int top = 0;
int bottom = matrix.size() - 1;
while(left <= right && bottom >= top){
for(int i = left; i <= right; i++) ans.push_back(matrix[top][i]);
for(int i = top+1; i <= bottom; i++) ans.push_back(matrix[i][right]);
if(left < right && bottom > top){
for(int i = right-1; i > left; i--) ans.push_back(matrix[bottom][i]);
for(int i = bottom; i > top; i--) ans.push_back(matrix[i][left]);
}
left++;
right--;
top++;
bottom--;
}
return ans;
}
};
60、31 栈的压入、弹出序列
栈模拟
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
stack<int> st;
int size = pushed.size();
int index = 0;
for(int i = 0; i < size; i++){
st.push(pushed[i]);
while(!st.empty() && st.top() == popped[index]){
index++;
st.pop();
}
}
return st.empty();
}
};
61、20 表示数值的字符串
方法一:模拟
class Solution {
public:
bool scanInteger(const string s, int& index){
if(s[index] == '+' || s[index] == '-')
++index;
return scanUnsignedInteger(s, index);
}
bool scanUnsignedInteger(const string s, int& index){
int befor = index;
while(index != s.size() && s[index] >= '0' && s[index] <= '9')
index ++;
return index > befor;
}
bool isNumber(string s) {
if(s.size() == 0) return false;
int index = 0;
//字符串开始有空格,可以返回true
while(s[index] == ' ')
++index;
bool numeric = scanInteger(s, index);
// 如果出现'.',接下来是数字的小数部分
if(s[index] == '.'){
++index;
// 下面一行代码用||的原因:
// 1. 小数可以没有整数部分,例如.123等于0.123;
// 2. 小数点后面可以没有数字,例如233.等于233.0;
// 3. 当然小数点前面和后面可以有数字,例如233.666
numeric = scanUnsignedInteger(s, index) || numeric;
}
// 如果出现'e'或者'E',接下来跟着的是数字的指数部分
if(s[index] == 'e' || s[index] == 'E'){
++index;
// 下面一行代码用&&的原因:
// 1. 当e或E前面没有数字时,整个字符串不能表示数字,例如.e1、e1;
// 2. 当e或E后面没有整数时,整个字符串不能表示数字,例如12e、12e+5.4
numeric = numeric && scanInteger(s ,index);
}
//字符串结尾有空格,可以返回true
while(s[index] == ' ')
++index;
return numeric && index == s.size();
}
};
方法二:有限状态自动机
class Solution {
public:
typedef pair<char,int> charint;
typedef unordered_map<char,int> unmap;
bool isNumber(string s) {
vector<unmap> states = {
unmap{charint(' ',0),charint('s',1),charint('d',2),charint('.',4)},
unmap{charint('d',2),charint('.',4)},
unmap{charint('d',2),charint('.',3),charint('e',5),charint(' ',8)},
unmap{charint('d',3),charint('e',5),charint(' ',8)},
unmap{charint('d',3)},
unmap{charint('s',6),charint('d',7)},
unmap{charint('d',7)},
unmap{charint('d',7),charint(' ',8)},
unmap{charint(' ',8)}
};
int p = 0;
char t;
for(char c:s){
if(c >= '0' && c <= '9')
t = 'd';
else if(c == '+' || c == '-')
t = 's';
else if(c == 'e' || c == 'E')
t = 'e';
else if(c == '.' || c == ' ')
t = c;
else
t = '?';
// 若字符类型t不在哈希表states[p] 中,说明无法转移至下一状态,因此直接返回 False
if(!states[p].count(t))
return false;
// 状态p转移至states[p][t]
p = (int) states[p][t];
}
return p == 2 || p == 3 || p == 7 || p == 8;
}
};
62、67 把字符串转换成整数
class Solution {
public:
int strToInt(string str) {
if(str.size() == 0) return 0;
// flag记录正负,val为结果的绝对值
int index = 0, flag = 1, val = 0;
// 跳过空格
while(str[index] == ' ') index++;
// 记录符号后,index指向符号的后一位
if(str[index] == '-') {
flag = -1;
index++;
}else if(str[index] == '+'){
index++;
}
int threshold = INT_MAX / 10;
while(str[index] >= '0' && str[index] <= '9'){
//当前位的值
int num = str[index] - '0';
if(val > threshold || (val == threshold && num > 7))
return flag == 1 ? INT_MAX : INT_MIN;
val = val * 10 + num;
index++;
}
return flag * val;
}
};
63、59 - I 滑动窗口的最大值
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> ans;
if(nums.size() == 0 || k < 1) return ans;
deque<int> q;
//未形成长度为k的窗口到刚刚形成长度为k的窗口
for(int i = 0; i < k; i++){
while(!q.empty() && nums[i] > q.back())
q.pop_back();
q.push_back(nums[i]);
}
ans.push_back(q.front());
//剩下的部分:窗口的长度一直为k
//保持q的队首元素为当前窗口最大值
for(int i = k; i < nums.size(); i++){
if(nums[i-k] == q.front()) q.pop_front();
while(!q.empty() && nums[i] > q.back()) q.pop_back();
q.push_back(nums[i]);
ans.push_back(q.front());
}
return ans;
}
};
64、59 - II 队列的最大值
和上一题比较像
class MaxQueue {
public:
queue<int> q;
deque<int> dq;
MaxQueue() {
}
int max_value() {
return dq.empty() ? -1 : dq.front();
}
void push_back(int value) {
q.push(value);
while(!dq.empty() && value > dq.back()) dq.pop_back();
dq.push_back(value);
}
int pop_front() {
if(q.empty()) return -1;
int tmp = q.front();
q.pop();
if(!dq.empty() && tmp == dq.front()) dq.pop_front();
return tmp;
}
};
65、38 字符串的排列
方法一:回溯算法核心框架
for 选择 in 选择列表:
# 做选择
将该选择从选择列表移除
路径.add(选择)
backtrack(路径, 选择列表)
# 撤销选择
路径.remove(选择)
将该选择再加入选择列表
https://mp.weixin.qq.com/s/nMUHqvwzG2LmWA9jMIHwQQ
方法二:
class Solution {
public:
vector<string> permutation(string s) {
dfs(s, 0);
return ans;
}
vector<string> ans;
void dfs(string s, int i){
if(i == s.size()-1){
ans.push_back(s);
return;
}
set<int> st;
for(int j = i; j < s.size(); j++){
if(st.find(s[j]) != st.end()) continue;
st.insert(s[j]);
swap(s[i], s[j]);
dfs(s, i+1);
swap(s[i], s[j]);
}
}
};
66、37 序列化二叉树
class Codec {
public:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
if(!root) return "";
queue<TreeNode*> q;
q.push(root);
string ans;
while(!q.empty()){
TreeNode* tmp = q.front();
q.pop();
if(tmp == nullptr){
ans.append("null");
ans += ',';
}else{
ans.append(to_string(tmp->val));
ans += ',';
q.push(tmp->left);
q.push(tmp->right);
}
}
return ans;
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
if(data.size() == 0) return nullptr;
vector<string> s = split(data);
queue<TreeNode*> q;
TreeNode* root = new TreeNode(stoi(s[0]));
q.push(root);
int i = 1;
while(!q.empty()){
TreeNode* tmp = q.front();
q.pop();
if(s[i]!="null"){
tmp->left = new TreeNode(stoi(s[i]));
q.push(tmp->left);
}
i++;
if(s[i]!="null"){
tmp->right = new TreeNode(stoi(s[i]));
q.push(tmp->right);
}
i++;
}
return root;
}
vector<string> split(string& s) {
vector<string> res;
int n = s.size();
int i = 0;
while (i < n) {
int j = i + 1;
while (j < n && s[j] != ',') ++j;
res.push_back(s.substr(i, j - i));
i = j + 1;
}
return res;
}
};
67、19 正则表达式匹配
class Solution {
public:
bool isMatch(string s, string p) {
int m = s.size();
int n = p.size();
vector<vector<bool>> f(m+1, vector<bool>(n+1, false));
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++) {
if(j==0)
f[i][j] = i==0;
else{
if(p[j-1] != '*'){
if (i > 0 && (s[i-1] == p[j-1] || p[j-1] == '.'))
f[i][j] = f[i - 1][j - 1];
}else{
if (j >= 2)
f[i][j] = f[i][j] || f[i][j - 2];
if (i >= 1 && j >= 2 && (s[i - 1] == p[j - 2] || p[j - 2] == '.'))
f[i][j] = f[i][j] || f[i - 1][j];
}
}
}
}
return f[m][n];
}
};
68、49 丑数
class Solution {
public:
int nthUglyNumber(int n) {
int dp[n];
int a=0, b=0, c=0;
dp[0] = 1;
for(int i = 1; i < n; i++){
int n2 = dp[a] * 2;
int n3 = dp[b] * 3;
int n5 = dp[c] * 5;
dp[i] = min(n2, min(n3, n5));
if(dp[i] == n2)
a++;
if(dp[i] == n3)
b++;
if(dp[i] == n5)
c++;
}
return dp[n-1];
}
};
69、60 n个骰子的点数
方法一:逆向(通用)
class Solution {
public:
vector<double> dicesProbability(int n) {
vector<double> ans(n*5 + 1, 0);
// 二维dp数组,dp[i][j]:i表示几个骰子,j表示数字之和,dp[i][j]表示概率
vector<vector<double>> dp(n+1,vector<double>(n*6+1,0));
for(int i = 1; i <= 6; i++){
dp[1][i] = 1.0 / 6.0;
}
for(int i = 2;i <= n;i++){ // 骰子个数
for(int j = i;j <= i*6;j++){ // 每个数字的概率
for(int k = 1;k <= 6;k++){ // 第i个骰子取值的概率
if(j-k>0)
// 递推公式为:n个骰子某个数字之和sum的概率 = n-1个骰子中(sum - 1~6)数字之和的概率之和除以6
dp[i][j] += dp[i-1][j-k]/6;
else
break;
}
}
}
for(int i = 0;i <= 5*n;i++){
ans[i] = dp[n][n+i];
}
return ans;
}
};
方法二:正向
class Solution {
public:
vector<double> dicesProbability(int n) {
vector<double> dp(6, 1.0 / 6.0);
for (int i = 2; i <= n; i++) {
vector<double> tmp(5 * i + 1, 0);
for (int j = 0; j < dp.size(); j++) {
for (int k = 0; k < 6; k++) {
tmp[j + k] += dp[j] / 6.0;
}
}
dp = tmp;
}
return dp;
}
};
70、17 打印从1到最大的n位数
适用于大数的解法。将数以字符串的形式存储。
class Solution {
public:
vector<int> ans;
string s;
vector<int> printNumbers(int n) {
s.resize(n, '0');
dfs(n, 0);
return ans;
}
void dfs(int end, int index){
if(index == end){
save();
return;
}
for(int i = 0; i <= 9; i++){
s[index] = i + '0';
dfs(end, index+1);
}
}
void save(){
int ptr = 0;
while(ptr < s.size() && s[ptr] == '0') ptr++;
if (ptr != s.size())
ans.emplace_back(stoi(s.substr(ptr)));
}
};
71、51 数组中的逆序对
归并排序与逆序对是息息相关的。
class Solution {
public:
int reversePairs(vector<int>& nums) {
vector<int> tmp(nums.size());
return mergeSort(0, nums.size() - 1, nums, tmp);
}
int mergeSort(int l, int r, vector<int>& nums, vector<int>& tmp) {
// 终止条件
if (l >= r) return 0;
// 递归划分
int m = (l + r) / 2;
int res = mergeSort(l, m, nums, tmp) + mergeSort(m + 1, r, nums, tmp);
// 合并阶段
int i = l, j = m + 1;
for (int k = l; k <= r; k++)
tmp[k] = nums[k];
for (int k = l; k <= r; k++) {
if (i == m + 1)
nums[k] = tmp[j++];
else if (j == r + 1 || tmp[i] <= tmp[j])
nums[k] = tmp[i++];
else {
nums[k] = tmp[j++];
res += m - i + 1; // 统计逆序对
}
}
return res;
}
};
72、14- II 剪绳子 II
方法一:
本题和58题一样,只是多了个取余。下面第一个代码框过不了,改成第二个的形式。问题在于pow()会越界,将其写成while的形式一步一步乘上去。
class Solution {
public:
int cuttingRope(int n) {
if (n <= 3) return n - 1;
int a = n / 3;
int b = n % 3;
int index = 1000000007;
int ans;
if (b == 0)
ans = (int)pow(3, a) % index;
else if (b == 1)
ans = (int)pow(3, a -1) * 4 % index;
else
ans = (int)pow(3, a) * 2 % index;
return ans;
}
};
class Solution {
public:
int cuttingRope(int n) {
if (n <= 3) return n - 1;
long ret = 1;
if (n % 3 == 1){
ret = 4;
n = n - 4;
}
if (n % 3 == 2){
ret = 2;
n = n - 2;
}
while (n) {
ret = ret * 3 % 1000000007;
n = n - 3;
}
return (int)ret;
}
};
作者:master_xue
链接:https://leetcode.cn/problems/jian-sheng-zi-ii-lcof/solution/wu-xu-fu-za-shu-xue-jiang-jie-dong-tai-g-j470/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
方法二:
class Solution {
public:
int cuttingRope(int n) {
if (n <= 3) return n - 1;
long long max = 1, mod = 1000000007;
while(n>4){
n -= 3;
max = max * 3 % mod;
}
max = max * n % mod;
return max;
}
};
73、43 1~n 整数中 1 出现的次数
https://leetcode.cn/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof/solution/1n-zheng-shu-zhong-1-chu-xian-de-ci-shu-umaj8/
class Solution {
public:
int countDigitOne(int n) {
// mulk 表示 10^k
long long mulk = 1;
int ans = 0;
for (int k = 0; n >= mulk; ++k) {
ans += n / (mulk * 10) * mulk;
if(n % (mulk * 10) >= 2*mulk)
ans += mulk;
else if(n % (mulk * 10) >= mulk)
ans += n % (mulk * 10) - mulk +1;
mulk *= 10;
}
return ans;
}
};
74、44 数字序列中某一位的数字
/* 数字范围 数量 位数 占多少位
1-9 9 1 9
10-99 90 2 180
100-999 900 3 2700
1000-9999 9000 4 36000 ...
例如 2901 = 9 + 180 + 2700 + 12 即一定是4位数,第12位 n = 12;
数据为 = 1000 + (n - 1) / 4 = 1000 + 2 = 1002
定位1002中的位置 = (n - 1) % 4 = 3
s[3] - '0' = 2;
*/
class Solution {
public:
int findNthDigit(int n) {
int flag = 1; // 目标是几位数
long start = 1; // flag位数 的第一个数是多少
long count = 9; // flag位数 共占多少位
while (n > count) {
n -= count;
flag++;
start *= 10;
count = start * 9 * flag;
}
long num = start + (n - 1) / flag;
return to_string(num)[(n - 1) % flag] - '0';
}
};
Hot 100
75、1. 两数之和
哈希表一次遍历
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> ans;
unordered_map<int, int> map;
for(int i = 0; i < nums.size(); i++){
if(map.find(target - nums[i]) != map.end()){
return {i, map[target-nums[i]]};
}else{
map[nums[i]] = i;
}
}
return {};
}
};
76、2. 两数相加
方法一:模拟
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
int carry = 0;
ListNode *head = nullptr, *tail = nullptr;
while(l1 || l2){
int n1 = l1 ? l1->val : 0 ;
int n2 = l2 ? l2->val : 0 ;
int sum = n1 + n2 + carry;
if(!head){
head = tail = new ListNode(sum % 10);
}else{
tail->next = new ListNode(sum % 10);
tail = tail->next;
}
carry = sum / 10;
if(l1) l1 = l1->next;
if(l2) l2 = l2->next;
}
if(carry > 0){
tail->next = new ListNode(carry);
}
return head;
}
};
方法二:递归
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
return helper(l1, l2, 0);
}
ListNode* helper(ListNode* l1, ListNode* l2, int carry){
if(!l1 && !l2 && carry==0) return nullptr;
int sum = carry;
if(!l1){
sum += l1->val;
l1 = l1->next;
}
if(!l2){
sum += l2->val;
l2 = l2->next;
}
ListNode* node = new ListNode(sum % 10);
node->next = helper(l1, l2, sum / 10);
return node;
}
};
77、3. 无重复字符的最长子串
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_set<char> set;
int l = 0, r = 0, ans = 0, size = s.size();
while(r < size){
if(set.find(s[r]) == set.end()){
set.insert(s[r++]);
}else{
set.erase(s[l++]);
}
ans = max(ans, r-l);
}
return ans;
}
};
78、4. 寻找两个正序数组的中位数
生不出人,我很抱歉。
class Solution {
public:
int getKthElement(const vector<int>& nums1, const vector<int>& nums2, int k) {
/* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
* 这里的 "/" 表示整除
* nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
* nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
* 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
* 这样 pivot 本身最大也只能是第 k-1 小的元素
* 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
* 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
* 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
*/
int m = nums1.size();
int n = nums2.size();
int index1 = 0, index2 = 0;
while (true) {
// 边界情况
if (index1 == m) {
return nums2[index2 + k - 1];
}
if (index2 == n) {
return nums1[index1 + k - 1];
}
if (k == 1) {
return min(nums1[index1], nums2[index2]);
}
// 正常情况
int newIndex1 = min(index1 + k / 2 - 1, m - 1);
int newIndex2 = min(index2 + k / 2 - 1, n - 1);
int pivot1 = nums1[newIndex1];
int pivot2 = nums2[newIndex2];
if (pivot1 <= pivot2) {
k -= newIndex1 - index1 + 1;
index1 = newIndex1 + 1;
}
else {
k -= newIndex2 - index2 + 1;
index2 = newIndex2 + 1;
}
}
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int totalLength = nums1.size() + nums2.size();
if (totalLength % 2 == 1) {
return getKthElement(nums1, nums2, (totalLength + 1) / 2);
}
else {
return (getKthElement(nums1, nums2, totalLength / 2) + getKthElement(nums1, nums2, totalLength / 2 + 1)) / 2.0;
}
}
};
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/median-of-two-sorted-arrays/solution/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-s-114/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
79、5. 最长回文子串
动态规划
class Solution {
public:
string longestPalindrome(string s) {
int size = s.size();
if(size < 2) return s;
int maxlength = 1, begin = 0;
vector<vector<int>> dp(size, vector<int>(size));
for(int i = 0; i < size; i++){
dp[i][i] = true;
}
for(int length = 2; length <= size; length++){
for(int left = 0; left < size; left++){
int right = left + length -1;
if(right >= size)
break;
if(s[left]!=s[right]){
dp[left][right] = false;
}else{
if(right - left < 3){
dp[left][right] = true;
}else{
dp[left][right] = dp[left+1][right-1];
}
}
if(dp[left][right] == true && length > maxlength){
maxlength = length;
begin = left;
}
}
}
return s.substr(begin, maxlength);
}
};
80、6. N 字形变换
class Solution {
public:
string convert(string s, int numRows) {
int size = s.size();
if(numRows == 1 || numRows >= size)
return s;
vector<string> v(numRows);
for(int i = 0, x = 0, t = 2*(numRows-1); i < size; i++){
v[x] += s[i];
numRows - 1 > i % t ? ++x : --x;
}
string ans;
for(auto row : v){
ans += row;
}
return ans;
}
};
更节省内存开销:
class Solution {
public:
string convert(string s, int numRows) {
int size = s.size();
if(numRows == 1 || numRows >= size)
return s;
string ans;
int t = 2*(numRows-1);
for(int i = 0; i < numRows; ++i){
for(int j = 0; i + j < size; j += t){
ans += s[i+j];
if(i>0 && i < numRows - 1 && j + t - i < size)
ans += s[j+t-i];
}
}
return ans;
}
};
81、7. 整数反转
数学推导较复杂,主要难在边界的确定
class Solution {
public:
int reverse(int x) {
int rev = 0;
while (x != 0) {
if (rev < INT_MIN / 10 || rev > INT_MAX / 10) {
return 0;
}
int digit = x % 10;
x /= 10;
rev = rev * 10 + digit;
}
return rev;
}
};
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/reverse-integer/solution/zheng-shu-fan-zhuan-by-leetcode-solution-bccn/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
82、8. 字符串转换整数 (atoi)
方法一:有限状态自动机
class Automaton {
string state = "start";
unordered_map<string, vector<string>> table = {
{"start", {"start", "signed", "in_number", "end"}},
{"signed", {"end", "end", "in_number", "end"}},
{"in_number", {"end", "end", "in_number", "end"}},
{"end", {"end", "end", "end", "end"}}
};
int get_col(char c) {
if (isspace(c)) return 0;
if (c == '+' or c == '-') return 1;
if (isdigit(c)) return 2;
return 3;
}
public:
int sign = 1;
long long ans = 0;
void get(char c) {
state = table[state][get_col(c)];
if (state == "in_number") {
ans = ans * 10 + c - '0';
ans = sign == 1 ? min(ans, (long long)INT_MAX) : min(ans, -(long long)INT_MIN);
}
else if (state == "signed")
sign = c == '+' ? 1 : -1;
}
};
class Solution {
public:
int myAtoi(string str) {
Automaton automaton;
for (char c : str)
automaton.get(c);
return automaton.sign * automaton.ans;
}
};
方法二:模拟
class Solution {
public:
int myAtoi(string str) {
int sign = 1, res = 0, size = str.size(), i = 0;
while(i < size && str[i] == ' '){
i++;
}
int start = i;
for( ; i < size ; i++){
char c = str[i];
if(i==start && c=='+')
sign = 1;
else if(i==start && c=='-')
sign = -1;
else if(isdigit(c)){
int digit = c - '0';
if(res > INT_MAX/10 || (res == INT_MAX/10 && digit > INT_MAX%10)){
return INT_MAX;
}
if(res < INT_MIN/10 || (res == INT_MIN/10 && -digit < INT_MIN%10)){
return INT_MIN;
}
res = res*10 + sign*digit;
}else{
break;
}
}
return res;
}
};
83、11. 盛最多水的容器
class Solution {
public:
int maxArea(vector<int>& height) {
int l = 0, r = height.size()-1, ans = 0;
while(l < r){
ans = max(ans,
min(height[l], height[r]) * (r-l)
);
height[l] < height[r] ? l++ : r--;
}
return ans;
}
};
84、15. 三数之和
排序加双指针
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int n = nums.size();
if(n < 3) return {};
std::sort(nums.begin(), nums.end());
vector<vector<int>> ans;
for(int i = 0; i < n; i++){
if(nums[i] > 0) break;
if(i > 0 && nums[i] == nums[i-1]) continue;
int left = i+1, right = n-1, target = -nums[i];
while(left < right){
if(nums[left]+nums[right] > target) right--;
else if(nums[left]+nums[right] < target) left++;
else{
ans.push_back({nums[i], nums[left], nums[right]});
left++;
right--;
while(left<right && nums[left]==nums[left-1]) left++;
while(left<right && nums[right]==nums[right+1]) right--;
}
}
}
return ans;
}
};
85、17. 电话号码的字母组合
class Solution {
public:
string MAPPING[10] = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
vector<string> ans;
string tmp;
void dfs(int i, string digits){
if(i == digits.size()){
ans.push_back(tmp);
return;
}
int index = digits[i] - '0';
for(auto c : MAPPING[index]){
tmp.push_back(c);
dfs(i+1, digits);
tmp.pop_back();
}
}
vector<string> letterCombinations(string digits) {
if(digits.size() == 0) return {};
dfs(0, digits);
return ans;
}
};
86、19. 删除链表的倒数第 N 个结点
方法一:快慢指针
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dum = new ListNode(0);
dum->next = head;
ListNode *fast = head, *slow = dum;
for(int i = 0; i < n; i++){
fast = fast->next;
}
while(fast){
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
ListNode* ans = dum->next;
delete dum;
return ans;
}
};
方法二:
遍历两次
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dum = new ListNode(0,head);
ListNode *helper = dum, *pre = dum;
int length = 0;
while(head){
++length;
head = head->next;
}
for(int i = 1; i < length - n + 1; ++i){
pre = pre->next;
}
pre->next = pre->next->next;
ListNode* ans = dum->next;
delete dum;
return ans;
}
};
87、21. 合并两个有序链表
方法一:迭代
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* dum = new ListNode(-1);
ListNode* pre = dum;
while(list1 && list2){
if(list1->val < list2->val){
pre->next = list1;
pre = pre->next;
list1 = list1->next;
}else{
pre->next = list2;
pre = pre->next;
list2 = list2->next;
}
}
pre->next = list2 == nullptr ? list1 : list2;
return dum->next;
}
};
方法二:递归
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
if(!list1) return list2;
else if(!list2) return list1;
else if(list1->val < list2->val){
list1->next = mergeTwoLists(list1->next, list2);
return list1;
}
else{
list2->next = mergeTwoLists(list1, list2->next);
return list2;
}
}
};
88、22. 括号生成
class Solution {
public:
vector<string> ans;
string tmp;
vector<string> generateParenthesis(int n) {
if(n == 0) return {};
dfs(n, n);
return ans;
}
void dfs(int left, int right){
if(left == 0 && right == 0) {
ans.push_back(tmp);
return;
}
if(left > right) return;
if(left > 0){
tmp += '(';
dfs(left-1, right);
tmp.pop_back();
}
if(right > 0){
tmp += ')';
dfs(left, right-1);
tmp.pop_back();
}
}
};
89、23. 合并 K 个升序链表
优先队列
class Solution {
public:
struct compare{
bool operator()(ListNode * l1, ListNode *l2){
return l1->val > l2->val;
}
};
priority_queue<ListNode*, vector<ListNode*>, compare> q;
ListNode* mergeKLists(vector<ListNode*>& lists) {
for(auto node : lists){
if(node)
q.push(node);
}
auto dum = new ListNode(-1);
auto cur = dum;
while(!q.empty()){
cur->next = q.top();
cur = q.top();
q.pop();
if(cur->next) q.push(cur->next);
}
auto ans = dum->next;
delete dum;
return ans;
}
};
90、31. 下一个排列
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int i = nums.size()-2;
while(i>=0 && nums[i]>=nums[i+1]){
i--;
}
if(i>=0){
int j = nums.size()-1;
while(nums[i] >= nums[j]){
j--;
}
swap(nums[i], nums[j]);
}
reverse(nums.begin() + i + 1, nums.end());
}
};
class Solution {
public:
void nextPermutation(vector<int>& nums) {
next_permutation(nums.begin(),nums.end());
}
};
91、32. 最长有效括号
方法一:栈
class Solution {
public:
int longestValidParentheses(string s) {
stack<int> st;
int ans = 0;
st.push(-1);
for(int i = 0; i < s.size(); i++){
if(s[i]=='(')
st.push(i);
else{
st.pop();
if(st.empty())
st.push(i);
else
ans = max(ans, i - st.top());
}
}
return ans;
}
};
方法二:动态规划
状态转移方程太难写了
class Solution {
public:
int longestValidParentheses(string s) {
int maxans = 0, n = s.length();
vector<int> dp(n, 0);
for (int i = 1; i < n; i++) {
if (s[i] == ')') {
if (s[i - 1] == '(') {
dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
} else if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(') {
dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
}
maxans = max(maxans, dp[i]);
}
}
return maxans;
}
};
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/longest-valid-parentheses/solution/zui-chang-you-xiao-gua-hao-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
92、33. 搜索旋转排序数组
改进版的二分查找
class Solution {
public:
int search(vector<int>& nums, int target) {
if(nums.size()==0) return -1;
int n = nums.size()-1;
if(n == 0) return nums[0] == target ? 0 : -1;
int l = 0, r = n;
while(l <= r){
int mid = (l+r)/2;
if(nums[mid]==target) return mid;
if(nums[l] <= nums[mid]){
if(target < nums[mid] && target >= nums[l])
r = mid - 1;
else
l = mid + 1;
}else{
if(target > nums[mid] && target <= nums[r])
l = mid + 1;
else
r = mid - 1;
}
}
return -1;
}
};
93、34. 在排序数组中查找元素的第一个和最后一个位置
改进版的二分查找
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
return{search(nums, target, "searchLeft"), search(nums, target, "searchRight")};
}
int search(vector<int>& nums, int target, string flag){
int l = 0, r = nums.size()-1, res = -1;
while(l <= r){
int mid = (l + r) / 2;
if(target < nums[mid])
r = mid - 1;
else if(target > nums[mid])
l = mid + 1;
else{
res = mid;
if(flag == "searchLeft")
r = mid -1;
else
l = mid + 1;
}
}
return res;
}
};
94、39. 组合总和
回溯
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
if(candidates.size() == 0) return {};
dfs(0, target, candidates);
return ans;
}
void dfs(int i, int target, vector<int>& candidates){
if(target == 0){
ans.push_back(path);
return;
}
if(i == candidates.size())
return;
dfs(i+1, target, candidates);
if(candidates[i] <= target){
path.push_back(candidates[i]);
dfs(i, target-candidates[i], candidates);
path.pop_back();
}
}
};
95、42. 接雨水
方法一:按列计算
第i列的雨水 = min(左边最高墙的高度,右边最高墙的高度) - 第i列的墙高
class Solution {
public:
int trap(vector<int>& height) {
int ans = 0, n = height.size();
for(int i = 1; i < n-1; i++){
int l = 0, r = 0;
for(int j = i-1; j >= 0; j--){
l = max(l, height[j]);
}
for(int k = i+1; k <= n-1; k++){
r = max(r, height[k]);
}
if( min(l, r) - height[i] > 0)
ans += (min(l, r) - height[i]);
}
return ans;
}
};
方法二:
使用dp方法优化方法一
class Solution {
public:
int trap(vector<int>& height) {
int ans = 0, n = height.size();
vector<int> left(n), right(n);
left[0] = height[0];
for(int i = 1; i < n; ++i){
left[i] = max(left[i-1], height[i]);
}
right[n-1] = height[n-1];
for(int i = n-2; i >= 0; --i){
right[i] = max(right[i+1], height[i]);
}
for(int i = 1; i < n-1; i++){
if( min(left[i], right[i]) - height[i] > 0)
ans += (min(left[i], right[i]) - height[i]);
}
return ans;
}
};
方法三:
使用变量来节省方法二的空间开销
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
int ans = 0, l = 0, r = n-1;
int leftMax = 0, rightMax = 0;
while(l < r){
leftMax = max(leftMax, height[l]);
rightMax = max(rightMax, height[r]);
if(leftMax < rightMax){
ans += leftMax - height[l];
++l;
}else{
ans += rightMax - height[r];
--r;
}
}
return ans;
}
};
方法四:单调栈
class Solution {
public:
int trap(vector<int>& height) {
int ans = 0, n = height.size();
stack<int> st;
for(int i = 0; i < n; ++i){
while(!st.empty() && height[i] > height[st.top()]){
int top = st.top();
st.pop();
if(st.empty())
break;
int left = st.top();
int width = i - left - 1;
int heightt = min(height[left], height[i]) - height[top];
ans += width * heightt;
}
st.push(i);
}
return ans;
}
};
96、46. 全排列
回溯
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> permute(vector<int>& nums) {
if(nums.size()==0) return{};
dfs(0, nums);
return ans;
}
bool pathExist( vector<int> path, int tmp){
for(auto i : path){
if(i == tmp) return true;
}
return false;
}
void dfs(int i, vector<int>& nums){
if(i == nums.size()){
ans.push_back(path);
return;
}
for(auto tmp : nums){
if(!pathExist(path, tmp)){
path.push_back(tmp);
dfs(i+1, nums);
path.pop_back();
}
}
}
};
97、48. 旋转图像
先水平翻转,再沿主对角线翻转,可以得到顺时针旋转90度的矩阵。
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
// 水平翻转
for (int i = 0; i < n / 2; ++i) {
for (int j = 0; j < n; ++j) {
swap(matrix[i][j], matrix[n - i - 1][j]);
}
}
// 主对角线翻转
for (int i = 0; i < n; ++i) {
for (int j = 0; j < i; ++j) {
swap(matrix[i][j], matrix[j][i]);
}
}
}
};
98、49. 字母异位词分组
方法一:排序
具有相同字母的不同单词排序之后是相同的
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> map;
for(auto s : strs){
string tmp = s;
sort(tmp.begin(), tmp.end());
map[tmp].push_back(s);
}
vector<vector<string>> ans;
for(auto it = map.begin(); it != map.end(); it++){
ans.push_back(it->second);
}
return ans;
}
};
方法二:自建哈希函数
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string> &strs) {
/**
* 注意:C++中char为8位,也即最多可表示256个字符
* 题目已经说明0<=strs[i].length<=100,故使用char来统计各字符数量是没有问题的
* 当length大于256时,使用该方法会出现碰撞
*/
auto key = [&](const string &val) {
string k(26, 0);
for (const auto &c: val)k[c - 'a']++;
return k;
};
// 统计异位词
unordered_map<string, vector<string>> mp;
for (const auto &s: strs)
mp[key(s)].push_back(s);
// map转vector
vector<vector<string>> res;
for (const auto &it: mp)res.push_back(it.second);
return res;
}
};
作者:zi-jie-yao-ling-er-si
链接:https://leetcode.cn/problems/group-anagrams/solution/ha-xi-biao-by-zi-jie-yao-ling-er-si-7a9v/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
99、55. 跳跃游戏
class Solution {
public:
bool canJump(vector<int>& nums) {
int n = nums.size();
int rightMost = 0;
for(int i = 0; i < n; i++){
if(rightMost - i < 0)
return false;
else{
rightMost = max(rightMost, i + nums[i]);
if(rightMost >= nums.size()-1) return true;
}
}
return false;
}
};
100、56. 合并区间
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end());
vector<vector<int>> ans;
int i = 0;
while(i < intervals.size()){
int l = intervals[i][0];
int r = intervals[i][1];
while(i+1 < intervals.size() && intervals[i+1][0] <= r){
r = max(r, intervals[i+1][1]);
i++;
}
ans.push_back({l, r});
i++;
}
return ans;
}
};
101、62. 不同路径
方法一:dfs
class Solution {
public:
int uniquePaths(int m, int n) {
return dfs(m-1, n-1);
}
int dfs(int m, int n){
if(m == 0 || n == 0)
return 1;
return dfs(m-1, n) + dfs(m, n-1);
}
};
方法二:dp
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> f(m,vector<int>(n));
for(int i = 0; i < m; i++) f[i][0] = 1;
for(int j = 0; j < n; j++) f[0][j] = 1;
for(int i = 1; i < m; i++){
for(int j = 1; j < n; j++){
f[i][j] = f[i-1][j] + f[i][j-1];
}
}
return f[m-1][n-1];
}
};
102、64. 最小路径和
比较简单的动态规划,直接在原数组上更改节省内存开销。
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(i==0 && j==0) continue;
else if(i==0) grid[i][j] = grid[i][j-1] + grid[i][j];
else if(j==0) grid[i][j] = grid[i-1][j] + grid[i][j];
else grid[i][j] = min(grid[i][j-1], grid[i-1][j]) + grid[i][j];
}
}
return grid[m-1][n-1];
}
};
103、70. 爬楼梯
方法一:dp
class Solution {
public:
int climbStairs(int n) {
int p = 0, q = 0, r = 1;
for(int i = 0; i < n; ++i){
p = q;
q = r;
r = p+q;
}
return r;
}
};
方法二:矩阵快速幂
class Solution {
public:
vector<vector<long long>> Pow(vector<vector<long long>> a, int n){
vector<vector<long long>> res = {{1,0},{0,1}};
while(n > 0){
if((n & 1) == 1){
res = Multi(res, a);
}
n >>= 1;
a = Multi(a, a);
}
return res;
}
vector<vector<long long>> Multi(vector<vector<long long>> a, vector<vector<long long>> b){
vector<vector<long long>> c(2, vector<long long>(2));
for(int i = 0; i < 2; ++i){
for(int j = 0; j < 2; ++j){
c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j];
}
}
return c;
}
int climbStairs(int n) {
vector<vector<long long>> a = {{1,1},{1,0}};
vector<vector<long long>> ans = Pow(a, n);
return ans[0][0];
}
};
104、72. 编辑距离
class Solution {
public:
int minDistance(string word1, string word2) {
int m = word1.size(), n = word2.size();
vector<vector<int>> dp(m+1,vector<int>(n+1));
for(int i = 0; i < m+1; ++i){
dp[i][0] = i;
}
for(int j = 0; j < n+1; ++j){
dp[0][j] = j;
}
for(int i = 1; i < m+1; ++i){
for(int j = 1; j < n+1; ++j){
if(word1[i-1] == word2[j-1]) dp[i][j] = dp[i-1][j-1];
else dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
}
}
return dp[m][n];
}
};
节省内存开销:
class Solution {
public:
int minDistance(string word1, string word2) {
int m = word1.size(), n = word2.size();
vector<int> dp(n+1, 0);
for(int j = 0; j <= n; ++j){
dp[j] = j;
}
for(int i = 1; i <= m; ++i){
int tmp = dp[0], cur = 0;
dp[0]++;
for(int j = 1; j <= n; ++j){
if(word1[i-1] == word2[j-1]) cur = tmp;
else cur = min(min(dp[j-1], dp[j]), tmp) + 1;
tmp = dp[j];
dp[j] = cur;
}
}
return dp.back();
}
};
105、75. 颜色分类
方法一:单指针
class Solution {
public:
void sortColors(vector<int>& nums) {
int ptr = 0;
for (int i = 0; i < nums.size(); ++i) {
if (nums[i] == 0) {
swap(nums[i], nums[ptr]);
++ptr;
}
}
for(int j = ptr; j < nums.size(); ++j){
if(nums[j] == 1){
swap(nums[j], nums[ptr]);
++ptr;
}
}
}
};
方法二:双指针
下表[0, p]为0,[q, size()-1]为2,其余部分为1
class Solution {
public:
void sortColors(vector<int>& nums) {
int i = 0, p = -1, q = nums.size();
while(i < q){
if(nums[i] == 0){
++p;
swap(nums[p], nums[i]);
++i;
}else if(nums[i] == 2){
--q;
swap(nums[q], nums[i]);
}else{
++i;
}
}
}
};
106、76. 最小覆盖子串
class Solution {
public:
unordered_map<char, int> ori, cnt;
bool check(){
for(const auto &p : ori){
if(cnt[p.first] < p.second)
return false;
}
return true;
}
string minWindow(string s, string t) {
for(auto c : t)
++ori[c];
int l = 0, len = INT_MAX, ansL = -1;
for(int r = 0; r < s.size(); r++){
++cnt[s[r]];
while(check() && l <= r){
if(r-l+1 < len){
len = r-l+1;
ansL = l;
}
--cnt[s[l++]];
}
}
return ansL == -1 ? "" : s.substr(ansL,len);
}
};
107、78. 子集
子集型回溯
方法一:
class Solution {
public:
vector<vector<int>> ans;
vector<int> tmp;
vector<vector<int>> subsets(vector<int>& nums) {
if(nums.size() == 0) return {};
dfs(0,nums);
return ans;
}
void dfs(int i, vector<int>& nums){
if(i==nums.size()) {
ans.push_back(tmp);
return;
}
dfs(i+1, nums);
tmp.push_back(nums[i]);
dfs(i+1, nums);
tmp.pop_back();
}
};
方法二:
class Solution {
public:
vector<vector<int>> ans;
vector<int> tmp;
vector<vector<int>> subsets(vector<int>& nums) {
if(nums.size() == 0) return {};
dfs(0,nums);
return ans;
}
void dfs(int i, vector<int>& nums){
ans.push_back(tmp);
for(int j = i; j < nums.size(); ++j){
tmp.push_back(nums[j]);
dfs(j+1, nums);
tmp.pop_back();
}
}
};
108、79. 单词搜索
回溯 + dfs
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
for (int i = 0; i < board.size(); i++) {
for (int j = 0; j < board[0].size(); j++) {
vector<vector<bool>> visited(board.size(), vector<bool>(board[0].size(), false));
if (dfs(board, visited, word, 0, i, j)) return true;
}
}
return false;
}
bool dfs(vector<vector<char>>& board, vector<vector<bool>>& visited, string word, int str_idx, int i, int j){
if(str_idx==word.size()) return true;
if (i >= board.size() || i < 0 ||
j >= board[0].size() || j < 0 ||
visited[i][j] == true || board[i][j] != word[str_idx]) return false;
visited[i][j] = true;
if (dfs(board, visited, word, str_idx + 1, i + 1, j) ||
dfs(board, visited, word, str_idx + 1, i - 1, j) ||
dfs(board, visited, word, str_idx + 1, i, j + 1) ||
dfs(board, visited, word, str_idx + 1, i, j - 1)) return true;
visited[i][j] = false;
return false;
}
};
更节省内存开销
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
for (int i = 0; i < board.size(); i++) {
for (int j = 0; j < board[0].size(); j++) {
if (dfs(board, word, 0, i, j)) return true;
}
}
return false;
}
bool dfs(vector<vector<char>>& board, string& word, int str_idx, int i, int j){
if(str_idx==word.size()) return true;
if (i >= board.size() || i < 0 ||
j >= board[0].size() || j < 0 ||
board[i][j] == '#' || board[i][j] != word[str_idx]) return false;
char c = board[i][j];
board[i][j] = '#';
if (dfs(board, word, str_idx + 1, i + 1, j) ||
dfs(board, word, str_idx + 1, i - 1, j) ||
dfs(board, word, str_idx + 1, i, j + 1) ||
dfs(board, word, str_idx + 1, i, j - 1)) return true;
board[i][j] = c;
return false;
}
};
109、84. 柱状图中最大的矩形
方法一:单调栈
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int ans = 0, n = heights.size();
stack<int> st;
vector<int> left(n), right(n);
for(int i = 0; i < n; ++i){
while(!st.empty() && heights[i] <= heights[st.top()])
st.pop();
left[i] = st.empty() ? -1 : st.top();
st.push(i);
}
while(!st.empty()) st.pop();
for(int i = n-1; i >= 0 ; --i){
while(!st.empty() && heights[i] <= heights[st.top()])
st.pop();
right[i] = st.empty() ? n : st.top();
st.push(i);
}
for(int i = 0; i < n; ++i){
ans = max(ans, (right[i] - left[i] - 1) * heights[i]);
}
return ans;
}
};
方法二:优化
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int ans = 0, n = heights.size();
stack<int> st;
vector<int> left(n), right(n, n);
for(int i = 0; i < n; ++i){
while(!st.empty() && heights[i] <= heights[st.top()]){
right[st.top()] = i;
st.pop();
}
left[i] = st.empty() ? -1 : st.top();
st.push(i);
}
for(int i = 0; i < n; ++i){
ans = max(ans, (right[i] - left[i] - 1) * heights[i]);
}
return ans;
}
};
110、85. 最大矩形
方法一:暴力
class Solution {
public:
int maximalRectangle(vector<vector<char>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
vector<vector<int>> left(m, vector<int>(n, 0));
for(int i = 0; i < m; ++i){
for(int j = 0; j < n; ++j){
if(matrix[i][j] == '1')
left[i][j] = j==0 ? 1 : left[i][j-1]+1;
}
}
int ans = 0;
for(int i = 0; i < m; ++i){
for(int j = 0; j < n; ++j){
if(matrix[i][j] == '0')
continue;
int width = left[i][j];
int area = width;
for(int k = i-1; k >=0; --k){
width = min(width, left[k][j]);
area = max(area, width*(i-k+1));
}
ans = max(ans, area);
}
}
return ans;
}
};
方法二:单调栈
首先构造up矩阵,每一个元素代表这个位置向上的最大高度,可以转化为上一题
class Solution {
public:
int maximalRectangle(vector<vector<char>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
vector<vector<int>> up(m, vector<int>(n, 0));
for(int i = 0; i < m; ++i){
for(int j = 0; j < n; ++j){
if(matrix[i][j] == '1')
up[i][j] = i==0 ? 1 : up[i-1][j]+1;
}
}
int ans = 0;
for(int i = 0; i < m; ++i){
stack<int> st;
vector<int> left(n,0), right(n,0);
for(int j = 0; j < n; ++j){
while(!st.empty() && up[i][j] <= up[i][st.top()])
st.pop();
left[j] = st.empty() ? -1 : st.top();
st.push(j);
}
while(!st.empty()) st.pop();
for(int j = n-1; j >= 0 ; --j){
while(!st.empty() && up[i][j] <= up[i][st.top()])
st.pop();
right[j] = st.empty() ? n : st.top();
st.push(j);
}
for(int j = 0; j < n; ++j){
ans = max(ans, (right[j] - left[j] - 1) * up[i][j]);
}
}
return ans;
}
};
111、94. 二叉树的遍历
方法一:递归
以中序为例
class Solution {
public:
vector<int> ans;
vector<int> inorderTraversal(TreeNode* root) {
if(!root) return ans;
inorderTraversal(root->left);
ans.push_back(root->val);
inorderTraversal(root->right);
return ans;
}
};
方法二:迭代
前序
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> ans;
stack<TreeNode*> st;
while(!st.empty() || root){
while(root){
ans.push_back(root->val);
st.push(root);
root = root->left;
}
root = st.top();
st.pop();
root = root->right;
}
return ans;
}
};
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> ans;
if(!root) return ans;
stack<TreeNode*> st;
st.push(root);
while(!st.empty()){
TreeNode* cur = st.top();
ans.push_back(cur->val);
st.pop();
if(cur->right) st.push(cur->right);
if(cur->left) st.push(cur->left);
}
return ans;
}
};
中序
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ans;
stack<TreeNode*> st;
while(!st.empty() || root){
while(root){
st.push(root);
root = root->left;
}
root = st.top();
ans.push_back(root->val);
st.pop();
root = root->right;
}
return ans;
}
};
后序
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> ans;
stack<TreeNode*> st;
TreeNode* prev = nullptr;
while(root || !st.empty()){
while(root){
st.push(root);
root = root->left;
}
root = st.top();
st.pop();
if(!root->right || root->right == prev){
ans.push_back(root->val);
prev = root;
root = nullptr;
}else{
st.push(root);
root = root->right;
}
}
return ans;
}
};
方法三:Morris
前序
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> ans;
TreeNode* pre;
while(root){
if(root->left){
pre = root->left;
while(pre->right && pre->right != root){
pre = pre->right;
}
if(!pre->right){
ans.push_back(root->val);
pre->right = root;
root = root->left;
continue;
}else{
pre->right = nullptr;
}
}else{
ans.push_back(root->val);
}
root = root->right;
}
return ans;
}
};
中序
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ans;
TreeNode* pre = nullptr;
while(root){
if(root->left){
pre = root->left;
while(pre->right && pre->right != root){
pre = pre->right;
}
if(!pre->right){
pre->right = root;
root = root->left;
}else{
ans.push_back(root->val);
pre->right = nullptr;
root = root->right;
}
}else{
ans.push_back(root->val);
root = root->right;
}
}
return ans;
}
};
112、96. 不同的二叉搜索树
动态规划
class Solution {
public:
int numTrees(int n) {
vector<int> dp(n+1,0);
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i <= n; ++i) {
for (int j = 1; j <= i; ++j) {
dp[i] += dp[j - 1] * dp[i - j];
}
}
return dp[n];
}
};
n = 3
根节点=1,dp[n] += dp[2]
根节点=2,dp[n] += dp[1] * dp[1]
根节点=3,dp[n] += dp[2]
n = 5
根节点=1 时 ,其余数字都比1大,只能在右边 dp[n] += dp[4]
根节点=2 时,左边有一个1比2小,右边有三个比2大的数字 dp[n] += dp[1] * dp[3]
根节点=3 时,左边有两个数比3小,右边有两个数比3大的数字 dp[i] += dp[2] * dp[2]
根节点=4 时,左边有一个1比2小,右边有三个比2大的数字 dp[n] += dp[3] * dp[1]
根节点=5,左边有4个数字比5小,只能放在5的左边,dp[n] += dp[4]
113、98. 验证二叉搜索树
方法一:前序
这个递归函数传入某一结点以及它的value正确的取值范围。root节点随意取,因此为LONG_MIN, LONG_MAX之间。
class Solution {
public:
bool helper(TreeNode* root, long l, long r){
if(!root) return true;
long x = root->val;
return l < x && x < r && helper(root->left, l, x) && helper(root->right, x, r);
}
bool isValidBST(TreeNode* root) {
return helper(root, LONG_MIN, LONG_MAX);
}
};
方法二:中序
将迭代的中序遍历改一下,将二叉搜索树进行中序遍历,比较当前节点的值与上一个节点的值,并不断更新tmp。
class Solution {
public:
bool isValidBST(TreeNode* root) {
vector<int> ans;
stack<TreeNode*> st;
long tmp = LONG_MIN;
while(!st.empty() || root){
while(root){
st.push(root);
root = root->left;
}
root = st.top();
st.pop();
if(root->val <= tmp) return false;
tmp = root->val;
root = root->right;
}
return true;
}
};
递归写法
class Solution {
public:
long tmp = LONG_MIN;
bool isValidBST(TreeNode* root) {
if(!root) return true;
if(!isValidBST(root->left) || root->val <= tmp) return false;
tmp = root->val;
return isValidBST(root->right);
}
};
114、101. 对称二叉树
递归
class Solution {
public:
bool isSymmetric(TreeNode* root) {
return helper(root->left, root->right);
}
bool helper(TreeNode* l, TreeNode* r){
if(!l && !r)
return true;
else if(!l || !r)
return false;
else
return l->val == r->val && helper(l->left, r->right) && helper(l->right, r->left);
}
};
115、102. 二叉树的层序遍历
广度优先搜索
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
if(!root) return {};
vector<vector<int>> ans;
queue<TreeNode*> q;
q.push(root);
while(!q.empty()){
int len = q.size();
vector<int> path;
for(int i = 0; i < len; i++){
TreeNode* node = q.front();
q.pop();
path.push_back(node->val);
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
}
ans.push_back(path);
}
return ans;
}
};
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> levelOrder(TreeNode* root) {
bfs(root, 0);
return ans;
}
void bfs(TreeNode* root, int n){
if(!root) return;
while(ans.size() < n+1) ans.push_back(vector<int>{});
ans[n].push_back(root->val);
bfs(root->left, n+1);
bfs(root->right, n+1);
}
};
116、114. 二叉树展开为链表
class Solution {
public:
TreeNode* pre;
void flatten(TreeNode* root) {
if(!root) return;
TreeNode* l = root->left;
TreeNode* r = root->right;
if(pre == nullptr) pre = root;
else{
pre->right = root;
pre->left = nullptr;
pre = pre->right;
}
flatten(l);
flatten(r);
}
};
117、124. 二叉树中的最大路径和
class Solution {
public:
int ans = INT_MIN;
int maxPathSum(TreeNode* root) {
helper(root);
return ans;
}
int helper(TreeNode* node){
if(!node) return 0;
int l = max(helper(node->left), 0);
int r = max(helper(node->right), 0);
ans = max(ans, node->val + l + r);
return max(0, node->val + max(l, r));
}
};
118、128. 最长连续序列
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> set;
int ans = 0;
for(auto num : nums)
set.insert(num);
for(auto num : set){
if(!set.count(num-1)){
int n = 1, tmp = num+1;
while(set.count(tmp)){
++n;
++tmp;
}
ans = max(ans, n);
}else
continue;
}
return ans;
}
};
119、136. 只出现一次的数字
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ans = 0;
for(auto num : nums){
ans = ans ^ num;
}
return ans;
}
};
120、139. 单词拆分
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
vector<bool> dp(s.size()+1, false);
dp[0] = true;
unordered_set<string> set;
for(auto word : wordDict){
set.insert(word);
}
for(int i = 1; i <= s.size(); ++i){
for(int j = 0; j < i; ++j){
if(dp[j] && set.count(s.substr(j, i-j))){
dp[i] = true;
break;
}
}
}
return dp[s.size()];
}
};
记忆化
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
vector<bool> dp(s.size()+1, false);
dp[0] = true;
for(int i = 0; i < s.size(); ++i){
if(!dp[i]) continue;
for(auto word : wordDict){
int len = word.size();
if(i+len <= s.size() && s.substr(i, len) == word)
dp[i+len] = true;
}
}
return dp[s.size()];
}
};
121、141. 环形链表
class Solution {
public:
bool hasCycle(ListNode *head) {
if(!head || !head->next) return false;
ListNode *l = head, *r = head->next;
while(l != r){
if(!r || !r->next) return false;
l = l->next;
r = r->next->next;
}
return true;
}
};
class Solution {
public:
bool hasCycle(ListNode *head) {
if(!head || !head->next) return false;
ListNode *l = head, *r = head;
while(r && r->next){
l = l->next;
r = r->next->next;
if(l==r) return true;
}
return false;
}
};
122、142. 环形链表 II
两次快慢指针过程
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(!head || !head->next) return nullptr;
ListNode *l = head, *r = head;
while(1){
if(!r || !r->next) return nullptr;
l = l->next;
r = r->next->next;
if(l == r) break;
}
l = head;
while(l!=r){
l = l->next;
r = r->next;
}
return l;
}
};
123、146. LRU 缓存
哈希表+双向链表
class Node{
public:
int key, val;
Node *prev, *next;
Node(): key(0), val(0), prev(nullptr), next(nullptr) {}
Node(int _key, int _val): key(_key), val(_val), prev(nullptr), next(nullptr) {}
};
class LRUCache {
private:
int capacity, size;
Node *head, *tail;
unordered_map<int, Node*> map;
public:
LRUCache(int _capacity): capacity(_capacity), size(0) {
head = new Node();
tail = new Node();
head->next = tail;
tail->prev = head;
}
int get(int key) {
if(!map.count(key)) return -1;
Node *node = map[key];
moveToHead(node);
return node->val;
}
void put(int key, int value) {
if(!map.count(key)){
Node *node = new Node(key, value);
map[key] = node;
addToHead(node);
++size;
if(size > capacity){
Node* tmp = removeTail();
map.erase(tmp->key);
delete tmp;
--size;
}
}else{
Node *node = map[key];
node->val = value;
moveToHead(node);
}
}
void moveToHead(Node *node){
removeNode(node);
addToHead(node);
}
void addToHead(Node *node){
node->prev = head;
node->next = head->next;
head->next->prev = node;
head->next = node;
}
Node* removeTail(){
Node* node = tail->prev;
removeNode(node);
return node;
}
void removeNode(Node *node){
node->prev->next = node->next;
node->next->prev = node->prev;
}
};
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache* obj = new LRUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/
124、148. 排序链表
方法一:自顶向下归并排序
递归
时间复杂度:O(nlogn)
空间复杂度:O(logn)
class Solution {
public:
ListNode* sortList(ListNode* head) {
if(!head || !head->next) return head;
ListNode* middle = findMiddle(head);
ListNode* rightHead = middle->next;
middle->next = nullptr;
ListNode* left = sortList(head);
ListNode* right = sortList(rightHead);
return merge(left, right);
}
ListNode* findMiddle(ListNode* head){
if (!head || !head->next)
return head;
ListNode* slow = head, *fast = head->next->next;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
ListNode* merge(ListNode* head1, ListNode* head2){
ListNode* dum = new ListNode(0);
ListNode* tmp = dum;
while(head1 && head2){
if(head1->val <= head2->val){
tmp->next = head1;
head1 = head1->next;
}else{
tmp->next = head2;
head2 = head2->next;
}
tmp = tmp->next;
}
if(head1)
tmp->next = head1;
else if(head2)
tmp->next = head2;
return dum->next;
}
};
方法二:自底向上归并排序
时间复杂度:O(nlogn)
空间复杂度:O(1)
class Solution {
public:
ListNode* sortList(ListNode* head) {
if (head == nullptr) return head;
int length = 0;
ListNode* node = head;
while (node != nullptr) {
length++;
node = node->next;
}
ListNode* dummyHead = new ListNode(0, head);
for (int subLength = 1; subLength < length; subLength <<= 1) {
ListNode* prev = dummyHead, *curr = dummyHead->next;
while (curr != nullptr) {
ListNode* head1 = curr;
for (int i = 1; i < subLength && curr->next != nullptr; i++) {
curr = curr->next;
}
ListNode* head2 = curr->next;
curr->next = nullptr;
curr = head2;
for (int i = 1; i < subLength && curr != nullptr && curr->next != nullptr; i++) {
curr = curr->next;
}
ListNode* next = nullptr;
if (curr != nullptr) {
next = curr->next;
curr->next = nullptr;
}
ListNode* merged = merge(head1, head2);
prev->next = merged;
while (prev->next != nullptr) {
prev = prev->next;
}
curr = next;
}
}
return dummyHead->next;
}
ListNode* merge(ListNode* head1, ListNode* head2) {
ListNode* dummyHead = new ListNode(0);
ListNode* temp = dummyHead, *temp1 = head1, *temp2 = head2;
while (temp1 != nullptr && temp2 != nullptr) {
if (temp1->val <= temp2->val) {
temp->next = temp1;
temp1 = temp1->next;
} else {
temp->next = temp2;
temp2 = temp2->next;
}
temp = temp->next;
}
if (temp1 != nullptr) {
temp->next = temp1;
} else if (temp2 != nullptr) {
temp->next = temp2;
}
return dummyHead->next;
}
};
125、152. 乘积最大子数组
class Solution {
public:
int maxProduct(vector<int>& nums) {
int max = nums[0], min = nums[0], ans = nums[0];
for(int i = 1; i < nums.size(); ++i){
int tmpMax = max, tmpMin = min;
max = findMax(nums[i], nums[i]*tmpMax, nums[i]*tmpMin);
min = findMin(nums[i], nums[i]*tmpMax, nums[i]*tmpMin);
ans = findMax(max, ans);
}
return ans;
}
int findMax(int a, int b, int c){
return max(a, max(b, c));
}
int findMax(int a, int b){
return max(a, b);
}
int findMin(int a, int b, int c){
return min(a, min(b, c));
}
};
126、155. 最小栈
储存最小值,压栈时,将元素与最小值的差值压入。
class MinStack {
public:
stack<long long> st;
long long min;
MinStack() {
min = -1;
}
void push(int val) {
if(st.empty()){
st.push(0);
min = val;
}else{
st.push(val - min);
if(val < min) min = val;
}
}
void pop() {
if(!st.empty()){
long long tmp = st.top();
st.pop();
if(tmp < 0) min -= tmp;
}
}
int top() {
long long diff = st.top();
if (diff < 0)
return min;
else
return min + diff;
}
int getMin() {
return min;
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack* obj = new MinStack();
* obj->push(val);
* obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->getMin();
*/