Datawhale & 阿里云天池LeetCode基础训练营

Task1: 数组

课后习题

1. 删除有序数组中的重复项(Easy)

题意: 给一个有序的数组,其中有若干数是重复出现的,如 [1,1,2,2,2,4,] 删除重复项后为 [1,2,4]。

现要求你将重复项删除,并返回新的数组长度。

示例 1:

输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

STL 做法

class Solution {public:int removeDuplicates(vector<int>& nums) {auto last = unique(nums.begin(),nums.end());nums.erase(last,nums.end());return nums.size();}
};

我们每次将重复的个数统计起来,然后前移cnt个位置,就能保证前面的值都是唯一的了

class Solution {public:int removeDuplicates(vector<int>& nums) {int len = nums.size(), cnt = 0;for(int i = 1; i < nums.size(); i++){if(nums[i] == nums[i-1]){cnt++;len--;}else{nums[i-cnt] = nums[i];}}return len;}
};

2. 移除元素(Easy)

**题意:**给你一个数组和值val,从数组中移除所有等于val的元素

示例 1:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

和上题一样的思路,这里不再赘述

class Solution {public:int removeElement(vector<int>& nums, int val) {int len = nums.size();int cnt = 0;for(int i = 0; i < nums.size(); i++){if(val == nums[i]){len--;cnt++;continue;}nums[i-cnt] = nums[i];}return len;}
};

3. 三数之和(Medium)

**题意:**给定一数组,按任意顺序返回三数之和为0 的三元组,其中元素各不重复,且返回的三元组也不重复

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

这道题的难点即在于三元组判重,通过观察可发现,若要三元组不重复,其实就是使三元组的前两个元素不重复出现,

熟悉STL的朋友可以使用set<pair<int,int>> 来判重,然后第三个元素我们通过二分来查找即可

不过Set判重的复杂度也不低,可以优化一下做法:

考虑到我们将数组排序后,元素皆有序,那么问题就变成了有序数组中寻找所有不重复的二元组问题

显然,二元组的第一个元素不枚举重复的,(否则必然导致重复答案)第二个元素也是(同理)。


我实在是想不通为什么我的二分比他们枚举还慢这么多,如果有知道的大佬劳请指点一二

STL做法

class Solution {public:vector<vector<int>> threeSum(vector<int>& nums) {int n = nums.size();if(n < 3) return {};sort(nums.begin(),nums.end());set<pair<int,int>> st;vector<vector<int>> ans;for(int i = 0; i < n; i++){if(nums[i] > 0) break;for(int j = i+1; j < n; j++){int l = j + 1, r = n - 1;while(l < r){int mid = (l+r)/2;if(nums[i] + nums[j] + nums[mid] == 0){l = mid;break;}else if(nums[i] + nums[j] + nums[mid] > 0) r = mid-1;else l = mid+1;}if(l < n && nums[i] + nums[j] + nums[l] == 0){if(st.find(make_pair(nums[i],nums[j])) == st.end()){ans.push_back(vector{nums[i],nums[j],nums[l]});st.insert(make_pair(nums[i],nums[j]));}}}}return ans;}
};

优化版:

class Solution {public:vector<vector<int>> threeSum(vector<int>& nums) {int n = nums.size(); if(n < 3) return {};sort(nums.begin(),nums.end());vector<vector<int>> ans;for(int i = 0; i < n; i++){if(i > 0 && nums[i] == nums[i-1]) continue;//第一个位置判重for(int j = i+1; j < n; j++){if(j > i+1 && nums[j] == nums[j-1]) continue;//第二个位置判重int l = j+1, r = n-1;while(l < r){int mid = (l+r)/2;if(nums[i] + nums[j] + nums[mid] == 0){l = mid;break;}else if(nums[i] + nums[j] + nums[mid] > 0) r = mid-1;else l = mid+1;}if(l < n && nums[i] + nums[j] + nums[l] == 0){ans.push_back(vector{nums[i],nums[j],nums[l]});}}}return ans;}
};

Task2:链表

课后习题

1.合并两个有序链表(Easy)

**题意:**给你两个有序的链表,合成一个有序的新链表

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

思路:递归返回或者迭代合并再返回

递归版:

/*** 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* 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;}}
};

2.相交链表(Easy)

**题意:**给你两个链表,其中有一个节点往后他们都相同,求该节点

示例 2:

[外链图片转存中…(img-sz5pZ068-1645112683925)]

如该处的相交处为2这个节点

思路:直接开哈希表记录结点即可

class Solution {public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {ListNode *t = headA;unordered_set< ListNode* > st;while(t){st.insert(t);t = t->next;}t = headB;while(t){if(st.count(t)) return t;t = t->next;}return nullptr;}
};

3.删除排序链表中的重复元素 II(Medium)

**题意:**将链表中所有出现1次以上的值的节点删除并返回新链表

示例 1:

输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]

思路:前后指针(或者叫左右指针), 首先用一个指针指向前指针,然后比较前后指针的值是否相等,是则一直移动后指针,直到不相等。然后更新左右指针,继续下次循环。

/*** 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* deleteDuplicates(ListNode* head) {if (!head) return nullptr;ListNode *prev = new ListNode(-123);ListNode *cur = prev;while (head) {int cnt = 0;while (head->next && head->val == head->next->val) {cnt++;head = head->next;} // 循环结束后,head 的值与下个节点的值不一样if (cnt == 0) {cur->next = new ListNode(head->val);cur = cur->next;}head = head->next;}return prev->next;}
};

Task3:栈

课后习题

1.最小栈(Easy)

**题意:**设计一个支持 pushpoptop 操作,并能在常数时间内检索到最小元素的栈。

示例:

输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]输出:
[null,null,null,null,-3,null,0,-2]解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2.

利用另外一个栈来保存前缀最小值即可,这里便不模拟栈了,直接使用stl内的栈

class MinStack {public:stack<int> stk,min_stk;MinStack() {min_stk.push(INT_MAX);}void push(int val) {stk.push(val);min_stk.push(min( min_stk.top(), val));}void pop() {stk.pop();min_stk.pop();}int top() {return stk.top();}int getMin() {return min_stk.top();}
};/*** 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();*/

2.比较含退格的字符串(Easy)

题意:‘ # ’ 字符表示退格操作,给你两个字符串,问退格操作后两字符串是否相等

示例 1:

输入:s = "ab#c", t = "ad#c"
输出:true
解释:S 和 T 都会变成 “ac”。

示例 2:

输入:s = "ab##", t = "c#d#"
输出:true
解释:s 和 t 都会变成 “”。

遇到字母就入栈,遇到# 就退栈,最后一次比较每个出栈的字符即可

class Solution {public:bool backspaceCompare(string s, string t) {stack<int> s_stk, t_stk;for(auto &c : s){if(c != '#'){s_stk.push(c);}else{if(!s_stk.empty()){s_stk.pop();}}}for(auto &c : t){if(c != '#'){t_stk.push(c);}else{if(!t_stk.empty()){t_stk.pop();}}}while(!s_stk.empty() && !t_stk.empty()){if(s_stk.top() != t_stk.top()) return false;s_stk.pop();t_stk.pop();}return s_stk.empty() && t_stk.empty();}
};

3.基本计算器 II(Medium)

**题意:**给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

整数除法仅保留整数部分。

示例 1:

输入:s = "3+2*2"
输出:7

示例 2:

输入:s = " 3/2 "
输出:1

示例 3:

输入:s = " 3+5 / 2 "
输出:5

由于这里的运算比较简单,我们将其转化为一个加法栈,最后求和即可。

我们来看看每组数字前面的运算符

加号 就直接把数字入栈

减号 就把相反数入栈

乘除 就把该组数字与栈顶的数字相运算即可。

最后对加法栈进行求和就是答案。

class Solution {public:int calculate(string s) {stack<int> stk;int val = 0;char pre_op = '+';int len = s.length();for (int i = 0; i < len; ++i) {if (isdigit(s[i])) {val = val * 10 + (s[i] - '0');}if (!isdigit(s[i]) && s[i] != ' ' || i == len - 1) {//防止除0和读完数字的情况switch (pre_op) {case '+':{stk.push(val);break;}case '-':{stk.push(-val);break;}case '*':{stk.top() *= val;break;}case '/':{stk.top() /= val;break;}}pre_op = s[i];val = 0;}}int ans = 0;while(!stk.empty()){ans += stk.top();stk.pop();}return ans;}
};

Task4:字符串

课后习题

1.验证回文字符串 Ⅱ

给定一个非空字符串 s最多删除一个字符。判断是否能成为回文字符串。

示例 1:

输入: s = "aba"
输出: true

示例 2:

输入: s = "abca"
输出: true
解释: 你可以删除c字符。

又由于回问串的特殊性质(回文串的子串也是回文串),所以我们可以从字符串两端开始判断,若此时两端字符不同,则分别判断去掉左右字符后是否回文即可。

class Solution {public:bool check(int l, int r, string s){int i = l, j = r;while (i < j){if (s[i] != s[j])   return false;i ++, j --;}return true;}bool validPalindrome(string s) {int n = s.length();int l = 0, r = n - 1;while (l < r){if (s[l] == s[r])   l ++, r --;else{l ++;if (check(l, r, s))    return true;l--, r--;return check(l, r, s);}}return true;}
};

2.Excel表列名称

整数 columnNumber ,返回它在 Excel 表中相对应的列名称。

例如:

A -> 1
B -> 2
C -> 3
...
Z -> 26
AA -> 27
AB -> 28
...

容易看出是一个10进制转26进制的问题,直接处理即可(按权展开)

class Solution {public:string convertToTitle(int columnNumber) {string ans;while (columnNumber > 0) {int a0 = (columnNumber - 1) % 26 + 1;ans += a0 - 1 + 'A';columnNumber = (columnNumber - a0) / 26;}reverse(ans.begin(), ans.end());return ans;}
};

3.字符串解码

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a2[4] 的输入。

示例 1:

输入:s = "3[a]2[bc]"
输出:"aaabcbc"

记录括号前的数字值,然后循环将括号内的数字复制即可

class Solution {public:string decodeString(string s) {stack<char> sta;for(auto ch : s){if(ch != ']')sta.push(ch);else{stack<char> tmp;while(!sta.empty() && sta.top() != '['){tmp.push(sta.top());sta.pop();}sta.pop();//找到它前面的倍数string number;while(!sta.empty() && sta.top() >= '0' && sta.top() <= '9'){number += sta.top();sta.pop();}reverse(number.begin(),number.end());int nth = stoi(number);for(int i = 0; i < nth;++i){auto current = tmp;while(!current.empty()){sta.push(current.top());current.pop();}}}}string res;while(!sta.empty()){res += sta.top();sta.pop();}reverse(res.begin(),res.end());return res;}
};

Task5:树

课后习题

1.二叉树的最小深度

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

**说明:**叶子节点是指没有子节点的节点。

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:2

思路:

深搜,每次在左右子树之间选择一个深度最低的值。

若根为空,则最小深度为0,否则初始化根的深度为INT_MAX 然后再进行深搜

/*** 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:int dfs(TreeNode *root){if(!root) return INT_MAX;if(root->left == nullptr && root->right == nullptr) return 1;return min(dfs(root->left), dfs(root->right)) + 1;}int minDepth(TreeNode* root) {if(!root) return 0;return dfs(root);}
};

2.路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false

叶子节点 是指没有子节点的节点。

示例 1:

输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

思路:

深搜:若当前节点为叶子节点且值等于targetsum,返回true

否则,每次往下搜一层,目标值减去当前节点的值

/*** 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:bool hasPathSum(TreeNode* root, int targetSum) {if(!root) return false;if(!root->left && !root->right && targetSum == root->val) return true;return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val); }
};

3.二叉搜索树迭代器

题意:类似于设计一个java的迭代器,只不过这里的迭代器是按中序遍历的顺序来迭代

思路,由于二叉树可以通过链式结构转为线性结构,所以我们按中序遍历将值存到数组里面

然后便可轻松判断hasNext()和next()

/*** 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 BSTIterator {public:BSTIterator(TreeNode* root) {idx = 0;to_arr(root, v);}void to_arr(TreeNode *root, vector<int> &vv){if(!root) return ;to_arr(root->left, vv);vv.push_back(root->val);to_arr(root->right, vv);}int next() {return v[idx++];}bool hasNext() {return idx != v.size();}vector<int> v;int idx;
};/*** Your BSTIterator object will be instantiated and called as such:* BSTIterator* obj = new BSTIterator(root);* int param_1 = obj->next();* bool param_2 = obj->hasNext();*/

Task6:位运算

课后习题:

1.2 的幂

给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false

如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。

思路:

由于计算机采用2进制,所以我们可以根据二进制的特点来进行计算

因为2的幂在2进制的表示下为 10000… ,所以判断一个数是否是2的幂只需判断其除了高位外是否都是0即可

因为 1 & 0 = 1 ,所以我们可以通过 n &(n-1) == 0 即可判断是否低位都是0

class Solution {public:bool isPowerOfTwo(int n) {return n > 0 && (n & (n-1)) == 0;}
};

2.只出现一次的数字 II

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 **三次 。**请你找出并返回那个只出现了一次的元素。

思路:

由于每个数字只出现三次,所以我们可以通过枚举每一位二进制值,若该位的二进制值和为3的整数倍,则说明答案值该位为0,否则为1,数据范围在 int32位以下,所以我们枚举32位即可。

或运算的性质就类似于 按位不进位加法 , 因为 1 | 0 = 1 ,1 | 1 = 1,所以我们对答案求和时就可用或来按位求和

class Solution {public:int singleNumber(vector<int>& nums) {int ans = 0;for(int i = 0; i < 32; i++){int val = 0;for(auto &c : nums){val += (c >> i) & 1;}if(val % 3){ans |= 1 << i;}}return ans;}
};

3.子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

思路:

这里我们使用状态枚举即可,由于二进制的性质,每一位上不是0就是1,所以我们可以通过枚举 1 - 2^n的值来选择每个值,0一般被用于表示不选,1则表示选。

class Solution {public:vector<vector<int>> subsets(vector<int>& nums) {int n = nums.size();vector<vector<int>> res;for(int i = 0; i < (1 << n); i++){int t = i;vector<int> temp;for(int j = 0; j < n; j++){if((t >> j) & 1){temp.push_back(nums[j]);}}res.push_back(temp);}return res;}
};

Task7: 双指针

课后习题

1.删除排序链表中的重复元素

题意:将排序链表中重复出现的多余的元素删除,使其只出现一次

若下一个节点不空且相同,则一直更新结点即可

/*** 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* deleteDuplicates(ListNode* head) {if (!head) return nullptr;ListNode *prev = new ListNode(-123);ListNode *cur = prev;while (head) {while (head->next && head->val == head->next->val) {head = head->next;}cur->next = new ListNode(head->val);cur = cur->next;head = head->next;}return prev->next;}
};

2.环形链表

题意:判断该链表是否有环

set判重即可

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {public:bool hasCycle(ListNode *head) {set< ListNode* > st;ListNode *temp = head;while(temp){if(st.count(temp)) return true;st.insert(temp);temp = temp->next;}return false;}
};

3.排序链表

题意:将给定链表排序并返回排序后的链表

将所有值保存并排序再回到链表全部替换顺序即可

/*** 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* sortList(ListNode* head) {priority_queue<int,vector<int>,greater<int>> que;auto temp = head;while(temp){que.push(temp->val);temp = temp->next;}temp = head;while(temp){temp->val = que.top();que.pop();temp = temp->next;}return head;}
};

Task8:搜索

课后习题

1.对称二叉树

给你一个二叉树的根节点 root , 检查它是否轴对称。

示例 1:

由于是镜像对称,所以我们递归时拿根节点的左子树和右子树比较即可

/*** 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:bool search(TreeNode *l, TreeNode *r){if(!l && !r) return true;if(!l || !r || l->val != r->val) return false;return search(l->left, r->right) && search(l->right, r->left);}bool isSymmetric(TreeNode* root) {return search(root, root);}
};

2.二叉树的中序遍历

给定一个二叉树的根节点 root ,返回它的 中序 遍历。

中序遍历即:左根右

/*** 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:void search(TreeNode *root){if( root == NULL ) return ;search(root->left);res.push_back(root->val);search(root->right);}vector<int> inorderTraversal(TreeNode* root) {res.clear();search(root);return res;}vector<int> res;
};

3.二叉搜索树中第K小的元素

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。

由于二叉树搜索树的特征就是左子节点值小于根节点,右子节点值大于根节点,那么我们按照中序遍历即可知道第k小的元素

/*** 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:void inorder(TreeNode *root, vector<int>& ans){if(!root) return ;inorder(root->left, ans);ans.emplace_back(root->val);inorder(root->right, ans);}int kthSmallest(TreeNode* root, int k) {vector<int> ans;inorder(root, ans);return ans[k-1];}
};

Task9:排序

课后习题

1.有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

求完平方再排序

class Solution {public:vector<int> sortedSquares(vector<int>& nums) {vector<int> res;for(auto n : nums){res.emplace_back(n*n);}sort(begin(res),end(res));return res;}
};

2.数组的相对排序

给你两个数组,arr1arr2arr2 中的元素各不相同,arr2 中的每个元素都出现在 arr1 中。

arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。未在 arr2 中出现过的元素需要按照升序放在 arr1 的末尾。

用pair记录相对位置,重新排序即可,未出现的数字权值可以设置成索引下标值

class Solution {public:vector<int> relativeSortArray(vector<int>& arr1, vector<int>& arr2) {map<int,int> mp;for(int i = 0; i < arr2.size(); i++){mp[arr2[i]] = i;}sort(begin(arr1), end(arr1),[&](int a, int b){if(mp.find(a) == mp.end() && mp.find(b) == mp.end()){return a < b;}if(mp.find(a) == mp.end()) return false;if(mp.find(b) == mp.end()) return true;return mp[a] < mp[b];});vector<int> ans = arr1;return ans;}
};

3. 对链表进行插入排序

给定单个链表的头 head ,使用 插入排序 对链表进行排序,并返回 排序后链表的头

插入排序 算法的步骤:

  1. 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
  2. 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
  3. 重复直到所有输入数据插入完为止。

下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。

对链表进行插入排序。

偷个懒,排序完再存回链表

/*** 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:void insert_sort(vector<int> &v){int i, j, temp;for(i = 1; i < v.size(); i++){temp = v[i];for(j = i -1; j >= 0 && v[j] > temp; j--){v[j+1] = v[j];}v[j+1] = temp; }}ListNode* insertionSortList(ListNode* head) {ListNode *t = head;vector<int> v;while(t){v.push_back(t->val);t = t->next;}insert_sort(v);reverse(begin(v), end(v));t = head;while(t){t->val = v.back();v.pop_back();t = t->next;}return head;}
};

课后习题

1.买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

记录第i天前的最低价格,然后在第i天卖出的价格,取最大值就是答案

class Solution {public:int maxProfit(vector<int>& prices) {int n = prices.size();if(n <= 1) return 0;int min_price = INT_MAX, max_ans = 0;for(int i = 0; i < n; i++){if(prices[i] < min_price) min_price = prices[i];else max_ans = max(max_ans, prices[i] - min_price);}return max_ans;}
};

2.买卖股票的最佳时机 II

给定一个数组 prices ,其中 prices[i] 表示股票第 i 天的价格。

在每一天,你可能会决定购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以购买它,然后在 同一天 出售。
返回 你能获得的 最大 利润

示例 1:

输入: prices = [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

每天有两个状态,持有一股或零股,每天又是一个状态,所以我们我们设

dp[i][0]为第i天持有0股股票所能获得的最大利润dp[i][0] 为第i天持有0股 股票所能获得的最大利润dp[i][0]为第i天持有0股股票所能获得的最大利润​

dp[i][1]为第i天持有1股股票所能获得的最大利润dp[i][1] 为第i天持有1股 股票所能获得的最大利润dp[i][1]为第i天持有1股股票所能获得的最大利润

那么 dp[i][0]就可能由dp[i−1][0]和dp[i−1][1]+prices[i]转移而来dp[i][0] 就可能由dp[i-1][0] 和 dp[i-1][1] + prices[i] 转移而来dp[i][0]就可能由dp[i−1][0]和dp[i−1][1]+prices[i]转移而来

(这里表示我们前一天的股票还是0股,和 前一天有1股,但今天卖掉了,所以是 + prices[i])

dp[i][1]则是由dp[i−1][1]和dp[i−1][0]−prices[i]dp[i][1] 则是由 dp[i-1][1] 和 dp[i-1][0] - prices[i]dp[i][1]则是由dp[i−1][1]和dp[i−1][0]−prices[i] 转移而来

初始状态:dp[0][0]=0,dp[0][1]=−prices[0]dp[0][0] = 0, dp[0][1] = -prices[0]dp[0][0]=0,dp[0][1]=−prices[0]

class Solution {public:int maxProfit(vector<int>& prices) {int n = prices.size();vector<vector<int>> dp(n,vector<int>(2));dp[0][0] = 0, dp[0][1] = -prices[0];for(int i = 1; i < n; i++){dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]);}return dp[n-1][0];}
};

3.最长回文子序列给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

示例 1:

输入:s = "bbbab"
输出:4
解释:一个可能的最长回文子序列为 "bbbb" 。

因为回文序列具有和dp一样的子结构特性,所以我们可以设 dp[i][j]为第i个位置到第j个位置的最长回文子序列长度dp[i][j] 为 第i个位置到第j个位置的最长回文子序列长度dp[i][j]为第i个位置到第j个位置的最长回文子序列长度​

则d[i][i]=1d[i][i] = 1d[i][i]=1 (单个字符就是一个回文串)

那么当 s[i]==s[j]时,dp[i][j]应等于dp[i+1][j−1]+2s[i] == s[j] 时, dp[i][j] 应等于 dp[i+1][j-1] + 2s[i]==s[j]时,dp[i][j]应等于dp[i+1][j−1]+2

而当 s[i]!=s[j]时,dp[i][j]应等于max(dp[i+1][j],dp[i][j−1])s[i] != s[j] 时,dp[i][j]应等于max(dp[i+1][j], dp[i][j-1])s[i]!=s[j]时,dp[i][j]应等于max(dp[i+1][j],dp[i][j−1])

因为我们要求整个字符串的最长回文子序列,所以最后答案应该是dp[0][n−1]dp[0][n-1]dp[0][n−1]

那么我们两重循环,外层从后往前计算即可

class Solution {public:int longestPalindromeSubseq(string s) {int n = s.length();vector<vector<int>> dp(n,vector<int>(n));for(int i = n-1; i >= 0; i--){dp[i][i] = 1;for(int j = i+1; j < n; j++){if(s[i] != s[j]){dp[i][j] = max(dp[i+1][j], dp[i][j-1]);}else{dp[i][j] = dp[i+1][j-1] + 2;}}}return dp[0][n-1];}
};

Task11:分治

课后习题

1. 多数元素

给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

根据题意可知排序后这个元素一定是位于数组中间的,所以我们排序后取中间的哪个值即可

class Solution {public:int majorityElement(vector<int>& nums) {sort(nums.begin(), nums.end());return nums[nums.size()/2];}
};

2.最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

dp:由于要求子数组连续,故我们可以在子数组变小时将新的子数组和 从当前值开始求和

然后求解最大值即可

class Solution {public:int maxSubArray(vector<int>& nums) {int ans = nums[0], n = nums.size(), sum = 0;for(int i = 0; i < n; i++){sum = max(sum + nums[i], nums[i]);ans = max(ans, sum);}return ans;}
};

3.从前序与中序遍历序列构造二叉树

给定两个整数数组 preorderinorder ,其中 preorder 是二叉树的先序遍历inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

示例 1:

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]

因为前序的第一个节点就是根节点,故可以在中序节点中找到根节点,然后划分成左右子树部分,然后先对左子树继续进行寻根,一直把所以节点找完,然后就是我们的二叉树了

class Solution {public:TreeNode* recurTree(int leftin,int rightin,int leftpre,int rightpre,vector<int>&preorder, vector<int>&inorder){if(leftpre>rightpre || leftin>rightin){return nullptr;}TreeNode* root=new TreeNode(preorder[leftpre]);int k;for(int i=leftin;i<inorder.size();i++){if(inorder[i]==root->val){k=i;break;}}root->left=recurTree(leftin,k-1,leftpre+1,leftpre+(k-leftin),preorder,inorder);root->right=recurTree(k+1,rightin,leftpre+(k-leftin)+1,rightpre,preorder,inorder);return root;}TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {return recurTree(0,inorder.size()-1,0,preorder.size()-1,preorder,inorder);}
};

Task12:哈希表

课后习题

1.存在重复元素 II

给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个 不同的索引 ij ,满足 nums[i] == nums[j]abs(i - j) <= k 。如果存在,返回 true ;否则,返回 false

示例 1:

输入:nums = [1,2,3,1], k = 3
输出:true

哈希表记录每个值的位置,然后判断即可

class Solution {public:bool containsNearbyDuplicate(vector<int>& nums, int k) {unordered_map<int,int> ump;for(int i = 0; i < nums.size(); i++){int n = nums[i];if(ump.count(n) && i - ump[n] <= k) return true;ump[n] = i;}return false;}
};

2.宝石与石头

给你一个字符串 jewels 代表石头中宝石的类型,另有一个字符串 stones 代表你拥有的石头。 stones 中每个字符代表了一种你拥有的石头的类型,你想知道你拥有的石头中有多少是宝石。

字母区分大小写,因此 "a""A" 是不同类型的石头。

示例 1:

输入:jewels = "aA", stones = "aAAbbbb"
输出:3

哈希表记录哪些石头是宝石,然后判断石头是否是宝石之一即可

class Solution {public:int numJewelsInStones(string jewels, string stones) {unordered_map<char,int> ump;int ans = 0;for(auto &c : jewels){ump[c] = 1;}for(auto &c : stones){if(ump.count(c)){ans++;}}return ans;}
};

3.子域名访问计数

网站域名 "discuss.leetcode.com" 由多个子域名组成。顶级域名为 "com" ,二级域名为 "leetcode.com" ,最低一级为 "discuss.leetcode.com" 。当访问域名 "discuss.leetcode.com" 时,同时也会隐式访问其父域名 "leetcode.com"以及 "com"

计数配对域名 是遵循 "rep d1.d2.d3""rep d1.d2" 格式的一个域名表示,其中 rep 表示访问域名的次数,d1.d2.d3 为域名本身。

给你一个 计数配对域名 组成的数组 cpdomains ,解析得到输入中每个子域名对应的 计数配对域名 ,并以数组形式返回。可以按 任意顺序 返回答案。

示例 1:

输入:cpdomains = ["9001 discuss.leetcode.com"]
输出:["9001 leetcode.com","9001 discuss.leetcode.com","9001 com"]
解释:例子中仅包含一个网站域名:"discuss.leetcode.com"。
按照前文描述,子域名 "leetcode.com" 和 "com" 都会被访问,所以它们都被访问了 9001 次。

其实就是统计每个串出现了多少次,同样哈希记录求和即可,最后按任意顺序输出

tips:注意域名可能是d2.d3这种情况即可

class Solution {public:vector<string> subdomainVisits(vector<string>& cpdomains) {vector<string> res;unordered_map<string,int> ump;for(auto &s : cpdomains){string num, domain, d2, d3;for(int i = 0; i < s.length(); i++){if(s[i] == ' '){num = s.substr(0,i);domain = s.substr(i+1);break;}}int cnt = 1;for(int i = 0; i < domain.length(); i++){if(domain[i] == '.'){if(cnt == 1){cnt++;d2 = domain.substr(i+1);}else{cnt++;d3 = domain.substr(i+1);break;}}}ump[string(" " + domain)] += stoi(num);ump[string(" " + d2)] += stoi(num);if(cnt == 3) ump[string(" " + d3)] += stoi(num);}for(auto &[k,v] : ump){res.push_back(string(to_string(v) + k));}return res;}
};

Datawhale 阿里云天池LeetCode基础训练营 c++ Code相关推荐

  1. 【组队学习】【34期】阿里云天池在线编程训练营

    阿里云天池在线编程训练营 航路开辟者:陈信达.杨世超.赵子一.马燕鹏 领航员:武帅.初晓宇.叶前坤.邱广坤.朱松青 航海士:宁彦吉.肖桐.汪超.陈信达.杨世超.赵子一.武帅.初晓宇.叶前坤.邱广坤.朱 ...

  2. 阿里云天池 金融风控训练营Task1 广东工业站

    Task1  赛题理解 一.学习知识点概要 本次学习先是介绍了赛题的背景和概况,题目以金融风控中的个人信贷为背景,给所给的47列特征中,根据贷款申请人的数据信息预测其是否有违约的可能,以此判断是否通过 ...

  3. Leetcode学习成长记:天池leetcode基础训练营Task02链表

    Task02 链表 知识点总结 单链表 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素.即逻辑上位置相邻的元素其物理位置不一定相邻. 链表中的数据是以结点来表示的,每个 ...

  4. 阿里云天池 零基础入门NLP - 新闻文本分类 2种做法,F1=0.87

    problem 1.赛题理解 数据集: 在NLP_data_list_0715.csv中,有三个链接. 分别可以下载训练集,测试集A,测试样例. f1_score介绍: F1分数(F1-score)是 ...

  5. 阿里云天池 Python训练营Task3: Python基础进阶:从函数到高级魔法方法 学习笔记

    本学习笔记为阿里云天池龙珠计划Python训练营的学习内容,学习链接为:https://tianchi.aliyun.com/specials/promotion/aicamppython?spm=5 ...

  6. 阿里云天池 Python训练营Task2: Python基础练习:数据结构大汇总 学习笔记

    本学习笔记为阿里云天池龙珠计划Python训练营的学习内容,学习链接为:https://tianchi.aliyun.com/specials/promotion/aicamppython?spm=5 ...

  7. 阿里云天池 Python训练营Task1:从变量到异常处理

    本学习笔记为阿里云天池龙珠计划Python训练营的学习内容,学习链接为:https://tianchi.aliyun.com/specials/promotion/aicamppython?spm=5 ...

  8. 阿里云天池SQL训练营学习记录

    SQL训练营任务 打卡目录 SQL训练营任务 前言 一.Task01打卡 二.Task02打卡 三.Task03打卡 总结 一.算术函数 二.字符串函数 三.日期函数 四.转换函数 五.LIKE谓词 ...

  9. 阿里云天池SQL训练营task6笔记

    本笔记为阿里云天池龙珠计划SQL训练营的学习内容,链接为: ​​​​​​Task06:综合练习题-10道经典题目-天池龙珠计划SQL训练营-天池技术圈-天池技术讨论区 (aliyun.com) 如果你 ...

最新文章

  1. vip能ping通,但80不通的解决方法
  2. java数据结构库函数_Java8 内置函数(api)总结
  3. Linux学习之CentOS(十二)--crontab命令的使用方法
  4. 如何将mysql的数据库渲染到页面_vue.js实现数据库的JSON数据输出渲染到html页面功能示例...
  5. 操作系统是计算机的什么管理者,操作系统是计算机资源的管理者
  6. 浅谈点击信号对搜索的影响
  7. 信息学奥赛一本通(1093:计算多项式的值)
  8. 【opencv有趣应用】基于MobileNet + SSD的物体检测
  9. 【英语学习】【WOTD】countermand 释义/词源/示例
  10. php 会话 写入浏览器,创建PHP会话变量会挂起我的浏览器
  11. Web信息收集,互联网上的裸奔者
  12. 看看故障诊断文献中的故障设置方法-中文论文篇
  13. Chinese Whisper 人脸聚类算法实现
  14. 极客战记计算机科学2村庄守卫,「网易官方」极客战记(codecombat)攻略-森林-村庄守护神-village-champion...
  15. clusters(clusters)
  16. ShareSDK QQ平台注册
  17. 【模电】0016 线性稳压电源的几个实用电路
  18. 2020-10《信息资源管理 02378》真卷(独家文字版),圈定章节考点+统计真题分布
  19. 2021.09.27-10.3 AI行业周刊(第65期):坚持的力量
  20. 2020,中国互联网的后高光时刻

热门文章

  1. OSChina 周四乱弹 ——没有我,你要记得快乐!
  2. 我国航空项目管理的演变与发展(转)
  3. 手机 download .cu .log_Edward H. Sargent教授Nature子刊:氢氧化物调节Cu上吸附氢,促进CO2电还原制乙醇...
  4. 定义一个包含十个元素的数组
  5. C++实例(十)Word文档操作
  6. 存量、增量、留存在产品运营中是如何定义的,看完恍然大悟
  7. 高效提升计算质量!瑞典量子计算机首次应用于化学
  8. 基于文本挖掘的游客对古镇旅游态度的分析
  9. python中英文半角还是全角_Python实现全角半角转换的方法
  10. 欧几里得算法 详细证明