**刷题目的: 熟练使用c++语法、STL组件的用法 **
count: 13
索引 |
---|
1. 两数之和 |
15. 三数之和 |
16. 最接近的三数之和 |
589. N叉树的前序遍历 |
47. 全排列 II |
124. 二叉树中的最大路径和 |
543. 二叉树的直径 |
448. 找到所有数组中消失的数字 |
283. 移动零 |
461. 汉明距离 |
17. 电话号码的字母组合 |
82. 删除排序链表中的重复元素 II |
10. 正则表达式匹配 |
------------------------index-----------------------
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { vector<int> ret; unordered_map<int,int>pos_map; for (int i = 0; i < nums.size();++i) { if (pos_map.count(target-nums[i])) { ret.push_back(i); ret.push_back(pos_map[target-nums[i]]); return ret; } pos_map[nums[i]] = i; } return ret; } };
**思路: **我们遍历数组nums,借助unordered_map做数组元素和其在数组中的位置的映射,每当我们遍历到一个数组成员,我们先查找unordered_map中是否存在与该元素相加,构成要求的target的元素,如果存在的话,那说明我们已经找到这两个元素,所以我们就可以通过他们的键得到他们的值,也即是他们在数组中的位置。
unordered_map 比map块,因为它不需要排序。尽管他们的范围遍历都是比较低效的。
Unordered maps 可通过运算符[]来使用键key,直接访问其映射的 == > mp[key] = value;
std::unordered_map::count ( )
搜索unordered_map容器中key=k的成员的个数,由于unordered_map容器键是唯一的,所以在这个key存在的基础上,这个返回值永远为1.
C语言:
int compare(void *a,void *b) { return *((int *)a) - *((int *)b); } /** * Return an array of arrays of size *returnSize. * The sizes of the arrays are returned as *returnColumnSizes array. * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). */ int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){ int **ret = NULL; int count = 0; int *retcol = NULL; (*returnSize) = count; (*returnColumnSizes) = retcol; if (!nums || numsSize <= 2) { return ret; } qsort(nums,numsSize,sizeof(int),(void *)compare); ret = (int **)realloc(ret,sizeof(int *)*numsSize*numsSize); retcol = (int *)realloc(retcol,sizeof(int)*numsSize*numsSize); for (int i = 0; i < numsSize-2;++i) { if (nums[i] > 0) { break; } if (i > 0 && nums[i] == nums[i-1] ) { continue; } int head = i+1; int tail = numsSize-1; while (head < tail) { int target = nums[head] + nums[tail] + nums[i]; if (target == 0) { #if 0 在内部逐步扩展指针数组,会导致超时,真操蛋! ret = (int **)realloc(ret,sizeof(int *)*(count+1)); retcol = (int *)realloc(retcol,sizeof(int)*(count+1)); #endif retcol[count] = 3; ret[count] = (int *)malloc(sizeof(int)*3); ret[count][0] = nums[i]; ret[count][1] = nums[head]; ret[count][2] = nums[tail]; ++count; while (head < tail && nums[head] == nums[head+1]) { ++head; } while (head < tail && nums[tail] == nums[tail - 1]) { --tail; } ++head; --tail; } else if (target < 0) { ++head; } else { --tail; } } } (*returnSize) = count; (*returnColumnSizes) = retcol; return ret; }
c++:
class Solution { public: vector<vector<int>> threeSum(vector<int>& nums) { const int size = (int)nums.size(); vector<vector<int>> ret; if (size < 3) { return ret; } sort(nums.begin(),nums.end()); for (int i = 0; i < size -2;++i) { if (nums[i] > 0) { break; } if (i > 0 && nums[i] == nums[i-1]) { continue; } int head = i+1; int tail = size-1; while (head < tail) { int target = nums[i] + nums[head] + nums[tail]; if (target == 0) { ret.push_back({nums[i],nums[head],nums[tail]}); while (head < tail && nums[head] == nums[head+1]) { ++head; } while (head < tail && nums[tail] == nums[tail-1]) { --tail; } ++head; --tail; } else if (target < 0) { ++head; } else { --tail; } } } return ret; } };
class Solution { public: int threeSumClosest(vector<int>& nums, int target) { const int size = (int)nums.size(); int mostequal = nums[0] + nums[1] + nums[2]; int diff = abs(mostequal - target); sort(nums.begin(),nums.end()); for (int i = 0; i < size -2;++i) { int head = i+1; int tail = size-1; while (head < tail) { int tmp = nums[i] + nums[head] + nums[tail]; int tmpdiff = abs(target - tmp); if (diff > tmpdiff) { diff = tmpdiff; mostequal = tmp; } if (tmp < target) { ++head; } else { --tail; } } } return mostequal; } };
class Solution { public: vector<int> preorder(Node* root) { vector<int> ret; if (!root) { return ret; } stack<Node *>auxstack; auxstack.push(root); while (!auxstack.empty()) { Node *ptmp = auxstack.top(); ret.push_back(ptmp->val); auxstack.pop(); for (auto itr = ptmp->children.rbegin();itr != ptmp->children.rend();++itr) { auxstack.push(*itr); } } return ret; } };
难度中等709收藏分享切换为英文接收动态反馈
给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1,1]]
示例 2:
输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
提示:
1 <= nums.length <= 8
-10 <= nums[i] <= 10
as
(1) 从输入流is获取string,直到遇到字符delim
istream& getline (istream& is, string& str, char delim);
istream& getline (istream&& is, string& str, char delim);
(2) 和(1)相比,默认delim为换行符’\n’
istream& getline (istream& is, string& str);
istream& getline (istream&& is, string& str);
注意事项:
1.delim字符并不作为输入保存
2.若执行getline之前,str中有内容,则将被is的输入所替换。
3.若is达到了文件结束状态,那么getline也会停止。
难度困难1066
路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。
路径和 是路径中各节点值的总和。
给你一个二叉树的根节点 root
,返回其 最大路径和 。
示例 1:
输入:root = [1,2,3] 输出:6 解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6
示例 2:
输入:root = [-10,9,20,null,null,15,7] 输出:42 解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42
提示:
[1, 3 * 104]
-1000 <= Node.val <= 1000
/** * 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 maxPathSum(TreeNode* root) { int ret = -1001; helper(root,ret); return ret; } int helper(TreeNode *node,int& res) { if (!node) return 0; int left = max(helper(node->left,res),0); //孩子值可能为负数,所以和0做对比,避免无用的“负”担 int right = max(helper(node->right,res),0); res = max(res,left+right+node->val);//此节点为根节点时构成的路径和若是超过当前最大路劲和res,那么就替换res为当前最大路径和。 return max(left,right) + node->val;//node不为根节点时,其必须和其左右孩子节点中的一个在同一个路径上,那么当然是值不为0且更大的那一个,但是两个孩子可能都为负数,此时就抛弃孩子不让其参与到路径中 } };
思路:
某个点,有可能以它为根节点,与其左右子树构成最大路径和,也有可能该节点只是最大路径和上的一个旁节点。如下的node A;
所以,我们在递归的过程中,要计算这两种情况,对于第一种情况,因为我们的回溯是从叶子节点向上的,所以当遍历到某个节点,我们不会再往下,那么我们就可以直接计算其为根节点时的路径和,并与当前最大路径和比较。
res = max(res,left+right+node->val);
同时,我们是从下到上的,那万一上面有一个节点F为根节点,与当前在其分支上的节点A其构成题目要求的最大路径和呢?所以在从下到上的回溯过程中我们要返回这种情况时的最大值,即A非根节点时其只能和其左右子树C和B中的最大者构成路径;又或者由于C、B(为根)的都是部分路径和为负数,此时只将A单独参与到最大路径的构成上去。
难度简单714收藏分享切换为英文接收动态反馈
给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
示例 :
给定二叉树
1 / \ 2 3 / \ 4 5
返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。
**注意:**两结点之间的路径长度是以它们之间边的数目表示。
/** * 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 reculenth(TreeNode *pnode,int& ret) { if (!pnode || (!pnode->left && !pnode->right)) { return 0; } int left = max(reculenth(pnode->left,ret),0); int right = max(reculenth(pnode->right,ret),0); if (pnode->left && pnode->right) { ret = max(ret,left + right + 2);//pnode此时作为根节点和其左右子树构成直径,所有左右子树的直径加上分别连接上pnode后新增的两个单位的路径 } else { ret = max(ret,left + right + 1);//pnode此时作为根节点和其左右子树构成直径,所有左右子树的直径加上分别连接上pnode后新增的两个单位的路径 } return max(left,right) + 1;//pnode以非根节点的节点身份构成直径,那么其左右子树只能取其一作为直径的一部分,那么按照题意,当然取最长的 } int diameterOfBinaryTree(TreeNode* root) { int ret = -0xfffffff; ret = max(reculenth(root,ret),ret); return ret; } };
难度简单751收藏分享切换为英文接收动态反馈
给你一个含 n
个整数的数组 nums
,其中 nums[i]
在区间 [1, n]
内。请你找出所有在 [1, n]
范围内但没有出现在 nums
中的数字,并以数组的形式返回结果。
示例 1:
输入:nums = [4,3,2,7,8,2,3,1] 输出:[5,6]
示例 2:
输入:nums = [1,1] 输出:[2]
提示:
n == nums.length
1 <= n <= 105
1 <= nums[i] <= n
**进阶:**你能在不使用额外空间且时间复杂度为 O(n)
的情况下解决这个问题吗? 你可以假定返回的数组不算在额外空间内。
class Solution { public: vector<int> findDisappearedNumbers(vector<int>& nums) { vector<int> ret; for (int i = 0; i < nums.size();++i) { nums[abs(nums[i]) -1] = - abs(nums[abs(nums[i]) -1]); } for (int i = 0; i < nums.size();++i) { if (nums[i] > 0) { ret.push_back(i+1); } } return ret; } };
难度简单1077收藏分享切换为英文接收动态反馈
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12] 输出: [1,3,12,0,0]
说明:
class Solution { public: void moveZeroes(vector<int>& nums) { if (nums.size() <= 0) { return; } int l_p = 0;//左指针永远指向数组中第一个0 int r_p = 0;//右指针永远指向左指针以后的第一个非0 while (r_p < nums.size()) { while (l_p < nums.size() && nums[l_p] != 0) { ++l_p; } r_p = l_p+1; while (r_p < nums.size() && nums[r_p] == 0) { ++r_p; } if (l_p >= nums.size() || r_p >= nums.size()) { break; } nums[l_p] ^= nums[r_p]; nums[r_p] ^= nums[l_p]; nums[l_p] ^= nums[r_p]; } return; } };
思路:
双指针,左指针l_p永远指向数组中第一个为0的元素,右指针永远指向左指针以后的第一个非0元素,此时交换左右指针指向的元素,就能将首0元素和首非0元素交换。这样就能得到题目要求。
难度简单486收藏分享切换为英文接收动态反馈
两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。
给你两个整数 x
和 y
,计算并返回它们之间的汉明距离。
示例 1:
输入:x = 1, y = 4 输出:2 解释: 1 (0 0 0 1) 4 (0 1 0 0) ↑ ↑ 上面的箭头指出了对应二进制位不同的位置。
示例 2:
输入:x = 3, y = 1 输出:1
提示:
0 <= x, y <= 231 - 1
class Solution { public: int hammingDistance(int x, int y) { int num = x^y; int dist = 0; while (num != 0) { if (num&0x1 == 1) { ++dist; } num>>=1; } return dist; } };
难度中等1347收藏分享切换为英文接收动态反馈
给定一个仅包含数字 2-9
的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例 1:
输入:digits = "23" 输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:
输入:digits = "" 输出:[]
示例 3:
输入:digits = "2" 输出:["a","b","c"]
提示:
0 <= digits.length <= 4
digits[i]
是范围 ['2', '9']
的一个数字。class Solution { public: map<char,string> mp = {{'2',"abc"},{'3',"def"},{'4',"ghi"},{'5',"jkl"}, {'6',"mno"},{'7',"pqrs"},{'8',"tuv"},{'9',"wxyz"}}; vector<string> res; string cur; vector<string> letterCombinations(string digits) { if (digits.size() <= 0) { return res; } dfs(digits); return res; } void dfs(string digits) { if (!digits.size()) { res.push_back(cur); return; } else { string letter = mp[digits[0]]; for (int i = 0;i < letter.size();++i) { cur.push_back(letter[i]); dfs(digits.substr(1)); cur.pop_back(); } } } };
首先要明确题目要求的组合是什么形式的?
题目给出数字字符串digits,然后要求我们按照digits中每个字符对应的数字在键盘中对应的字符符来互相组合,组合出长度为strlen(digits)的字符串,这个字符串每个字母都唯一来自该数字所在的键位中的某个字符。
既然是组合问题,那么很大可能就是DFS,那如何得到呢?既然返回结果中的每一个string中的每一个字母都唯一来自不同键位,那么我们就当买菜,分别从各键位只挑一个来组合即可。
难度中等634收藏分享切换为英文接收动态反馈
存在一个按升序排列的链表,给你这个链表的头节点 head
,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字。
返回同样按升序排列的结果链表。
示例 1:
输入:head = [1,2,3,3,4,4,5] 输出:[1,2,5]
示例 2:
输入:head = [1,1,1,2,3] 输出:[2,3]
提示:
链表中节点数目在范围 [0, 300]
内
-100 <= Node.val <= 100
题目数据保证链表已经按升序排列
/** * 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 || !head->next)//确保至少有两个结点 { return head; } ListNode *pnewhead = new ListNode(-1); ListNode *ptmp = pnewhead; pnewhead->next = head; ListNode *p1 = head; ListNode *p2 = head->next; while (p2) { if (p1->val == p2->val) { while (p1->val == p2->val)//找到结点同值的最右结点的下一个结点 { p2 = p2->next; if (!p2) { pnewhead->next = NULL; return ptmp->next; } } p1 = p2; p2 = p2->next; } else { pnewhead->next = p1; pnewhead = p1; p1 = p2; p2 = p2->next; } } pnewhead->next = p1; return ptmp->next; } };
思路:
当p1和p2相等时,就循环找到p2往后的第一个和p1不等的结点,然后重复上述过程,直到找到一个唯一的结点,此时就将其加入题目要求的链表。
难度困难2173收藏分享切换为英文接收动态反馈
给你一个字符串 s
和一个字符规律 p
,请你来实现一个支持 '.'
和 '*'
的正则表达式匹配。
'.'
匹配任意单个字符'*'
匹配零个或多个前面的那一个元素所谓匹配,是要涵盖 整个 字符串 s
的,而不是部分字符串。
示例 1:
输入:s = "aa" p = "a" 输出:false 解释:"a" 无法匹配 "aa" 整个字符串。
示例 2:
输入:s = "aa" p = "a*" 输出:true 解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
示例 3:
输入:s = "ab" p = ".*" 输出:true 解释:".*" 表示可匹配零个或多个('*')任意字符('.')。
示例 4:
输入:s = "aab" p = "c*a*b" 输出:true 解释:因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
示例 5:
输入:s = "mississippi" p = "mis*is*p*." 输出:false
提示:
0 <= s.length <= 20
0 <= p.length <= 30
s
可能为空,且只包含从 a-z
的小写字母。
p
可能为空,且只包含从 a-z
的小写字母,以及字符 .
和 *
。
保证每次出现字符 *
时,前面都匹配到有效的字符
class Solution { public: bool isMatch(string s, string p) { if (p.empty()) return s.empty(); if (p.size() == 1) { return (s.size() == 1 && (s[0] == p[0] || p[0] == '.')); } if (p[1] != '*') {//说明p至少需要去匹配s中的一个字符 if (s.empty()) return false;//若此时s为空,那一定不满足条件 return (s[0] == p[0] || p[0] == '.') && isMatch(s.substr(1), p.substr(1)); } while (!s.empty() && (s[0] == p[0] || p[0] == '.')) { if (isMatch(s, p.substr(2))) //这样做的原因是假设此时的星号的作用是让前面的字符出现0次,验证是否匹配(即,从p当前的*的下一个字符去匹配s) { return true; } s = s.substr(1);//若上面假设的s[i]出现0次匹配失败了,那么我们就从s的下一个字符开始匹配,而p为什么不动呢?因为p可匹配的字母是0或多个,这里的else逻辑是这多个的分支,而多个决定我们不用移动p } return isMatch(s, p.substr(2));//若上面的情况都没有结果,那么就要在p之后的两个字符以后开始匹配 } };