Datawhale 阿里云天池LeetCode基础训练营 c++ Code
Datawhale & 阿里云天池LeetCode基础训练营
Task1: 数组
课后习题
1. 删除有序数组中的重复项(Easy)
题意: 给一个有序的数组,其中有若干数是重复出现的,如 [1,1,2,2,2,4,] 删除重复项后为 [1,2,4]。
输入: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的元素
输入: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 的三元组,其中元素各不重复,且返回的三元组也不重复
输入: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)
输入: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)
**题意:**给你两个链表,其中有一个节点往后他们都相同,求该节点
[外链图片转存中…(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次以上的值的节点删除并返回新链表
输入: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)
**题意:**设计一个支持 push
,pop
,top
操作,并能在常数时间内检索到最小元素的栈。
输入:
["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)
题意:‘ # ’ 字符表示退格操作,给你两个字符串,问退格操作后两字符串是否相等
输入:s = "ab#c", t = "ad#c"
输出:true
解释:S 和 T 都会变成 “ac”。
输入: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
,请你实现一个基本计算器来计算并返回它的值。
输入:s = "3+2*2"
输出:7
输入:s = " 3/2 "
输出:1
输入: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
,最多删除一个字符。判断是否能成为回文字符串。
输入: s = "aba"
输出: true
输入: 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
,例如不会出现像 3a
或 2[4]
的输入。
输入: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.二叉树的最小深度
输入: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 = [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
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
输入: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.对称二叉树
由于是镜像对称,所以我们递归时拿根节点的左子树和右子树比较即可
/*** 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.二叉树的中序遍历
中序遍历即:左根右
/*** 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.数组的相对排序
给你两个数组,arr1
和 arr2
,arr2
中的元素各不相同,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
,使用 插入排序 对链表进行排序,并返回 排序后链表的头 。
- 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
- 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
- 重复直到所有输入数据插入完为止。
下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。
偷个懒,排序完再存回链表
/*** 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
。
输入:[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
天的价格。
在每一天,你可能会决定购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以购买它,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
输入: 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
,找出其中最长的回文子序列,并返回该序列的长度。
子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。
输入: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.从前序与中序遍历序列构造二叉树
给定两个整数数组 preorder
和 inorder
,其中 preorder
是二叉树的先序遍历, inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。
输入: 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 = [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"
是不同类型的石头。
输入: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.子域名访问计数
计数配对域名 是遵循 "rep d1.d2.d3"
或 "rep d1.d2"
格式的一个域名表示,其中 rep
表示访问域名的次数,d1.d2.d3
为域名本身。
给你一个 计数配对域名 组成的数组 cpdomains
,解析得到输入中每个子域名对应的 计数配对域名 ,并以数组形式返回。可以按 任意顺序 返回答案。
输入: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相关推荐
- 【组队学习】【34期】阿里云天池在线编程训练营
阿里云天池在线编程训练营 航路开辟者:陈信达.杨世超.赵子一.马燕鹏 领航员:武帅.初晓宇.叶前坤.邱广坤.朱松青 航海士:宁彦吉.肖桐.汪超.陈信达.杨世超.赵子一.武帅.初晓宇.叶前坤.邱广坤.朱 ...
- 阿里云天池 金融风控训练营Task1 广东工业站
Task1 赛题理解 一.学习知识点概要 本次学习先是介绍了赛题的背景和概况,题目以金融风控中的个人信贷为背景,给所给的47列特征中,根据贷款申请人的数据信息预测其是否有违约的可能,以此判断是否通过 ...
- Leetcode学习成长记:天池leetcode基础训练营Task02链表
Task02 链表 知识点总结 单链表 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素.即逻辑上位置相邻的元素其物理位置不一定相邻. 链表中的数据是以结点来表示的,每个 ...
- 阿里云天池 零基础入门NLP - 新闻文本分类 2种做法,F1=0.87
problem 1.赛题理解 数据集: 在NLP_data_list_0715.csv中,有三个链接. 分别可以下载训练集,测试集A,测试样例. f1_score介绍: F1分数(F1-score)是 ...
- 阿里云天池 Python训练营Task3: Python基础进阶:从函数到高级魔法方法 学习笔记
本学习笔记为阿里云天池龙珠计划Python训练营的学习内容,学习链接为:https://tianchi.aliyun.com/specials/promotion/aicamppython?spm=5 ...
- 阿里云天池 Python训练营Task2: Python基础练习:数据结构大汇总 学习笔记
本学习笔记为阿里云天池龙珠计划Python训练营的学习内容,学习链接为:https://tianchi.aliyun.com/specials/promotion/aicamppython?spm=5 ...
- 阿里云天池 Python训练营Task1:从变量到异常处理
本学习笔记为阿里云天池龙珠计划Python训练营的学习内容,学习链接为:https://tianchi.aliyun.com/specials/promotion/aicamppython?spm=5 ...
- 阿里云天池SQL训练营学习记录
SQL训练营任务 打卡目录 SQL训练营任务 前言 一.Task01打卡 二.Task02打卡 三.Task03打卡 总结 一.算术函数 二.字符串函数 三.日期函数 四.转换函数 五.LIKE谓词 ...
- 阿里云天池SQL训练营task6笔记
本笔记为阿里云天池龙珠计划SQL训练营的学习内容,链接为: Task06:综合练习题-10道经典题目-天池龙珠计划SQL训练营-天池技术圈-天池技术讨论区 (aliyun.com) 如果你 ...
最新文章
- vip能ping通,但80不通的解决方法
- java数据结构库函数_Java8 内置函数(api)总结
- Linux学习之CentOS(十二)--crontab命令的使用方法
- 如何将mysql的数据库渲染到页面_vue.js实现数据库的JSON数据输出渲染到html页面功能示例...
- 操作系统是计算机的什么管理者,操作系统是计算机资源的管理者
- 浅谈点击信号对搜索的影响
- 信息学奥赛一本通(1093:计算多项式的值)
- 【opencv有趣应用】基于MobileNet + SSD的物体检测
- 【英语学习】【WOTD】countermand 释义/词源/示例
- php 会话 写入浏览器,创建PHP会话变量会挂起我的浏览器
- Web信息收集,互联网上的裸奔者
- 看看故障诊断文献中的故障设置方法-中文论文篇
- Chinese Whisper 人脸聚类算法实现
- 极客战记计算机科学2村庄守卫,「网易官方」极客战记(codecombat)攻略-森林-村庄守护神-village-champion...
- clusters(clusters)
- ShareSDK QQ平台注册
- 【模电】0016 线性稳压电源的几个实用电路
- 2020-10《信息资源管理 02378》真卷(独家文字版),圈定章节考点+统计真题分布
- 2021.09.27-10.3 AI行业周刊(第65期):坚持的力量
- 2020,中国互联网的后高光时刻
热门文章
- OSChina 周四乱弹 ——没有我,你要记得快乐!
- 我国航空项目管理的演变与发展(转)
- 手机 download .cu .log_Edward H. Sargent教授Nature子刊:氢氧化物调节Cu上吸附氢,促进CO2电还原制乙醇...
- 定义一个包含十个元素的数组
- C++实例(十)Word文档操作
- 存量、增量、留存在产品运营中是如何定义的,看完恍然大悟
- 高效提升计算质量!瑞典量子计算机首次应用于化学
- 基于文本挖掘的游客对古镇旅游态度的分析
- python中英文半角还是全角_Python实现全角半角转换的方法
- 欧几里得算法 详细证明