目录
哈希
1 力扣49 两数之和
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int>umap; //key存放数值,value存放下标
for(int i=0;i<nums.size();i++){
auto iter = umap.find(target-nums[i]);
if(iter!=umap.end()){
return {iter->second,i};
}
umap.insert(make_pair(nums[i],i));
}
return{};
}
};
2 力扣49 字母异位词分组
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string> &strs) {
unordered_map<string, vector<string>> m;
for (auto &s : strs) {
string sorted_s = s;
ranges::sort(sorted_s);
m[sorted_s].push_back(s); // sorted_s 相同的字符串分到同一组
}
vector<vector<string>> ans;
ans.reserve(m.size()); // 预分配空间
for (auto &[_, value] : m) {
ans.push_back(value);
}
return ans;
}
};
3 力扣128 最长连续序列
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> num_set;
for (const int& num : nums) {
num_set.insert(num);
}
int longestStreak = 0;
for (const int& num : num_set) {
if (!num_set.count(num - 1)) {
int currentNum = num;
int currentStreak = 1;
while (num_set.count(currentNum + 1)) {
currentNum += 1;
currentStreak += 1;
}
longestStreak = max(longestStreak, currentStreak);
}
}
return longestStreak;
}
};
双指针
4 力扣283 移动零
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int n = nums.size(), left = 0, right = 0;
while (right < n) {
if (nums[right]) {
swap(nums[left], nums[right]);
left++;
}
right++;
}
}
};
5 力扣11 盛水最多的容器
class Solution {
public:
int maxArea(vector<int>& height) {
int l = 0, r = height.size() - 1;
int ans = 0;
while (l < r) {
int area = min(height[l], height[r]) * (r - l);
ans = max(ans, area);
if (height[l] <= height[r]) {
++l;
}
else {
--r;
}
}
return ans;
}
};
6 力扣15 三数之和
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>>res;
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();i++){
if(nums[i]>0)return res; //边界条件
if(i>0&&nums[i]==nums[i-1])continue;
unordered_set<int>uset;
for(int j = i+1;j<nums.size();j++){
//因为i\j\k都不能相同,所以需要从i+2开始
if(j>i+2&&nums[j]==nums[j-1]&&nums[j]==nums[j-2])continue;
int c = 0-nums[i]-nums[j];
if(uset.find(c)!=uset.end()){
res.push_back(vector<int>{nums[i],nums[j],c});
uset.erase(c); //c只是一个媒介,不需要保持
}else{
uset.insert(nums[j]); //将遍历的元素放入哈希表
}
}
}
return res;
}
};
7 力扣42 接雨水
记录从左向右的最大值和从右到左的最大值,每个坐标的高度水平能接的雨水量是两边比他高的更小的乘上差值
class Solution {
public:
int trap(vector<int>& height) {
if (height.size() <= 2) return 0;
vector<int> maxLeft(height.size(), 0);
vector<int> maxRight(height.size(), 0);
int size = maxRight.size();
// 记录每个柱子左边柱子最大高度
maxLeft[0] = height[0];
for (int i = 1; i < size; i++) {
maxLeft[i] = max(height[i], maxLeft[i - 1]);
}
// 记录每个柱子右边柱子最大高度
maxRight[size - 1] = height[size - 1];
for (int i = size - 2; i >= 0; i--) {
maxRight[i] = max(height[i], maxRight[i + 1]);
}
// 求和
int sum = 0;
for (int i = 0; i < size; i++) {
int count = min(maxLeft[i], maxRight[i]) - height[i];
if (count > 0) sum += count;
}
return sum;
}
};
滑动窗口
8 力扣3 无重复字符的最长子串
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> dic;
int i = -1, res = 0, len = s.size();
//遍历完才能知道最长的不重复长度 abcaingnkdl
for(int j = 0; j < len; j++) {
if (dic.find(s[j]) != dic.end())
i = max(i, dic.find(s[j])->second); // 更新左指针
dic[s[j]] = j; // 哈希表记录
res = max(res, j - i); // 更新结果
}
return res;
}
};
9 力扣483 找到字符串中所有字母异位词
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int sLen = s.size(), pLen = p.size();
if (sLen < pLen) {
return vector<int>();
}
vector<int> ans;
vector<int> sCount(26);
vector<int> pCount(26);
//同时统计字符串中前Plen长度的字符个数
for (int i = 0; i < pLen; ++i) {
++sCount[s[i] - 'a'];
++pCount[p[i] - 'a'];
}
if (sCount == pCount) { //两个数组相等装入结果0
ans.emplace_back(0);
}
//开始滑动
for (int i = 0; i < sLen - pLen; ++i) {
--sCount[s[i] - 'a']; //减少窗口的首位
++sCount[s[i + pLen] - 'a']; //增加窗口的末尾
if (sCount == pCount) {
ans.emplace_back(i + 1);
}
}
return ans;
}
};
子串
10 力扣560 和为K的子数组(子数组连续)
算法面试实录-和为 k 的子数组_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1gN411E7Zx/?spm_id_from=333.337.search-card.all.click&vd_source=b5cc04f324fc9d6ee48a5febd77392fc 上面的链接中前缀和 和 哈希的关联找子数组讲的清楚
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int preSum = 0;
int ans = 0;
// 键:前缀和,值:前缀和出现的次数
unordered_map<int, int> record;
// 初始化时为空区间,则前缀和为0,出现的次数为1,这样可以统计单个元素满足的情况
record[0] = 1;
for(const int& num: nums) {
preSum += num;
// 检查当前的前缀和preSum减去目标值k后的结果在哈希表中是否存在
if(record.count(preSum - k)) {
ans += record[preSum - k];
}
++record[preSum];
}
return ans;
}
};
11 力扣239 滑动窗口最大值
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n = nums.size();
deque<int> q;
//队列中只存放大到小的数值,当有更大的值出现时,把队列中的数全部出队
for (int i = 0; i < k; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);
}
vector<int> ans = {nums[q.front()]};
for (int i = k; i < n; ++i) {
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);
//当左侧的值要被丢弃时,i-k是窗口左侧第一个下标
while (q.front() <= i - k) {
q.pop_front();
}
ans.push_back(nums[q.front()]);
}
return ans;
}
};
12 力扣76 最小覆盖子串
class Solution {
bool is_covered(int cnt_s[], int cnt_t[]) {
//如果其中一个字母出现的次数小于寻找的字符串中的次数,不存在
for (int i = 'A'; i <= 'Z'; i++) {
if (cnt_s[i] < cnt_t[i]) {
return false;
}
}
for (int i = 'a'; i <= 'z'; i++) {
if (cnt_s[i] < cnt_t[i]) {
return false;
}
}
return true;
}
public:
string minWindow(string s, string t) {
int m = s.length();
int ans_left = -1, ans_right = m, left = 0;
int cnt_s[128]{}, cnt_t[128]{}; //使用128个ASCII码存储
for (char c : t) { //统计t字符串的字符出现情况
cnt_t[c]++;
}
//滑动窗口
for (int right = 0; right < m; right++) { // 移动子串右端点
cnt_s[s[right]]++; // 右端点字母移入子串
while (is_covered(cnt_s, cnt_t)) { // 涵盖
if (right - left < ans_right - ans_left) { // 找到更短的子串
ans_left = left; // 记录此时的左右端点
ans_right = right;
}
cnt_s[s[left++]]--; // 左端点字母移出子串
}
}
//如果子串的起始值为0,返回空字符串,否则从左侧起点截取对应的长度
return ans_left < 0 ? "" : s.substr(ans_left, ans_right - ans_left + 1);
}
};
普通数组
13 力扣53 最大子数组和
贪心算法,当目前和小于0就从头开始加,当更大的时候就更换结果
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int res=INT_MIN;
int count = 0;
for(int i=0;i<nums.size();i++){
count+=nums[i];
if(count>res){
res = count;
}
if(count<0)count = 0;
}
return res;
}
};
14 力扣56 合并区间
先按一边排序,先放进一个区间,之后遍历,对另一边进行判断,不断更新判断边的数值,无重合就插入新的区间,有重合就更新插入新区间的边界值
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
sort(intervals.begin(),intervals.end(),[](vector<int>l,vector<int>r){
return l[0]<r[0];
});
vector<vector<int>>res={};
res.emplace_back(intervals[0]);
for(int i=1;i<intervals.size();i++){
if(res.back()[1]>=intervals[i][0]){
res.back()[1] = max(intervals[i][1],res.back()[1]);
}else{
res.emplace_back(intervals[i]);
}
}
return res;
}
};
15 力扣189 轮转数组(3次翻转)
class Solution {
public:
void rotate(vector<int>& nums, int k) {
auto reverse = [&](int i, int j) {
while (i < j) {
swap(nums[i++], nums[j--]);
}
};
int n = nums.size();
k %= n; // 轮转 k 次等于轮转 k%n 次
reverse(0, n - 1);
reverse(0, k - 1);
reverse(k, n - 1);
}
};
16 力扣238 除自身以外数组的乘积
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size();
vector<int> pre(n, 1);
for (int i = 1; i < n; i++) {
pre[i] = pre[i - 1] * nums[i - 1];
}
vector<int> suf(n, 1);
for (int i = n - 2; i >= 0; i--) {
suf[i] = suf[i + 1] * nums[i + 1];
}
vector<int> ans(n);
for (int i = 0; i < n; i++) {
ans[i] = pre[i] * suf[i];
}
return ans;
}
};
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int len = nums.size();
if (len == 0) return {};
vector<int> ans(len, 1);
ans[0] = 1;
int tmp = 1;
for (int i = 1; i < len; i++) { //先求左部分乘积
ans[i] = ans[i - 1] * nums[i - 1];
}
for (int i = len - 2; i >= 0; i--) { //使用tmp存储右侧乘积结果
tmp *= nums[i + 1];
ans[i] *= tmp;
}
return ans;
}
};
17 力扣41 缺失的第一个正数
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int n = nums.size();
for (int& num: nums) { //先将元素小于0的数剔除
if (num <= 0) {
num = n + 1;
}
}
for (int i = 0; i < n; ++i) { //使用负数标记出现过的位置
int num = abs(nums[i]);
if (num <= n) {
nums[num - 1] = -abs(nums[num - 1]);
}
}
for (int i = 0; i < n; ++i) {
if (nums[i] > 0) {
return i + 1;
}
}
return n + 1;
}
};
矩阵
18 力扣73 矩阵置零
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int m = matrix.size();
int n = matrix[0].size();
vector<int> row(m), col(n); //记录出现0的情况
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (!matrix[i][j]) {
row[i] = col[j] = true;
}
}
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (row[i] || col[j]) { //如果判断行或者列是0,则将其置为0
matrix[i][j] = 0;
}
}
}
}
};
19 力扣54 螺旋矩阵
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
int m=matrix.size();
int n=matrix[0].size();
vector<int> res;
int i,j;
int startx=0,starty=0;
int minmum=min(m,n);//把m和n的最小值记录下来
int loop=minmum/2;//要跑的圈数
int offset=1;//动态调整每圈每个边的长度,都遵循左闭右开的原则
while(loop--){
i=startx;//每圈开始前要调整初始元素的位置,比如第一圈初始为[0][0],第二圈就是[1][1]
j=starty;
for(j=starty;j<n-offset;j++){//上
res.push_back(matrix[startx][j]);
}
for(i=startx;i<m-offset;i++){//右
res.push_back(matrix[i][j]);
}
for(;j>starty;j--){//下
res.push_back(matrix[i][j]);
}
for(;i>startx;i--){//左
res.push_back(matrix[i][j]);
}
startx++;
starty++;
offset++;
}
if(minmum%2==1){//矩阵的宽为奇数时,遍历到最后会留一行或一列元素未遍历
i=startx;//把所有的圈遍历完后,要把i,j再次赋值到新的初始位置准备遍历那一行或一列
j=starty;//startx,starty已经在最后一圈里自增1了
if(minmum==m){//左右为宽,上下为长,这种情况未遍历的为一行
for(;j<n-offset+1;j++){//不用在遵循左闭右开的原则了,所以n-offset+1,把最后一个元素也遍历了
res.push_back(matrix[startx][j]);
}
}
else{//左右为长,上下为狂,这种情况未遍历的为一列
for(;i<m-offset+1;i++){
res.push_back(matrix[i][j]);
}
}
}
return res;
}
};
20 力扣48 旋转图像
class Solution {
public:
void rotate(vector<vector<int> > &matrix) {
int Layer = matrix.size()/2; //需要旋转的层数
int Move = matrix.size();
/* Top, Left, Bottom, Right */ //每次移动4个元素
int T = 0;
int L = 0;
int B = matrix.size()-1;
int R = matrix.size()-1;
for( int LayerIdx = 0; LayerIdx < Layer; ++LayerIdx )
{
for(int i = 0; i < (Move-1) ; i++)
{
swap( matrix[T][L+i], matrix[T+i][R] );
swap( matrix[T][L+i], matrix[B][R-i] );
swap( matrix[T][L+i], matrix[B-i][L] );
}
++T;
--B;
--R;
++L;
Move -= 2;
}
}
};
20 力扣240 搜索二维矩阵2
从左下或者右上开始 ,如果两边的单调性相同,移动时无法判断
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int i = 0, j = matrix[0].size() - 1;
while(i < matrix.size() && j >= 0) {
if (target == matrix[i][j]) {
return true;
}
else if (target > matrix[i][j]) {
i++;
} else {
j--;
}
}
return false;
}
};
链表
21 力扣160 相交链表
/**
* 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) {
if (headA == NULL || headB == NULL) {
return NULL;
}
ListNode* tmp1 = headA;
ListNode* tmp2 = headB;
while(tmp1!=tmp2){
if(tmp1==NULL){
tmp1 = headB;
}else{
tmp1 = tmp1->next;
}
if(tmp2==NULL){
tmp2 = headA;
}else{
tmp2 = tmp2->next;
}
}
return tmp1;
}
};
22 力扣206 反转链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = nullptr;
ListNode* cur = head;
while(cur!=nullptr){
ListNode* tmp =cur->next;
cur->next=pre ;
pre = cur;
cur = tmp;
}
return pre;
}
};
23 力扣234 回文链表
class Solution {
public:
bool isPalindrome(ListNode* head) {
vector<int> vals;
while (head != nullptr) {
vals.emplace_back(head->val);
head = head->next;
}
for (int i = 0, j = (int)vals.size() - 1; i < j; ++i, --j) {
if (vals[i] != vals[j]) {
return false;
}
}
return true;
}
};
24 力扣141 环形链表
class Solution {
public:
bool hasCycle(ListNode* head) {
if (head == nullptr || head->next == nullptr) {
return false;
}
ListNode* slow = head;
ListNode* fast = head->next;
while (slow != fast) {
if (fast == nullptr || fast->next == nullptr) {
return false;
}
slow = slow->next;
fast = fast->next->next;
}
return true;
}
};
25 力扣142 环形链表2
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head==NULL)return NULL;
ListNode* fast = head;
ListNode* slow = head;
while(fast->next!=NULL&&fast->next->next!=NULL){
slow = slow->next;
fast = fast->next->next;
if(fast==slow){
ListNode* tmp1 = fast;
ListNode* tmp2 = head;
while(tmp1!=tmp2){
tmp1 = tmp1->next;
tmp2 = tmp2->next;
}
return tmp2;
}
}
return NULL;
}
};
26 力扣21 合并两个有序的链表
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* dum = new ListNode(0);
ListNode* cur = dum;
while (list1 != nullptr && list2 != nullptr) {
if (list1->val < list2->val) {
cur->next = list1;
list1 = list1->next;
}
else {
cur->next = list2;
list2 = list2->next;
}
cur = cur->next;
}
cur->next = list1 != nullptr ? list1 : list2;
return dum->next;
}
};
——27 力扣2 两数相加
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
// 构造哑巴节点 dummy,最后返回 dummy.next, 以方便处理新链表的头节点。
ListNode* dummy = new ListNode(0);
ListNode* node = dummy; // node 一直会变化(前进)
int carrier = 0; // 进位
// 只要有没走到头的链表或者进位不为 0 就一直前进。
while (l1 || l2 || carrier) {
// 求和,考虑可能有链表走到头
int sum = (l1 ? l1->val : 0) + (l2 ? l2->val : 0) + carrier;
// 在尾部添加节点
node->next = new ListNode(sum % 10);
node = node->next;
// 更新进位,并向两个链表尾部前进
carrier = sum / 10;
if (l1) l1 = l1->next;
if (l2) l2 = l2->next;
}
return dummy->next;
}
};
28 力扣19 删除链表的倒数第N个节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyhead = new ListNode(0);
dummyhead->next = head;
ListNode* fast = dummyhead;
while(n--&&fast!=nullptr){
fast = fast->next;
}
fast= fast->next; //fast多走一步,这样才能将slow放到删除节点的前一个节点
ListNode* slow = dummyhead;
while(fast!=nullptr){
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
return dummyhead->next;
}
};
29 力扣24 两两交换链表的节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyhead = new ListNode(0);
dummyhead->next = head;
ListNode* cur = dummyhead;
while(cur->next!=nullptr&&cur->next->next!=nullptr){
ListNode* tmp1 = cur->next->next;
ListNode* tmp2 = tmp1->next;
ListNode* tmp3 = cur->next;
cur->next->next = tmp2;
tmp1->next = tmp3;
cur->next = tmp1;
cur = cur->next;
cur = cur->next;
}
return dummyhead->next;
}
};
30 力扣25 K个一组翻转链表
两两翻转改为K个一组翻转,通过循环将两两翻转改为K个一组
class Solution {
public:
ListNode *reverseKGroup(ListNode *head, int k) {
int n = 0;
for (ListNode *cur = head; cur; cur = cur->next)
++n; // 统计节点个数
ListNode *dummy = new ListNode(0, head), *p0 = dummy;
ListNode *pre = nullptr, *cur = head;
for (; n >= k; n -= k) {
for (int i = 0; i < k; ++i) { // 同 92 题
ListNode *nxt = cur->next;
cur->next = pre; // 每次循环只修改一个 next,方便大家理解
pre = cur;
cur = nxt;
}
// 见视频
ListNode *nxt = p0->next;
p0->next->next = cur;
p0->next = pre;
p0 = nxt;
}
return dummy->next;
}
};
——31 力扣138 随机链表的复制
哈希表
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head == nullptr) return nullptr;
Node* cur = head;
unordered_map<Node*, Node*> map;
// 3. 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
while(cur != nullptr) {
map[cur] = new Node(cur->val);
cur = cur->next;
}
cur = head;
// 4. 构建新链表的 next 和 random 指向
while(cur != nullptr) {
map[cur]->next = map[cur->next];
map[cur]->random = map[cur->random];
cur = cur->next;
}
// 5. 返回新链表的头节点
return map[head];
}
};
拼接+拆分
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head == nullptr) return nullptr;
Node* cur = head;
// 1. 复制各节点,并构建拼接链表
while(cur != nullptr) {
Node* tmp = new Node(cur->val);
tmp->next = cur->next;
cur->next = tmp;
cur = tmp->next;
}
// 2. 构建各新节点的 random 指向
cur = head;
while(cur != nullptr) {
if(cur->random != nullptr)
cur->next->random = cur->random->next;
cur = cur->next->next;
}
// 3. 拆分两链表
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; // 返回新链表头节点
}
};
32 力扣148 排序链表
归并排序
class Solution {
public:
ListNode* sortList(ListNode* head) {
if (!head || !head->next) return head;
ListNode* mid = findMid(head);
ListNode* newHead = mid->next;
mid->next = nullptr;
ListNode* left = sortList(head);
ListNode* right = sortList(newHead);
return merge(left, right);
}
ListNode* findMid(ListNode* head) {
ListNode* slow = head;
ListNode* fast = head;
while (fast->next && fast->next->next) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
ListNode* merge(ListNode* l1, ListNode* l2) {
if (!l1) return l2;
if (!l2) return l1;
if (l1->val <= l2->val) {
l1->next = merge(l1->next, l2);
return l1;
} else {
l2->next = merge(l1, l2->next);
return l2;
}
}
};
——33 力扣23 合并K个升序链表
归并排序
class Solution {
// 21. 合并两个有序链表
ListNode *mergeTwoLists(ListNode *list1, ListNode *list2) {
ListNode dummy{}; // 用哨兵节点简化代码逻辑
auto cur = &dummy; // cur 指向新链表的末尾
while (list1 && list2) {
if (list1->val < list2->val) {
cur->next = list1; // 把 list1 加到新链表中
list1 = list1->next;
} else { // 注:相等的情况加哪个节点都是可以的
cur->next = list2; // 把 list2 加到新链表中
list2 = list2->next;
}
cur = cur->next;
}
cur->next = list1 ? list1 : list2; // 拼接剩余链表
return dummy.next;
}
// 合并从 lists[i] 到 lists[j-1] 的链表
ListNode *mergeKLists(vector<ListNode *> &lists, int i, int j) {
int m = j - i;
if (m == 0) return nullptr; // 注意输入的 lists 可能是空的
if (m == 1) return lists[i]; // 无需合并,直接返回
auto left = mergeKLists(lists, i, i + m / 2); // 合并左半部分
auto right = mergeKLists(lists, i + m / 2, j); // 合并右半部分
return mergeTwoLists(left, right); // 最后把左半和右半合并
}
public:
ListNode *mergeKLists(vector<ListNode *> &lists) {
return mergeKLists(lists, 0, lists.size());
}
};
——34 力扣146 LRU缓存
struct DLinkedNode {
int key, value;
DLinkedNode* prev;
DLinkedNode* next;
DLinkedNode(): key(0), value(0), prev(nullptr), next(nullptr) {}
DLinkedNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}
};
class LRUCache {
private:
unordered_map<int, DLinkedNode*> cache;
DLinkedNode* head;
DLinkedNode* tail;
int size;
int capacity;
public:
LRUCache(int _capacity): capacity(_capacity), size(0) {
// 使用伪头部和伪尾部节点
head = new DLinkedNode();
tail = new DLinkedNode();
head->next = tail;
tail->prev = head;
}
int get(int key) {
if (!cache.count(key)) {
return -1;
}
// 如果 key 存在,先通过哈希表定位,再移到头部
DLinkedNode* node = cache[key];
moveToHead(node);
return node->value;
}
void put(int key, int value) {
if (!cache.count(key)) {
// 如果 key 不存在,创建一个新的节点
DLinkedNode* node = new DLinkedNode(key, value);
// 添加进哈希表
cache[key] = node;
// 添加至双向链表的头部
addToHead(node);
++size;
if (size > capacity) {
// 如果超出容量,删除双向链表的尾部节点
DLinkedNode* removed = removeTail();
// 删除哈希表中对应的项
cache.erase(removed->key);
// 防止内存泄漏
delete removed;
--size;
}
}
else {
// 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
DLinkedNode* node = cache[key];
node->value = value;
moveToHead(node);
}
}
void addToHead(DLinkedNode* node) {
node->prev = head;
node->next = head->next;
head->next->prev = node;
head->next = node;
}
void removeNode(DLinkedNode* node) {
node->prev->next = node->next;
node->next->prev = node->prev;
}
void moveToHead(DLinkedNode* node) {
removeNode(node);
addToHead(node);
}
DLinkedNode* removeTail() {
DLinkedNode* node = tail->prev;
removeNode(node);
return node;
}
};
二叉树
35 力扣240 二叉树的中序遍历
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
traversal(cur->left, vec); // 左
vec.push_back(cur->val); // 中
traversal(cur->right, vec); // 右
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
};
36 力扣104 二叉树的最大深度
class solution {
public:
int getdepth(treenode* node) {
if (node == NULL) return 0;
int leftdepth = getdepth(node->left); // 左
int rightdepth = getdepth(node->right); // 右
int depth = 1 + max(leftdepth, rightdepth); // 中
return depth;
}
int maxdepth(treenode* root) {
return getdepth(root);
}
};
37 力扣226 翻转二叉树
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (root == NULL) return root;
swap(root->left, root->right); // 中
invertTree(root->left); // 左
invertTree(root->right); // 右
return root;
}
};
38 力扣101 对称二叉树
class Solution {
public:
bool compare(TreeNode* left, TreeNode* right) {
// 首先排除空节点的情况
if (left == NULL && right != NULL) return false;
else if (left != NULL && right == NULL) return false;
else if (left == NULL && right == NULL) return true;
// 排除了空节点,再排除数值不相同的情况
else if (left->val != right->val) return false;
// 此时就是:左右节点都不为空,且数值相同的情况
// 此时才做递归,做下一层的判断
bool outside = compare(left->left, right->right); // 左子树:左、 右子树:右
bool inside = compare(left->right, right->left); // 左子树:右、 右子树:左
bool isSame = outside && inside; // 左子树:中、 右子树:中 (逻辑处理)
return isSame;
}
bool isSymmetric(TreeNode* root) {
if (root == NULL) return true;
return compare(root->left, root->right);
}
};
39 力扣101 二叉树的直径
class Solution {
int ans;
int depth(TreeNode* rt){
if (rt == NULL) {
return 0; // 访问到空节点了,返回0
}
int L = depth(rt->left); // 左儿子为根的子树的深度
int R = depth(rt->right); // 右儿子为根的子树的深度
ans = max(ans, L + R + 1); // 计算d_node即L+R+1 并更新ans
return max(L, R) + 1; // 返回该节点为根的子树的深度
}
public:
int diameterOfBinaryTree(TreeNode* root) {
ans = 1;
depth(root);
return ans - 1;
}
};
40 力扣102 二叉树的层序遍历
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
vector<vector<int>> result;
while (!que.empty()) {
int size = que.size();
vector<int> vec;
// 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
vec.push_back(node->val);
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
result.push_back(vec);
}
return result;
}
};
40 力扣108 将有序数组转为二叉搜索树
class Solution {
private:
TreeNode* traversal(vector<int>& nums, int left, int right) {
if (left > right) return nullptr;
int mid = left + ((right - left) / 2);
TreeNode* root = new TreeNode(nums[mid]);
root->left = traversal(nums, left, mid - 1);
root->right = traversal(nums, mid + 1, right);
return root;
}
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
TreeNode* root = traversal(nums, 0, nums.size() - 1);
return root;
}
};
41 力扣98 验证二叉搜索树
中序遍历放入数组,之后对数组进行判断
class Solution {
private:
vector<int> vec;
void traversal(TreeNode* root) {
if (root == NULL) return;
traversal(root->left);
vec.push_back(root->val); // 将二叉搜索树转换为有序数组
traversal(root->right);
}
public:
bool isValidBST(TreeNode* root) {
vec.clear(); // 不加这句在leetcode上也可以过,但最好加上
traversal(root);
for (int i = 1; i < vec.size(); i++) {
// 注意要小于等于,搜索树里不能有相同元素
if (vec[i] <= vec[i - 1]) return false;
}
return true;
}
};
42 力扣230 二叉搜索树中的第K个最小元素
class Solution {
public:
int kthSmallest(TreeNode* root, int k) {
vector<int> res;
dfs(root, res);
return res[k - 1];
}
void dfs(TreeNode* root, vector<int>& res) {
if (!root) return;
dfs(root->left, res);
res.push_back(root->val);
dfs(root->right, res);
}
};
43 力扣230 二叉树中的右视图
层序遍历,每层最后一个元素放进去
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
vector<int> result;
while (!que.empty()) {
int size = que.size(); //先记录下每层的元素个数
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
// 将每一层的最后元素放入result数组中
if (i == (size - 1)) result.push_back(node->val);
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return result;
}
};
44 力扣114 二叉树展开为链表
class Solution {
public:
void flatten(TreeNode* root) {
while(root){
TreeNode* p = root->left;
if(p){
while(p->right) p = p->right;
p->right = root->right;
root->right = root->left;
root->left = nullptr;
}
root = root->right;
}
}
};
45 力扣105 从前序与中序遍历序列构造二叉树
class Solution {
private:
TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) {
if (postorder.size() == 0) return NULL;
// 后序遍历数组最后一个元素,就是当前的中间节点
int rootValue = postorder[postorder.size() - 1];
TreeNode* root = new TreeNode(rootValue);
// 叶子节点
if (postorder.size() == 1) return root;
// 找到中序遍历的切割点
int delimiterIndex;
for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
if (inorder[delimiterIndex] == rootValue) break;
}
// 切割中序数组
// 左闭右开区间:[0, delimiterIndex)
vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
// [delimiterIndex + 1, end)
vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end() );
// postorder 舍弃末尾元素
postorder.resize(postorder.size() - 1);
// 切割后序数组
// 依然左闭右开,注意这里使用了左中序数组大小作为切割点
// [0, leftInorder.size)
vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
// [leftInorder.size(), end)
vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());
root->left = traversal(leftInorder, leftPostorder);
root->right = traversal(rightInorder, rightPostorder);
return root;
}
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (inorder.size() == 0 || postorder.size() == 0) return NULL;
return traversal(inorder, postorder);
}
};
46 力扣437 路径总和3(二叉树 + 回溯)
class Solution {
private:
unordered_map<int, int> prefix; // <前缀和,其出现次数>
void dfs(TreeNode* root, int sum, int cur_sum, int& res)
{
if (!root) return;
cur_sum += root->val; // 更新前缀和
// 当前路径中存在以当前节点为终点的和为sum的子路径
if (prefix.find(cur_sum - sum) != prefix.end())
res += prefix[cur_sum - sum];
prefix[cur_sum]++; // 将当前节点加入路径
dfs(root->left, sum, cur_sum, res); // 在其左子树中递归寻找
dfs(root->right, sum, cur_sum, res);// 在其右子树中递归寻找
prefix[cur_sum]--; // 回溯
}
public:
int pathSum(TreeNode* root, int sum)
{
int res = 0; // 满足条件的路径数量
prefix[0] = 1; // 前缀和为0的路径只有一条:哪个节点都不选
dfs(root, sum, 0, res);
return res;
}
};
47 力扣236 二叉树的最近公共祖先
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == q || root == p || root == NULL) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left != NULL && right != NULL) return root;
if (left == NULL && right != NULL) return right;
else if (left != NULL && right == NULL) return left;
else { // (left == NULL && right == NULL)
return NULL;
}
}
};
48 力扣124 二叉树的最大路径和
class Solution {
int res = Integer.MIN_VALUE; // 最大路径和,初始为极小值
public int maxPathSum(TreeNode root) {
dfs(root); // 深度优先搜索
return res;
}
private int dfs(TreeNode node){
if(node == null)return 0; // 空节点,贡献值为0
int leftMaxPathSum = Math.max(0, dfs(node.left)); // 递归搜索节点的左子树,获取左子树贡献的路径值,小于0的贡献为0
int rightMaxPathSum = Math.max(0, dfs(node.right)); // 递归搜索节点的右子树,获取左子树贡献的路径值,小于0的贡献为0
res = Math.max(res, node.val + leftMaxPathSum + rightMaxPathSum); // 以当前节点为中点的路径和
return node.val + Math.max(leftMaxPathSum, rightMaxPathSum); // 返回该节点参与的最大子树路径和
}
}
——图论
49 力扣200 岛屿数量
50 力扣200 腐烂的橘子
51 力扣207 课程表
52 力扣208 前缀树
回溯
53 力扣46 全排列
与子集问题不同,排列后也无法直接开始索引判断,但是还需要判断元素是否使用过,所以使用used数组
class Solution {
public:
vector<vector<int>> result;
vector<int> path;
void backtracking (vector<int>& nums, vector<bool>& used) {
// 此时说明找到了一组
if (path.size() == nums.size()) {
result.push_back(path);
return;
}
for (int i = 0; i < nums.size(); i++) {
if (used[i] == true) continue; // path里已经收录的元素,直接跳过
used[i] = true;
path.push_back(nums[i]);
backtracking(nums, used);
path.pop_back();
used[i] = false;
}
}
vector<vector<int>> permute(vector<int>& nums) {
result.clear();
path.clear();
vector<bool> used(nums.size(), false);
backtracking(nums, used);
return result;
}
};
54 力扣78 子集
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& nums, int startIndex) {
result.push_back(path); // 收集子集,要放在终止添加的上面,否则会漏掉自己
if (startIndex >= nums.size()) { // 终止条件可以不加
return;
}
for (int i = startIndex; i < nums.size(); i++) {
path.push_back(nums[i]);
backtracking(nums, i + 1);
path.pop_back();
}
}
public:
vector<vector<int>> subsets(vector<int>& nums) {
result.clear();
path.clear();
backtracking(nums, 0);
return result;
}
};
55 力扣17 电话号码的字母组合
class Solution {
public:
const string lettermap[10]{ //先得到字符数组的映射
"",
"",
"abc",
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz"
};
vector<string>res;
string path="";
void backtrading(string digits,int idx){
if(path.size()==digits.size()){
res.push_back(path);
return ;
}
int dig = digits[idx] -'0'; //将指向位置的字符转为数字
string letter = lettermap[dig]; //获得数字对应的字符串
for(int i=0;i<letter.size();i++){
path.push_back(letter[i]);
backtrading(digits,idx+1); //在backtrading的索引位置+1
path.pop_back();
}
}
vector<string> letterCombinations(string digits) {
if(digits.size()==0){
return res;
}
backtrading(digits,0);
return res;
}
};
56 力扣39 组合总和
class Solution {
public:
vector<vector<int>>res;
vector<int>path;
void backtrading(vector<int>candidates,int target,int sum,int idx){
if(sum==target){
res.push_back(path);
return;
}
if(sum>target){
return ;
}
for(int i=idx;i<candidates.size();i++){
sum+=candidates[i];
path.push_back(candidates[i]);
backtrading(candidates,target,sum,i);
path.pop_back();
sum-=candidates[i];
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
if(candidates.size()==0){
return res;
}
backtrading(candidates,target,0,0);
return res;
}
};
57 力扣22 括号生成
使用左右括号变量计数来进行回溯
class Solution {
void backtrack(vector<string>& ans, string& cur, int open, int close, int n) {
if (cur.size() == n * 2) {
ans.push_back(cur);
return;
}
if (open < n) { //左括号判断
cur.push_back('(');
backtrack(ans, cur, open + 1, close, n);
cur.pop_back();
}
if (close < open) { //右括号判断
cur.push_back(')');
backtrack(ans, cur, open, close + 1, n);
cur.pop_back();
}
}
public:
vector<string> generateParenthesis(int n) {
vector<string> result;
string current;
backtrack(result, current, 0, 0, n);
return result;
}
};
——58 力扣79 单词搜索
class Solution {
public:
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;
}
private:
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;
}
};
59 力扣131 分割回文串
class Solution {
private:
vector<vector<string>> result;
vector<string> path; // 放已经回文的子串
void backtracking (const string& s, int startIndex) {
// 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了
if (startIndex >= s.size()) {
result.push_back(path);
return;
}
for (int i = startIndex; i < s.size(); i++) {
if (isPalindrome(s, startIndex, i)) { // 是回文子串
// 获取[startIndex,i]在s中的子串
string str = s.substr(startIndex, i - startIndex + 1);
path.push_back(str);
} else { // 不是回文,跳过
continue;
}
backtracking(s, i + 1); // 寻找i+1为起始位置的子串
path.pop_back(); // 回溯过程,弹出本次已经填在的子串
}
}
bool isPalindrome(const string& s, int start, int end) {
for (int i = start, j = end; i < j; i++, j--) {
if (s[i] != s[j]) {
return false;
}
}
return true;
}
public:
vector<vector<string>> partition(string s) {
result.clear();
path.clear();
backtracking(s, 0);
return result;
}
};
60 力扣51 N皇后
class Solution {
private:
vector<vector<string>> result;
// n 为输入的棋盘大小
// row 是当前递归到棋盘的第几行了
void backtracking(int n, int row, vector<string>& chessboard) {
if (row == n) {
result.push_back(chessboard);
return;
}
for (int col = 0; col < n; col++) {
if (isValid(row, col, chessboard, n)) { // 验证合法就可以放
chessboard[row][col] = 'Q'; // 放置皇后
backtracking(n, row + 1, chessboard);
chessboard[row][col] = '.'; // 回溯,撤销皇后
}
}
}
bool isValid(int row, int col, vector<string>& chessboard, int n) {
// 检查列
for (int i = 0; i < row; i++) { // 这是一个剪枝
if (chessboard[i][col] == 'Q') {
return false;
}
}
// 检查 45度角是否有皇后
for (int i = row - 1, j = col - 1; i >=0 && j >= 0; i--, j--) {
if (chessboard[i][j] == 'Q') {
return false;
}
}
// 检查 135度角是否有皇后
for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (chessboard[i][j] == 'Q') {
return false;
}
}
return true;
}
public:
vector<vector<string>> solveNQueens(int n) {
result.clear();
std::vector<std::string> chessboard(n, std::string(n, '.'));
backtracking(n, 0, chessboard);
return result;
}
};
二分查找
61 力扣35 搜索插入位置
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
int left = 0, right = n - 1, ans = n; //先将结果放到最后一个位置
while (left <= right) {
int mid = ((right - left) >> 1) + left;
if (target <= nums[mid]) {
ans = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return ans;
}
};
62 力扣74 搜索二维矩阵
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
//m是行数,n是列数
int m = matrix.size(), n = matrix[0].size();
int i = 0, j = n - 1;
while (i < m && j >= 0) { // 还有剩余元素
if (matrix[i][j] == target) {
return true; // 找到 target
}
if (matrix[i][j] < target) {
i++; // 这一行剩余元素全部小于 target,排除
} else {
j--; // 这一列剩余元素全部大于 target,排除
}
}
return false;
}
};
63 力扣34 在排序数组中查找元素的第一个和最后一个位置
取巧方式
class Solution {
public:
int searchInsert(vector<int>& nums, double target) {
int n = nums.size();
int left = 0, right = n - 1, ans = n; //先将结果放到最后一个位置
while (left <= right) {
int mid = ((right - left) >> 1) + left;
if (target <= nums[mid]) {
ans = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return ans;
}
vector<int> searchRange(vector<int>& nums, int target) {
int left = searchInsert(nums,target-0.5);
int right = searchInsert(nums,target+0.5);
if(left>=nums.size()||nums[left]!=target){
return {-1,-1};
}else{
return {left,right-1};
}
}
};
常规思想
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
// 二分查找下标,是否存在target的对应下标mid,没有就返回[-1,-1],有的话返回下标,
// mid向左右范围扩展是否有相同值
vector<int> ans;
int left = 0, right = nums.size() - 1;
// 用来标记是否有target在nums数组中
int pivot = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else {
pivot = mid;
break;
}
}
// 二分查找中是否有结果
if (pivot == -1) {
ans.push_back(-1);
ans.push_back(-1);
}
else {
int r;
int l = r = pivot;
while (l >= 0 && nums[l] == target) l--;
while (r <= nums.size() - 1 && nums[r] == target) r++;
ans.push_back(l + 1);
ans.push_back(r - 1);
}
return ans;
}
};
64 力扣33 搜索旋转排序数组
class Solution {
public:
int search(vector<int>& nums, int target) {
int n = (int)nums.size();
if (!n) {
return -1;
}
if (n == 1) {
return nums[0] == target ? 0 : -1;
}
int l = 0, r = n - 1;
while (l <= r) {
int mid = (l + r) / 2;
if (nums[mid] == target) return mid;
if (nums[0] <= nums[mid]) {
if (nums[0] <= target && target < nums[mid]) {
r = mid - 1;
} else {
l = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[n - 1]) {
l = mid + 1;
} else {
r = mid - 1;
}
}
}
return -1;
}
};
65 力扣153 寻找旋转排序数组中的最小值
class Solution {
public:
int findMin(vector<int>& nums) {
int low = 0;
int high = nums.size() - 1;
while (low < high) {
int pivot = low + (high - low) / 2;
if (nums[pivot] < nums[high]) {
high = pivot;
}
else {
low = pivot + 1;
}
}
return nums[low];
}
};
66 力扣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;
}
}
};
——栈
67 力扣20 有效的括号
class Solution {
public:
bool isValid(string s) {
if(s.size()%2!=0)return false;
stack<char>st;
for(int i=0;i<s.size();i++){
if(s[i]=='('){
st.push(')');
}else if(s[i]=='['){
st.push(']');
}else if(s[i]=='{'){
st.push('}');
}else if(st.empty()||st.top()!=s[i]){ //多右括号或者不能匹配
return false;
}else{
st.pop();
}
}
return st.empty();
}
};
68 力扣155 最小栈
class MinStack {
stack<int> x_stack;
stack<int> min_stack;
public:
MinStack() {
min_stack.push(INT_MAX);
}
void push(int x) {
x_stack.push(x);
min_stack.push(min(min_stack.top(), x));
}
void pop() {
x_stack.pop();
min_stack.pop();
}
int top() {
return x_stack.top();
}
int getMin() {
return min_stack.top();
}
};
69 力扣394 字符串解码
class Solution {
public:
string getDigits(string &s, size_t &ptr) {
string ret = "";
while (isdigit(s[ptr])) {
ret.push_back(s[ptr++]);
}
return ret;
}
string getString(vector <string> &v) {
string ret;
for (const auto &s: v) {
ret += s;
}
return ret;
}
string decodeString(string s) {
vector <string> stk;
size_t ptr = 0;
while (ptr < s.size()) {
char cur = s[ptr];
if (isdigit(cur)) {
// 获取一个数字并进栈
string digits = getDigits(s, ptr);
stk.push_back(digits);
} else if (isalpha(cur) || cur == '[') {
// 获取一个字母并进栈
stk.push_back(string(1, s[ptr++]));
} else {
++ptr;
vector <string> sub;
while (stk.back() != "[") {
sub.push_back(stk.back());
stk.pop_back();
}
reverse(sub.begin(), sub.end());
// 左括号出栈
stk.pop_back();
// 此时栈顶为当前 sub 对应的字符串应该出现的次数
int repTime = stoi(stk.back());
stk.pop_back();
string t, o = getString(sub);
// 构造字符串
while (repTime--) t += o;
// 将构造好的字符串入栈
stk.push_back(t);
}
}
return getString(stk);
}
};
70 力扣739 每日温度(单调栈应用)
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
vector<int>res(temperatures.size(),0);
stack<int>st; //单调栈,存放数组下标
st.push(0);
for(int i=1;i<temperatures.size();i++){
if(temperatures[i]<=temperatures[st.top()]){ //小于等于就将遍历元素的下标放入单调栈
st.push(i);
}else{ //当前遍历元素大于栈顶的下标元素
while(!st.empty()&&temperatures[i]>temperatures[st.top()]){
res[st.top()] = i-st.top();
st.pop(); //因为有出栈操作,所以在while循环对栈是否为空进行判断
}
//对当前元素进行操作后,还需要将当前遍历元素入栈
st.push(i);
}
}
return res;
}
};
71 力扣84 柱状图中最大的矩形
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int n = heights.size();
vector<int> left(n), right(n);
stack<int> mono_stack;
for (int i = 0; i < n; ++i) {
while (!mono_stack.empty() && heights[mono_stack.top()] >= heights[i]) {
mono_stack.pop();
}
left[i] = (mono_stack.empty() ? -1 : mono_stack.top());
mono_stack.push(i);
}
mono_stack = stack<int>();
for (int i = n - 1; i >= 0; --i) {
while (!mono_stack.empty() && heights[mono_stack.top()] >= heights[i]) {
mono_stack.pop();
}
right[i] = (mono_stack.empty() ? n : mono_stack.top());
mono_stack.push(i);
}
int ans = 0;
for (int i = 0; i < n; ++i) {
ans = max(ans, (right[i] - left[i] - 1) * heights[i]);
}
return ans;
}
};
堆
72 力扣215 数组中的第K个最大元素
泛型算法排序
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
sort(nums.begin(), nums.end());
return nums[nums.size() - k];
}
};
快排
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
return quickSelect(nums, k);
}
private:
int quickSelect(vector<int>& nums, int k) {
// 随机选择基准数
int pivot = nums[rand() % nums.size()];
// 将大于、小于、等于 pivot 的元素划分至 big, small, equal 中
vector<int> big, equal, small;
for (int num : nums) {
if (num > pivot)
big.push_back(num);
else if (num < pivot)
small.push_back(num);
else
equal.push_back(num);
}
// 第 k 大元素在 big 中,递归划分
if (k <= big.size())
return quickSelect(big, k);
// 第 k 大元素在 small 中,递归划分
if (nums.size() - small.size() < k)
return quickSelect(small, k - nums.size() + small.size());
// 第 k 大元素在 equal 中,直接返回 pivot
return pivot;
}
};
73 力扣347 前K个高频元素
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> um;
for (auto& i : nums) {
++um[i];
}
priority_queue<pair<int, int>> pq;
for (auto& i : um) {
pq.push({i.second, i.first});
}
vector<int> res;
while (k--) {
res.push_back(pq.top().second);
pq.pop();
}
return res;
}
};
74 力扣295 数据流的中位数
左边大顶堆,右边小顶堆,小的加左边,大的加右边,平衡俩堆数,新加就弹出,堆顶给对家,奇数取多的,偶数取除2
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;
}
};
贪心算法
75 力扣121 买卖股票的最佳时机
class Solution {
public:
int maxProfit(vector<int>& prices)
{
int cost = INT_MAX, profit = 0; //成本初始化最大值,利润初始化为0
for (int price : prices)
{
cost = min(cost, price); //花费在之前和现在之间选择更小值
profit = max(profit, price - cost); //利润在当前利润和后续利润之间选择更大值
}
return profit;
}
};
76 力扣55 跳跃游戏
class Solution {
public:
bool canJump(vector<int>& nums) {
int cover = 0;
if(nums.size()==1)return true;
for(int i=0;i<=cover;i++){ //在覆盖的范围内跳动
cover = max(cover,nums[i]+i);
if(cover>=nums.size()-1){
return true;
}
}
return false;
}
};
77 力扣45 跳跃游戏2
// 版本二
class Solution {
public:
int jump(vector<int>& nums) {
int curDistance = 0; // 当前覆盖的最远距离下标
int ans = 0; // 记录走的最大步数
int nextDistance = 0; // 下一步覆盖的最远距离下标
for (int i = 0; i < nums.size() - 1; i++) { // 注意这里是小于nums.size() - 1,这是关键所在
nextDistance = max(nums[i] + i, nextDistance); // 更新下一步覆盖的最远距离下标
if (i == curDistance) { // 遇到当前覆盖的最远距离下标
curDistance = nextDistance; // 更新当前覆盖的最远距离下标
ans++;
}
}
return ans;
}
};
78 力扣763 划分字母区间
class Solution {
public:
vector<int> partitionLabels(string s) {
int hash[27]={};
for(int i=0;i<s.size();i++){
hash[s[i]-'a'] = i; //将每个字母出现的最远下标计入数组
}
vector<int>res;
int left = 0;
int right = 0;
for(int i=0;i<s.size();i++){
right = max(right,hash[s[i]-'a']);
if(i==right){
res.push_back(right-left+1);
left = i+1;
}
}
return res;
}
};
动态规划
79 力扣70 爬楼梯
class Solution {
public:
int dp[46]={}; //dp[i],到第i个台阶有dp[i]种方法
int climbStairs(int n) {
dp[1]=1;
dp[2]=2;
for(int i=3;i<=n;i++){
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
};
80 力扣118 杨辉三角
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>> c(numRows);
for (int i = 0; i < numRows; i++) {
c[i].resize(i + 1, 1);
for (int j = 1; j < i; j++) {
// 左上方的数 + 正上方的数
c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
}
}
return c;
}
};
81 力扣198 打家劫舍
class Solution {
public:
int rob(vector<int>& nums) {
vector<int>dp(nums.size()+1,0); //考虑前i个房间的最大值为dp[i]
if(nums.size()==1)return nums[0];
if(nums.size()==2)return max(nums[0],nums[1]);
dp[1] = nums[0];
dp[2] = max(nums[0],nums[1]);
for(int i=3;i<=nums.size();i++){
dp[i] = max(dp[i-1],dp[i-2]+nums[i-1]);
}
return dp[nums.size()];
}
};
82 力扣297 完全平方数
class Solution {
public:
int numSquares(int n) {
//问最少放多少,初始化为最大值
vector<int>dp(n+1,INT_MAX); //dp[i]表示装满空间为i的背包最少使用dp【i】个
dp[0]=0;
for(int i=1;i*i<=n;i++){
for(int j=i*i;j<=n;j++){
dp[j] = min(dp[j],dp[j-i*i]+1);
}
}
return dp[n];
}
};
83 力扣322 零钱兑换
完全背包,装满有几种装法
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
vector<int>dp(amount+1,INT_MAX);
dp[0]=0;
for(int i=0;i<coins.size();i++){
for(int j=coins[i];j<=amount;j++){ //背包之间的便利从小到大
if(dp[j-coins[i]]!=INT_MAX){
dp[j] = min(dp[j],dp[j-coins[i]]+1);
}
}
}
if(dp[amount]==INT_MAX)return -1;
return dp[amount];
}
};
84 力扣139 单词拆分
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
vector<bool>dp(s.size()+1,false);
unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
dp[0]=true;
for(int j=0;j<=s.size();j++){ //遍历背包 排列对结果有影响
for(int i=0;i<j;i++){ //遍历物品
//截出长度判断是否为字典里的值
string word = s.substr(i,j-i); //(截取位置,截取长度)
//如果字典里有且之前的位置是true,那么现在的位置也是true
if(wordSet.find(word)!=wordSet.end()&&dp[i]==true){
dp[j] = true;
}
}
}
return dp[s.size()];
}
};
85 力扣300 最长递增子序列
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
if(nums.size()<=1)return nums.size();
vector<int>dp(nums.size(),1); //dp[i]表示i之前以i结尾的最长递增子序列长度
int res=0; //记录最大值,因为不一定最后的是最长的
for(int i=1;i<nums.size();i++){
for(int j=0;j<i;j++){
if(nums[i]>nums[j])dp[i] = max(dp[i],dp[j]+1);
}
if(dp[i]>res)res = dp[i];
}
return res;
}
};
86 力扣152 乘积最大子数组
class Solution {
public:
int maxProduct(vector<int>& nums) {
int n = nums.size();
if(n == 0){
return 0;
} else if(n == 1) {
return nums[0];
}
int p = nums[0];
int maxP = nums[0];
int minP = nums[0];
for(int i = 1; i < n; i++) {
int t = maxP;
maxP = max(max(maxP * nums[i], nums[i]), minP *nums[i]);
minP = min(min(t * nums[i], nums[i]), minP * nums[i]);
p = max(maxP, p);
}
return p;
}
};
87 力扣416 分割等和子集
背包为分半判断
class Solution {
public:
bool canPartition(vector<int>& nums) {
int count=0;
for(int i=0;i<nums.size();i++){
count+=nums[i];
}
if(count%2==1){
return false;
}
int target = count/2;
vector<vector<int>>dp(nums.size(),vector<int>(nums.size(),0)); //dp[j]表示背包空间为j的最大价值是dp[j]
for(int i=0;i<nums.size();i++){
for(int j=0;j<=;j++){ //背包的下限得能装下物品空间
dp[i][j] = max(dp[i-1][j],dp[i-1][j-nums[i]]+nums[i]);
}
}
if(dp[nums.size()][target]==target){
return true;
}else{
return false;
}
}
};
——88 力扣32 最长有效括号
class Solution {
public:
int longestValidParentheses(string s) {
int size = s.length();
vector<int> dp(size, 0);
int maxVal = 0;
for(int i = 1; i < size; i++) {
if (s[i] == ')') {
if (s[i - 1] == '(') {
dp[i] = 2;
if (i - 2 >= 0) {
dp[i] = dp[i] + dp[i - 2];
}
} else if (dp[i - 1] > 0) {
if ((i - dp[i - 1] - 1) >= 0 && s[i - dp[i - 1] - 1] == '(') {
dp[i] = dp[i - 1] + 2;
if ((i - dp[i - 1] - 2) >= 0) {
dp[i] = dp[i] + dp[i - dp[i - 1] - 2];
}
}
}
}
maxVal = max(maxVal, dp[i]);
}
return maxVal;
}
};
动态多维规划
89 力扣62 不同路径
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>>dp(m,vector<int>(n,0)); //到(i,j)位置有dp[i,j]种方法
//初始化
for(int i=0;i<m;i++){ //最左侧只有一种走法
dp[i][0]=1;
}
for(int j=0;j<n;j++){ //最上侧只有一种走法
dp[0][j]=1;
}
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
dp[i][j]=dp[i-1][j]+dp[i][j-1]; //动态转移方程
}
}
return dp[m-1][n-1]; //第二行第二列的到达方法有dp[1][1]种
}
};
——90 力扣64 最小路径和
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
if (grid.size() == 0 || grid[0].size() == 0) {
return 0;
}
int rows = grid.size(), columns = grid[0].size();
auto dp = vector < vector <int> > (rows, vector <int> (columns));
dp[0][0] = grid[0][0];
for (int i = 1; i < rows; i++) {
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
for (int j = 1; j < columns; j++) {
dp[0][j] = dp[0][j - 1] + grid[0][j];
}
for (int i = 1; i < rows; i++) {
for (int j = 1; j < columns; j++) {
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
return dp[rows - 1][columns - 1];
}
};
——91 力扣5 最长回文子串
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
if (n < 2) {
return s;
}
int maxLen = 1;
int begin = 0;
// dp[i][j] 表示 s[i..j] 是否是回文串
vector<vector<int>> dp(n, vector<int>(n));
// 初始化:所有长度为 1 的子串都是回文串
for (int i = 0; i < n; i++) {
dp[i][i] = true;
}
// 递推开始
// 先枚举子串长度
for (int L = 2; L <= n; L++) {
// 枚举左边界,左边界的上限设置可以宽松一些
for (int i = 0; i < n; i++) {
// 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
int j = L + i - 1;
// 如果右边界越界,就可以退出当前循环
if (j >= n) {
break;
}
if (s[i] != s[j]) {
dp[i][j] = false;
} else {
if (j - i < 3) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i + 1][j - 1];
}
}
// 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
if (dp[i][j] && j - i + 1 > maxLen) {
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substr(begin, maxLen);
}
};
92 力扣1143 最长公共子序列
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
//以下标i-1结尾的text1 和 以下标j-1结尾的text2 的最长公共子序列为dp[i][j]
vector<vector<int>>dp(text1.size()+1,vector<int>(text2.size()+1,0));
for(int i=1;i<=text1.size();i++){
for(int j=1;j<=text2.size();j++){
if(text1[i-1]==text2[j-1]){
dp[i][j]=dp[i-1][j-1]+1;
}else{
dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
}
}
}
return dp[text1.size()][text2.size()];
}
};
93 力扣72 编辑距离
class Solution {
public:
int minDistance(string word1, string word2) {
//以下标i-1结尾的word1 和 以下标j-1结尾的word2 的最小操作数为dp[i][j]
vector<vector<int>>dp(word1.size()+1,vector<int>(word2.size()+1));
for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;
for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;
for(int i=1;i<=word1.size();i++){
for(int j=1;j<=word2.size();j++){
if(word1[i-1]==word2[j-1]){
dp[i][j] = dp[i-1][j-1]; //相等的情况下什么也不做,向前找
}else{
//增删(增删是相同的,其中一个向前退一步,之后再增加一步操作)换(两个各向前退一步在换)
dp[i][j] = min({dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+1});
}
}
}
return dp[word1.size()][word2.size()];
}
};
技巧
94 力扣136 只出现一次的数字
整个数组异或操作,使用0对整个数组操作
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ret = 0;
for (auto e: nums) ret ^= e;
return ret;
}
};
95 力扣169 多数元素
抵消原则: 在一个数组中,如果某个元素的出现次数超过了数组长度的一半,那么这个元素与其他所有元素一一配对,最后仍然会剩下至少一个该元素。 通过“投票”和“抵消”的过程,可以逐步消除不同的元素,最终留下的候选人就是可能的主要元素。
class Solution {
public:
int majorityElement(vector<int>& nums) {
int x = 0, votes = 0;
for (int num : nums){
if (votes == 0) x = num;
votes += num == x ? 1 : -1;
}
return x;
}
};
96 力扣75 颜色分类
双指针实现
class Solution {
public:
void sortColors(vector<int>& nums) {
int n = nums.size();
int p0 = 0, p1 = 0;
for (int i = 0; i < n; ++i) {
if (nums[i] == 1) {
swap(nums[i], nums[p1]);
++p1;
} else if (nums[i] == 0) {
swap(nums[i], nums[p0]);
if (p0 < p1) {
swap(nums[i], nums[p1]);
}
++p0;
++p1;
}
}
}
};
97 力扣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 (j >= 0 && nums[i] >= nums[j]) {
j--;
}
swap(nums[i], nums[j]);
}
reverse(nums.begin() + i + 1, nums.end());
}
};
98 力扣287 寻找重复数
当作一个链表来看,数组的下标就是指向元素的指针,把数组的元素也看作指针。
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int fast = 0, slow = 0;
while(true){
fast = nums[nums[fast]];
slow = nums[slow];
if(fast == slow)
break;
}
int finder = 0;
while(true){
finder = nums[finder];
slow = nums[slow];
if(slow == finder)
break;
}
return slow;
}
};