学习目标:
熟悉hot100中的常见数据结构
unordered_map<int,int> map; map.find(3);//是查找键为3的元素,如果找到了键为 1 的元素,则 it 将指向该元素的迭代器;如果没有找到,则 it 将等于 umap.end(),表示未找到。
unordered_set<int> set; set.find(3); //是查找值为3的元素。
一、哈希表
最常用的:std::unordered_map 。key无序,key不可重复。
std::multimap ,key有序,key可重复。
哈希表的创建:unordered_map<int,int> Hash; 迭代器unordered_map<int,int>::iterator it; 或者auto it
unordered_map 中元素唯一,即key相同的元素不再进行插入。
unordered_set 常用方法:
.insert /*插入后自动去重*/ .find .count
添加元素
Hash[1]=3;
Hash.insert<make_pair(1,3)>;
Hash.insert({ {1,3},{2,4} });
查找 ——找不到返回hash.end()
it=Hash.find(1);
清除哈希表
Hash.erase(1);
Hash.clear();
tips:
数组去重常用方法:
1.unordered_set 2.sort之后nums.erase(unique(nums.begin(),nums.end()),nums.end()) ;//去重
学习内容:
2024-2-7:
两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map <int,int> umap;
// vector<int> result;
for(int i = 0;i<nums.size() ;i++){
int temp = target - nums[i];
auto it = umap.find(temp);
if( it ==umap.end()){//没找到
umap[nums[i]] = i;
}
else{ //找到了
// result[0] = i;
// result[1] = nums[i];
return {i,it->second};
}
}
return {};
}
};
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
字母易位词
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
/*对每个元素排序,字母易位词排序后一样,作为键,本身作为值*/
vector<vector<string>> result = {};
unordered_map<string,vector<string>> umap;
// for(int i = 0;i<strs.size();i++){
// }
for(string each:strs){
string tmp = each;
sort(tmp.begin(),tmp.end());
umap[tmp].push_back(each);
}
//把map转为vector
for(auto it = umap.begin();it!=umap.end();it++)
result.push_back(it->second);
return result;
}
};
示例 1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
示例 2:
输入: strs = [""]
输出: [[""]]
示例 3:
输入: strs = ["a"]
输出: [["a"]]
2024-2-8:
最长连续序列(秒)
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
if(nums.size() == 0) return 0;
//排序后遍历
int result = 1;
// unordered_set<int> uset ;
vector<int> num = nums;
sort(num.begin(),num.end());
num.erase(unique(num.begin(),num.end()),num.end()) ;//去重
int tmp = 1;
for(int i = 0;i<num.size()-1;i++){
if(num[i] - num[i+1] == -1){
tmp++;
}
else{
tmp = 1;
}
if(result < tmp ) result = tmp;
}
return result;
}
};
示例 1:
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
示例 2:
输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9
移动0
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
class Solution {
public:
void moveZeroes(vector<int>& nums) {
//双指针应用
int fast = 0,slow = 0;
for( ;fast<nums.size();fast++){
if(nums[fast] == 0) {
}
else{ //交换
nums[slow++] = nums[fast];
}
}
for(int i = slow;i<nums.size();i++)
nums[i] = 0;
}
};
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:
输入: nums = [0]
输出: [0]
盛最多水的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。说明:你不能倾斜容器。
(题解:向内移动短板,面积可能增加,向内移动长板,面积一定减小。)
class Solution {
public:
int maxArea(vector<int>& height) {
//双指针向内移动
int i = 0,j = height.size()-1;
int res = 0;
int tmp = 0;
while(i<j){ //向内移动短板,面积可能增大
if(height[i]<height[j]) {
tmp = (j-i)*height[i];
res = max(res,tmp);
i++;
}
else{
tmp = (j-i)*height[j];
res = max(res,tmp);
j--;
}
}
return res;
}
};
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1]
输出:1
2024-2-13:
三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。(难点在于降重上,很麻烦这个)
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
if(nums.size()<3) return {};
sort(nums.begin(),nums.end());
vector<vector<int>> res;
int totlen = nums.size();
for(int i = 0;i<totlen;i++){
if(nums[i] > 0 ) return res;
int L = i+1,R =totlen -1;
while(L<R){
int tmp = nums[i]+nums[L]+nums[R];
if(tmp == 0) {
res.push_back({nums[i],nums[L],nums[R]});
// break; 不能直接跳出
while(L<R && nums[L] == nums[L+1]) L++;
while(L<R && nums[R] == nums[R-1]) R--;
L++;
R--;
}
else if(tmp > 0) R--;
else if(tmp < 0) L++;
}
//对i处理,降重,i一样就跳过一个
while(i<totlen-1 && nums[i+1] == nums[i]) i++;
}
return res;
}
};
接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。(使用的双指针,超时,下一次使用一个数组记录每列左右最高点位置,用空间换时间)
class Solution {
public:
int getlocl(int cur,vector<int>& height){
int max = 0;
int res = -1;
for(int i = 0;i<cur;i++){
if(height[i] >= max) {
res = i;
max = height[i];
}
}
return res;
}
int getlocr(int cur,vector<int>& height){
int max = 0;
int res = -1;
for(int i = cur;i<height.size();i++){
if(height[i] >=max) {
res = i;
max = height[i];
}
}
return res;
}
int trap(vector<int>& height) {
int len = height.size();
if(len <3) return 0;
//解法一:按列求(双指针)
int sum = 0;
for(int i = 0;i<len-1;i++ ){
int cur = i+1;
int l = getlocl(cur, height);
int r = getlocr(cur, height);
int minvalue = min(height[l],height[r]);
if(height[cur]<minvalue){
sum+=minvalue-height[cur];
}
}
return sum;
}
};
2024-2-14:
无重复字符最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
注意unordered_set的使用,不能使用user.erase(uset.begin(),uset.find(s[i])); 最多只能使用uset.erase(uset.begin(),uset.end());
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int res = 0;
unordered_set<char> uset;
int left = 0;
for(int i = 0;i<s.length();i++){
auto it = uset.find(s[i]);
if(it!=uset.end()){ //找到了相同元素
// uset.erase(uset.begin(),it); //擦除此元素及之前位置的元素
// uset.insert(s[i]);
//上述代码不对是因为erase方法不能这么使用
while(s[i]!=s[left] ){
uset.erase(s[left++]);
}
left++;
}
else{ //没找到
uset.insert(s[i]);
}
if(uset.size()>res){
res = uset.size();
}
}
return res;
}
};
// //使用队列 注:队列不能访问任意位置的元素,pwwkew,pww就不能pass
// queue<char> q;
// int res = 0;
// for(int i = 0;i<s.length();i++){
// if(s[i] != q.front()) //没找到
// {
// q.push(s[i]); // 添加元素
// }
// else{ //移除首元素
// q.pop();
// }
// if(q.size()>res){
// res = q.size();
// }
// }
// return res;
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
找到字符串中所有字母异位词
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> res;
vector<int> sv(26);
vector<int> pv(26);
int plen = p.size();
int slen = s.size();
if(slen < plen) return vector<int>();
for(int i = 0;i<plen;i++){
sv[s[i]-'a']++;
pv[p[i]-'a']++;
}
if(sv == pv) {
res.emplace_back(0);
}
for(int i = 0;i<slen-plen;++i){
sv[s[i]-'a']--;
sv[s[i+plen]-'a']++;
if(sv == pv) {
res.emplace_back(i+1);
}
}
return res;
}
};
示例 1:
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
示例 2:
输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。
2024-2-15:
和为k的子数组(秒)
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。子数组是数组中元素的连续非空序列。
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int res = 0;
int tmp = 0;
// sort(nums.begin(),nums.end()); //不能改变数组,改了结果就变了,因为“子数组是数组中元素的连续非空序列。
//滑动一个不定长数组
for(int i = 0;i<nums.size();i++){
tmp = nums[i];
if(tmp == k) res++;
int j = i+1;
while(j<nums.size()){
tmp+=nums[j++];
if(tmp == k) res++;
}
}
return res;
}
};
示例 1:
输入:nums = [1,1,1], k = 2
输出:2
示例 2:
输入:nums = [1,2,3], k = 3
输出:2
滑动窗口最大值(超时)
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
//暴力解法,超时
vector<int> res;
for(int i = 0;i<nums.size()-k+1;i++){
int tmp = nums[i];
int j = 1;
while(j<k){
tmp = max(tmp,nums[i+j]);
j++;
}
res.push_back(tmp);
}
return res;
}
};
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
示例 2:
输入:nums = [1], k = 1
输出:[1]
2024-2-16:
最小覆盖字串(超时)
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
class Solution {
public:
bool check(unordered_map<char,int> smap,unordered_map<char,int>tmap){ //判断两个map是否一样
for(auto &tmp:smap){
if(tmap[tmp.first]<tmp.second){ //不一样
return false;
}
}
return true;
}
string minWindow(string s, string t) {
unordered_map<char,int> tmap,smap;
int ans = -1;
for(char tmp:t){
tmap[tmp]++;
}
int r = -1 ,l = 0;
int len = s.size()+1; //防止只有一个元素的特殊情况
int totlen = s.size();
while(r<totlen){
if(tmap.find(s[++r])!=tmap.end()){ //先判断完一轮
smap[s[r]]++;
}
while((check(tmap,smap) ==true)&& l<=r){ // smap与tmap一样时,判断长度与更新左边界
if(r-l+1<len) {
len = r-l+1;
ans = l;
}
if(tmap.find(s[l])!=tmap.end()){
smap[s[l]]--;
}
l++;
}
}
if(ans == -1) return string();
else return s.substr(ans,len);
}
};
2024-2-17:
最大子数组和
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
//动态规划
int res = nums[0];
for(int i = 0;i<nums.size()-1;i++){
if(nums[i]>0){
nums[i+1] = nums[i+1]+nums[i];
}
res = max(nums[i+1],res);
}
return res;
}
};
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1]
输出:1
示例 3:
输入:nums = [5,4,-1,7,8]
输出:23
合并区间
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
vector<vector<int>> res;
sort(intervals.begin(),intervals.end());
int l = intervals[0][0],r = intervals[0][1];
for(int i = 1 ; i<intervals.size();i++){
if(intervals[i][0]<= r){
r = max(intervals[i][1],r);
}
else{
res.push_back({l,r});
l = intervals[i][0];
r = intervals[i][1];
}
}
res.push_back({l,r});
return res;
}
};
示例 1:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。
轮转数组(秒)
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。
示例 1: 输入: nums = [1,2,3,4,5,6,7], k = 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮转 2 步: [6,7,1,2,3,4,5] 向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2: 输入:nums = [-1,-100,3,99], k = 2 输出:[3,99,-1,-100] 解释: 向右轮转 1 步:[99,-1,-100,3] 向右轮转 2 步: [3,99,-1,-100]
class Solution {
public:
void rotate(vector<int>& nums, int k) {
vector<int> res = nums;
int totlen = nums.size();
k = k%totlen;
int j = -1;
for(int i = 0;i<totlen;i++){
int tmp = totlen - k + i;
if(tmp >= totlen ) tmp = ++j;
if(tmp < totlen && tmp>=0)
res[i] = nums[tmp];
}
nums = res;
}
};
2024-2-19:
除自身以外的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。请 不要使用除法,且在 O(n) 时间复杂度内完成此题。
示例 1:输入: nums = [1,2,3,4] 输出: [24,12,8,6]
示例 2: 输入: nums = [-1,1,0,-3,3] 输出: [0,0,9,0,0]
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
//空间换时间典型案例
int totlen = nums.size();
int tmp = 1;
vector<int> ans(totlen,1);
//计算下三角
for(int i = 1;i<totlen;i++){
ans[i] = ans[i-1]*nums[i-1];
}
for(int i = totlen-2;i>=0;i--){
tmp = tmp*nums[i+1];
ans[i] = ans[i]*tmp;
}
return ans;
}
};
//暴力(超时)
// vector<int> ans;
// int totlen = nums.size();
// int tmp = 1;
// for(int i = 0;i<totlen;i++){
// int j = 0;
// while(j<totlen){
// if(i==j) continue;
// tmp *= nums[j++];
// }
// ans.push_back(tmp);
// }
// return ans;
2024-2-20:
矩阵置零(秒)
给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
vector<vector<int>> re = matrix;
for(int i = 0 ;i<matrix.size();i++){
for(int j = 0;j<matrix[0].size();j++){
if(re[i][j]==0) {
for(int a = 0;a<matrix.size();a++) matrix[a][j] = 0;
for(int b = 0;b<matrix[0].size();b++) matrix[i][b] = 0;
}
}
}
}
};
螺旋矩阵(重要,参考别人的更改边界的思想)
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector <int> res;
int left = 0;
int right = matrix[0].size()-1;
int up = 0;
int down = matrix.size()-1;
for(;;){
for(int i = left;i<=right;i++) res.push_back(matrix[up][i]);
if(++up >down) break;
for(int i = up;i<=down;i++) res.push_back(matrix[i][right]);
if(--right <left) break;
for(int i = right;i>=left;i--) res.push_back(matrix[down][i]);
if(--down <up) break;
for(int i = down;i>=up;i--) res.push_back(matrix[i][left]);
if(++left >right) break;
}
return res;
}
};
旋转图像(秒,直觉上来说对应坐标存在一个映射,很容易分析出来数学关系)
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
//就是一个数学关系,仿射变换
//x与y互换后,一个不变,一个用n-1-x
vector<vector<int>> re = matrix;
int n = matrix.size();
for(int i = 0;i<n;i++){
for(int j = 0;j<n;j++){
matrix[i][j] = re[n-1-j][i];
}
}
}
};
搜索二维矩阵Ⅱ(学习二分查找与为什么引用可以减小内存使用)
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
碰到的问题:*** for(auto const& row:matrix)*** 这种循环使用了引用 &,即每次循环迭代都会访问 matrix 中的元素的引用,而不是进行值的拷贝。如果不加引用会超出内存限制。
这种方式避免了在循环中创建对象的副本,因此在循环内部使用的是原始对象的引用,节省了内存和时间。
因此,对于大型的数据结构或对象,使用引用可以提高程序的效率,并减少内存开销。
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int i = 0,j = 0;
#if 0
//暴力搜索
for(int i = 0;i<matrix.size();i++){
for(int j = 0;j<matrix[0].size();j++){
if(matrix[i][j] == target) return true;
}
}
#else
for(auto const& row:matrix){
auto it = lower_bound(row.begin(),row.end(),target); //由于每行都是排好序的,使用二分查找target
if(it != row.end() && *it == target) return true;
}
#endif
return false;
}
};
相交链表(秒)
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at ‘8’
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
/**
* 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) {
//先保持长度一致,再往后遍历
ListNode* tmp = headA;
int lena = 0,lenb = 0;
while(tmp!=NULL){
lena++;
tmp = tmp->next;
}
tmp = headB;
while(tmp!=NULL){
lenb++;
tmp = tmp->next;
}
int dif = abs(lena-lenb);
ListNode* tmpS ;//较短的链表
if(lena>lenb) {tmp = headA;tmpS = headB;}
else {tmp = headB;tmpS = headA;}
for(int i = 0;i<dif;i++){
tmp = tmp->next;
}
do{ //do while是为了解决只有一个元素直接跳过的情况
if(tmp == tmpS) return tmp;
tmp = tmp->next; tmpS = tmpS->next;
}while(tmp !=NULL);
return NULL;
}
};
2024-2-21:
反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *pre = nullptr;
ListNode *tmp ;
ListNode *cur = head ;
while(cur!=nullptr)
{
tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
};
回文链表
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
输入:head = [1,2,2,1] 输出:true
class Solution {
public:
bool isPalindrome(ListNode* head) {
//引入一个数组
vector <int> val;
ListNode* cur = head;
int k = 0;
while(cur!=NULL){
val.push_back(cur->val);
cur = cur->next;
}
int len = val.size();
#if 0
for(int i = 0;i<len/2;i++){
if(val[i] != val[len-1-i]) return false;
}
#else
vector <int> val2 = val;
reverse(val.begin(),val.end());
return val == val2;
#endif
return true;
}
};
环形链表
给你一个链表的头节点 head ,判断链表中是否有环。
class Solution {
public:
bool hasCycle(ListNode *head) {
//双指针
ListNode* fast = head;
ListNode* slow = head;
if(head == NULL || head->next ==NULL) return false;
while(fast!=NULL){
if(fast->next!=NULL) fast = fast->next;
else return false;
if(fast->next!=NULL) fast = fast->next;
else return false;
if(slow->next!=NULL) slow = slow->next;
else return false;
if(fast == slow) return true;
}
return false;
}
};
环形链表Ⅱ
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。
注意:第一相遇不一定在哪,第二次一定是在相交节点相遇
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *fast = head,*slow = head;
//第一次相遇不一定在哪
while(1){
if(fast!=NULL) fast = fast->next;
else return NULL;
if(fast!=NULL) fast = fast->next;
else return NULL;
slow = slow->next;
if(slow == fast) break;
}
fast = head;
while(fast !=slow){ //第二次相遇一定是在相交节点上
fast = fast->next;
slow = slow->next;
}
return fast;
}
};
合并两个有序链表(不知道为什么不设置虚拟头节点时,在最后一个元素相等的情况下运行会找不到指针)
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4]
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode *list;
ListNode *pre = new ListNode(-1);
ListNode *tmp = pre;
//设置了虚拟头节点可以运行,没有虚拟头节点会在最后一个元素相同时报找不到指针的错,怀疑是likou的bug
// if(list1 !=nullptr && list2!=nullptr){
// if(list1->val <= list2->val) {list = list1;list1 = list1->next;}
// else {list = list2; list2 = list2->next;}
// tmp = list;
// }
while(list1!=nullptr && list2!=nullptr){
if(list1->val <= list2->val ) {tmp->next = list1; list1 = list1->next;}
else {tmp->next = list2; list2 = list2->next;}
tmp = tmp->next;
}
if(list1 ==nullptr) {tmp->next = list2; }
else {tmp->next = list1; }
return pre->next;
}
};
两数相加(秒)
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。请你将两个数相加,并以相同形式返回一个表示和的链表。你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
输入:l1 = [2,4,3], l2 = [5,6,4] 输出:[7,0,8] 解释:342 + 465 = 807.
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
int flag = 0;//进位标志
ListNode* prehead = new ListNode(-1);
ListNode* p = prehead;
while(l1!= nullptr && l2!= nullptr){//前n共同位相加
int tmp = l1->val+l2->val+flag;
ListNode* list;
if(tmp>9) {flag = 1; list = new ListNode(tmp%10);}
else {flag = 0; list = new ListNode(tmp);}
p->next = list;
p = p->next;
l1 = l1->next;
l2 = l2->next;
}
/*处理多余的部分*/
while(l1!= nullptr) {
int tmp = l1->val+flag;
ListNode* list;
if(tmp>9) {flag = 1; list = new ListNode(tmp%10);}
else {flag = 0; list = new ListNode(tmp);}
p->next = list;
p = p->next;
l1 = l1->next;
}
while(l2!= nullptr) {
int tmp = l2->val+flag;
ListNode* list;
if(tmp>9) {flag = 1; list = new ListNode(tmp%10);}
else {flag = 0; list = new ListNode(tmp);}
p->next = list;
p = p->next;
l2 = l2->next;
}
if(flag == 1) { //处理最后的进位
ListNode* list = new ListNode(1);
p->next = list;
}
return prehead->next;
}
};
//不是此题的目的,unsigned long int存储不了这么大的数,除非进行拆分
// unsigned long int a = 0,b = 0;
// int i = 0,j = 0;
// while(l1!= nullptr){
// int tmp = l1->val;
// a += tmp*pow(10,i++);
// l1 = l1->next;
// }
// while(l2!= nullptr){
// int tmp = l2->val;
// b += tmp*pow(10,j++);
// l2 = l2->next;
// }
// unsigned long int sum = a+b;
// ListNode* prehead = new ListNode(-1);
// ListNode* p = prehead;
// if(sum == 0) return new ListNode(0);
// while(sum!=0){
// int tmp = sum%10;
// sum /=10;
// ListNode* list = new ListNode(tmp);
// p->next = list;
// p = p->next;
// }
// return prehead->next;
删除链表中的倒数第N个结点(麻烦的地方在于删除首位结点的处理)
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *myhead = new ListNode(0);
myhead->next = head;
ListNode *cur = head;
int cnt = 0;
while(cur !=nullptr){
cur = cur->next;
cnt++;
}
cur = head;
int tmp = cnt - n;
if(tmp > 0){ //小于等于0如果进入下面的循环,--tmp,tmp始终为负数
while((--tmp )&&(cur!=nullptr)){
cur = cur->next;
}
}
else if(tmp ==0) {return head->next;} //删除第一个元素
if(cur == nullptr){ return nullptr;}
if(cur->next == nullptr){ return nullptr;}
if(cur->next->next ==nullptr &&(n ==1)){cur->next = nullptr;} //删除最后一个元素
else{
ListNode *temp = cur->next->next;
cur->next = temp;
// delete temp;
// temp = nullptr;
}
return myhead->next;
}
};
2024-2-22:
两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
输入:head = [1,2,3,4] 输出:[2,1,4,3]
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode *myhead = new ListNode(0);
myhead->next = head;
ListNode *cur = myhead;
while(cur->next != nullptr && cur->next->next != nullptr){
ListNode *tmp= cur->next;
ListNode *tmp1=cur->next->next->next;
cur->next = cur->next->next;
cur->next->next = tmp;
cur->next->next->next = tmp1;
cur = cur->next->next;
}
return myhead->next;
};
随机链表的复制(注意哈希表的用法)
给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
class Solution {
public:
Node* copyRandomList(Node* head) {
unordered_map<Node*,Node*> map;
Node *cur = head;
while(cur!=nullptr){
map[cur] = new Node(cur->val);
cur = cur->next;
}
cur = head;
//构建新链表的next和random
while(cur!=nullptr){
map[cur]->next = map[cur->next];
map[cur]->random = map[cur->random];
cur = cur->next;
}
return map[head];
}
};
排序链表(秒)(有序,有重复值,使用multimap存储链表,打散链表,重新组织链表!!!碾压官解)
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
输入:head = [4,2,1,3] 输出:[1,2,3,4]
class Solution {
public:
ListNode* sortList(ListNode* head) {
if(head == nullptr) return nullptr;
multimap <int,ListNode*> map;
ListNode * cur = head;
ListNode * pre = new ListNode(-1);
pre->next = head;
while(cur!=nullptr){
// map[cur->val] = cur;
map.insert({cur->val,cur});
cur = cur->next;
}
cur = head;
/*断开各指针之间的联系,分散开避免后续造成混乱*/
for(auto it = map.begin();it!=map.end();it++){
it->second->next = nullptr; //first-键 second-value
}
cur = pre;
for(auto it = map.begin();it!=map.end();it++){
cur->next = it->second; //first-键 second-value
cur = cur->next;
}
return pre->next;
}
};
// if(!head) return nullptr;
// multiset<int> set; //可能会有重复值,且要有序
// ListNode* ptr = head;
// while(ptr)
// {
// set.insert(ptr->val);
// ptr = ptr->next;
// }
// ptr = head;
// #if 0
// //取巧方法,只是改了值没有动链表。事实上使用multiset已经大大简化了此题。
// for(auto it=set.begin(); it!=set.end(); it++) {
// ptr->val = *it;
// ptr = ptr->next;
// }
// #else
// //变更指针
// for(auto it=set.begin(); it!=set.end(); it++) {
// ptr->val = *it;
// ptr = ptr->next;
// }
// #endif
// return head;
合并k个升序链表(秒)(和上面的题一样,记录链表,打散链表,组织链表)*
给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]] 输出:[1,1,2,3,4,4,5,6] 解释:链表数组如下: [
1->4->5, 1->3->4, 2->6 ] 将它们合并到一个有序链表中得到。 1->1->2->3->4->4->5->6
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
multimap <int ,ListNode*> map;
ListNode *tmp ;
ListNode *pre = new ListNode(-1) ;
for(int i = 0;i<lists.size();i++){
tmp = lists[i];
while(tmp!=nullptr){
map.insert({tmp->val,tmp});
tmp = tmp->next;
}
}
for(auto it = map.begin();it!=map.end();it++){
it->second->next = nullptr;
}
if(map.empty() ==true) return nullptr; //数组空和链表空
//拿到头节点
auto iter = map.begin();
ListNode *head = iter->second ;
pre->next = head;
tmp = pre;
for(auto it = map.begin();it!=map.end();it++){
tmp->next = it->second;
tmp = tmp->next;
}
return pre->next;
}
};
2024-2-23:
二叉树的中序遍历
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
输入:root = [1,null,2,3] 输出:[1,3,2]
class Solution {
public:
void tra(TreeNode*cur ,vector<int>& res){ //左中右
if(cur == nullptr) return ;
tra(cur->left,res);
res.push_back(cur->val);
tra(cur->right,res);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
TreeNode* cur = root;
tra(cur,res);
return res;
}
};
二叉树的最大深度(层序遍历)
给定一个二叉树 root ,返回其最大深度。二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
输入:root = [3,9,20,null,null,15,7] 输出:3
class Solution {
public:
int maxDepth(TreeNode* root) {
int dep = 0;
queue<TreeNode*> que;
if(root !=nullptr) que.push(root);
while(!que.empty()){
int size = que.size();
// vector <int> vec;
for(int i = 0;i<size;i++){
TreeNode* cur = que.front();
que.pop();
// vec.push_back(cur->val);
if(cur->left) que.push(cur->left);
if(cur->right) que.push(cur->right);
}
dep++;
}
return dep;
}
};
翻转二叉树
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
输入:root = [4,2,7,1,3,6,9] 输出:[4,7,2,9,6,3,1]
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
queue<TreeNode*> q;
if(root!=nullptr) q.push(root);
while(!q.empty()){
int size = q.size();
for(int i = 0;i<size;i++){
TreeNode* cur = q.front();
q.pop();
// if(cur->left!=nullptr && cur->right!=nullptr) swap(cur->left->val,cur->right->val);
swap(cur->left,cur->right); //链表交换
if(cur->left) q.push(cur->left);
if(cur->right) q.push(cur->right);
}
}
return root;
}
};
二叉树的直径
给你一棵二叉树的根节点,返回该树的 直径 。二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。两节点之间路径的 长度 由它们之间边数表示。
输入:root = [1,2,3,4,5] 输出:3 解释:3 ,取路径 [4,2,1,3] 或 [5,2,1,3] 的长度。
class Solution {
public:
int ans = 0;
int dfs(TreeNode *cur){
if(cur == nullptr) return 0;
int l = dfs(cur->left);
int r = dfs(cur->right);
ans = max(ans,l+r);
return max(l,r)+1; //最后一层往上返回的时候路径得增加1
}
int diameterOfBinaryTree(TreeNode* root) {
dfs(root);
return ans;
}
};
二叉树 的层序遍历
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
输入:root = [3,9,20,null,null,15,7] 输出:[[3],[9,20],[15,7]]
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
queue <TreeNode*> q;
if(root!=nullptr) q.push(root);
while(!q.empty()){ //队列非空进循环
int size = q.size();
vector<int> vec;
for(int i = 0;i<size;i++){
TreeNode* node = q.front();
q.pop();
vec.push_back(node->val);
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
}
res.push_back(vec);
}
return res;
}
};
验证二叉搜索树
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。
输入:root = [2,1,3] 输出:true
class Solution {
public:
bool tra(TreeNode* cur,long low,long big){
if(cur == nullptr) return true;
if(cur->val<= low || cur->val >= big) return false;
bool tmp1 = tra(cur->left,low,cur->val);
bool tmp2 = tra(cur->right,cur->val,big);
return tmp1&&tmp2;
}
bool isValidBST(TreeNode* root) {
return tra(root,LONG_MIN,LONG_MAX);
}
};
2024-2-25:
有效的括号
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对应的相同类型的左括号。
输入:s = “()” 输出:true 示例 2:
输入:s = “()[]{}” 输出:true 示例 3:
输入:s = “(]” 输出:false
class Solution {
public:
bool isValid(string s) {
stack<char> st;
if(s.size()%2!=0) return false;
for(char t:s){
if(t =='(') st.push(')');
else if(t == '[') st.push(']');
else if(t =='{') st.push('}');
else if(st.empty()||st.top()!=t) return false;
else st.pop();
}
if(!st.empty()) return false;
return true;
}
};
2024-2-26:
最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。void pop() 删除堆栈顶部的元素。int top() 获取堆栈顶部的元素。int getMin() 获取堆栈中的最小元素。
示例 1:
输入: [“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.
class MinStack {
public:
stack<int> st;
stack<int> st1;//存储每个元素对应的最小元素
int tmp = INT_MAX;
MinStack() {
st1.push(tmp);
}
void push(int val) {
st.push(val);
if(val<st1.top()) st1.push(val);
else st1.push(st1.top());
}
void pop() {
st.pop();
st1.pop();
}
int top() {
return st.top();
}
int getMin() {
return st1.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();
*/
每日温度
给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。
示例 1: 输入: temperatures = [73,74,75,71,69,72,76,73] 输出:
[1,1,4,2,1,1,0,0]
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
vector<int> res(temperatures.size(),0); //数组必须初始化吗
stack <int> st;
if(temperatures.size()==0) return vector<int>();
st.push(0);
for(int i = 1;i<temperatures.size();i++){
if(temperatures[i]<temperatures[st.top()]) { //当前元素小于栈顶元素
st.push(i);
}
else if(temperatures[i] == temperatures[st.top()]){
st.push(i);
}
else{ //当前元素>栈顶元素 ,需要判断历史元素
while(st.empty()==false && temperatures[i]>temperatures[st.top()]){
res[st.top()] = i - st.top(); st.pop();
}
st.push(i);
}
}
return res;
}
};
2024-2-28:
对称二叉树
给你一个二叉树的根节点 root , 检查它是否轴对称。
输入:root = [1,2,2,3,4,4,3] 输出:true
class Solution {
public:
bool tre(TreeNode *l,TreeNode *r){
if(l == nullptr && r ==nullptr) return true;
else if(!l || !r ) return false; //一个为null时
if(l->val == r->val) {
return tre(l->left,r->right)&&tre(l->right,r->left);
}
return false;
}
bool isSymmetric(TreeNode* root) {
if(root ==nullptr) return false;
return tre(root,root);;
}
};
二叉搜索树中第k小的元素
给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。
输入:root = [3,1,4,null,2], k = 1 输出:1
class Solution {
public:
vector<int>res;
void tre(TreeNode *cur){
if(cur == nullptr) return ;
res.push_back(cur->val);
tre(cur->left);
tre(cur->right);
}
int kthSmallest(TreeNode* root, int k) {
//遍历后排序
tre(root);
sort(res.begin(),res.end());
return res[k-1];
}
};
2024-2-29:
二叉树的右视图(秒)
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
输入: [1,2,3,null,5,null,4] 输出: [1,3,4]
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
//层序遍历返回最右侧的节点值
vector<int> res;
if(root == nullptr) return vector<int>();
queue <TreeNode*> que;
que.push(root);
while(!que.empty()){
int size = que.size();
for(int i = 0;i<size;i++){
TreeNode *tmp = que.front();
que.pop();
if(i == size-1) res.push_back(tmp->val);
if(tmp->left) que.push(tmp->left);
if(tmp->right) que.push(tmp->right);
}
}
return res;
}
};
将二叉树转换为链表(秒,依然是我们的先进思想)
给你二叉树的根结点 root ,请你将它展开为一个单链表:展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。展开后的单链表应该与二叉树 先序遍历 顺序相同。
输入:root = [1,2,5,3,4,null,6] 输出:[1,null,2,null,3,null,4,null,5,null,6]
class Solution {
public:
queue <TreeNode*> que;
void tre(TreeNode *cur){
if(cur == nullptr) return;
que.push(cur);
tre(cur->left);
tre(cur->right);
}
void flatten(TreeNode* root) {
tre(root);
while(!que.empty()){
TreeNode* tmp = que.front();
que.pop();
tmp->left =nullptr;
if(!que.empty()) {
TreeNode* next = que.front(); //取出来但不放回去
tmp->right = next;
}
else break;
}
return ;
}
};
2024-3-1:
只出现一次的数字
给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
输入:nums = [2,2,1] 输出:1
输入:nums = [4,1,2,1,2] 输出:4
class Solution {
public:
int singleNumber(vector<int>& nums) {
unordered_map<int,int> umap;
for(int tmp:nums){
umap[tmp]++;
}
// auto it = umap.find(1);
for( auto it = umap.begin();it!=umap.end();it++){
if(it->second == 1)
return it->first;
}
return -1;
}
};
路径总和
给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8 输出:3 解释:和等于8 的路径有 3 条,如图所示。
class Solution {
public:
int res = 0;
void tre(TreeNode* cur,int target,long int nowval){
if(cur == nullptr) return;
nowval+=cur->val;
if(nowval == target) {res++; /*nowval=0;*/}
tre(cur->left,target,nowval);
tre(cur->right,target,nowval);
return;
}
int pathSum(TreeNode* root, int targetSum) {
//递归
if(root == nullptr) return 0;
TreeNode* cur = root;
tre(cur,targetSum,0);
pathSum(cur->left,targetSum);
pathSum(cur->right,targetSum);
return res;
}
};
二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 输出:3 解释:节点 5 和节点1 的最近公共祖先是节点 3 。
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == NULL) return NULL;
if(p == root || q == root) return root;
TreeNode* left = lowestCommonAncestor(root->left,p,q);
TreeNode* right = lowestCommonAncestor(root->right,p,q);
if(left == NULL) return right;
if(right== NULL) return left;
return root;
}
};
2024-3-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:
unordered_map<int,int> index;
TreeNode* tre(vector<int>& preorder, vector<int>& inorder,int pl,int pr,int il,int ir){
if(pl>pr) return nullptr;
int proot = pl;
int inroot = index[preorder[proot]];
TreeNode* root = new TreeNode(preorder[proot]);
int sub = inroot - il;
root->left = tre(preorder,inorder,pl+1,pl+sub,il,inroot-1);
root->right = tre(preorder,inorder,pl+sub+1,pr,inroot+1,ir);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.size() == 0) return nullptr;
for(int i = 0;i<preorder.size();i++){
index[inorder[i]] = i;
}
return tre(preorder,inorder,0,preorder.size()-1,0,preorder.size()-1);
}
};
二叉树中的最大路径和
二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。路径和 是路径中各节点值的总和。给你一个二叉树的根节点 root ,返回其 最大路径和 。
输入:root = [1,2,3] 输出:6 解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6
class Solution {
public:
int maxval = INT_MIN;
int tre(TreeNode* root){
if(root == nullptr) return 0;
int left = max(tre(root->left),0);
int right = max(tre(root->right),0);
int newval = root->val+left+right;
maxval = max(maxval,newval);
return root->val+max(left,right);
}
int maxPathSum(TreeNode* root) {
tre(root);
return maxval;
}
};
2024-3-5:
电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例 1:输入:digits = "23"输出:[“ad”,“ae”,“af”,“bd”,“be”,“bf”,“cd”,“ce”,“cf”]
class Solution {
public:
vector<string> res;
void backtrack(string& digits,int index,string *map,string s){
if(index == digits.size()){
res.push_back(s);
return ;
}
int digit = digits[index]-'0';
string tmp = map[digit];
for(char m:tmp){
s.push_back(m);
backtrack(digits,index+1,map,s);
s.pop_back();
}
}
vector<string> letterCombinations(string digits) {
string map[10] = {
"","",
"abc",
"def",
"ghi",
"jkl",
"mno",
"pqrs",
"tuv",
"wxyz"
};
string s;
if(digits.size()==0) return vector<string>();
backtrack( digits,0 , map,s);
return res;
}
};
组合总和
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 对于给定的输入,保证和为 target 的不同组合数少于 150 个。
示例 1:输入:candidates = [2,3,6,7], target = 7输出:[[2,2,3],[7]]解释:2 和 3
可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。 7 也是一个候选, 7 = 7 。仅有这两种组合。
class Solution {
public:
vector<vector<int>> res;
void backtrack(vector<int>& candidates, int target,vector<int>& s,int sum,int startindex){
// if(index == candidates.size()){
// res.push_back(s); return ;
// }
if(sum>target) return ;
if(sum == target) {res.push_back(s); return ;}
for(int i = startindex;i<candidates.size();i++){
sum += candidates[i];
s.push_back(candidates[i]);
backtrack(candidates,target,s,sum,i);
s.pop_back();
sum -= candidates[i];
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<int> s;
backtrack(candidates,target,s,0,0);
return res;
}
};
2024-3-6:
括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
输入:n = 3 输出:[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”]
class Solution {
public:
vector<string> res;
void backtrack(int n,int l,int r,string& s){
if(l==r && l==n){
res.push_back(s); return ;
}
if(l<r) return ;
if(l<n){
s.push_back('(');
backtrack(n,l+1,r,s);
s.pop_back();
}
if(l>r){
s.push_back(')');
backtrack(n,l,r+1,s);
s.pop_back();
}
}
vector<string> generateParenthesis(int n) {
string s;
backtrack(n,0,0,s);
return res;
}
};
单词搜索(超时)
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]],
word = “ABCCED” 输出:true
// class Solution {
// public:
// bool flag = false;
// void backtrack(vector<vector<char>>& board, string word,string& s,int startindex,int m,int n,int M,int N,vector<vector<int>> subboard,int k){
// if(s == word){
// flag = true; return ;
// }
// if(k++ >word.length()) return;
// if(m>=M || m<0 || n>=N || n<0 ||board[m][n] != word[startindex]) return ;
// if(subboard[m][n] == 1) return;
// if(board[m][n] == word[startindex] ){//有相等的就回溯
// s.push_back(word[startindex]);
// subboard[m][n] = 1;
// backtrack(board,word,s,startindex+1,m+1,n,M,N,subboard,k);
// subboard[m][n] = 0;
// s.pop_back();
// s.push_back(word[startindex]);
// subboard[m][n] = 1;
// backtrack(board,word,s,startindex+1,m-1,n,M,N,subboard,k);
// subboard[m][n] = 0;
// s.pop_back();
// s.push_back(word[startindex]);
// subboard[m][n] = 1;
// backtrack(board,word,s,startindex+1,m,n+1,M,N,subboard,k);
// subboard[m][n] = 0;
// s.pop_back();
// s.push_back(word[startindex]);
// subboard[m][n] = 1;
// backtrack(board,word,s,startindex+1,m,n-1,M,N,subboard,k);
// subboard[m][n] = 0;
// s.pop_back();
// }
// }
// bool exist(vector<vector<char>>& board, string word) {
// int M = board.size();
// int N = board[0].size();
// vector<vector<int>> subboard(M,vector<int>(N));
// string s;
// for(int i = 0;i<M;i++){
// for(int j = 0;j<N;j++){
// if(board[i][j] == word[0]){
// backtrack(board,word,s,0,i,j,M,N,subboard,0);
// }
// }
// }
// return flag;
// }
// };
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;
}
};
分割回文字符串
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。回文串 是正着读和反着读都一样的字符串。
示例 1:
输入:s = “aab” 输出:[[“a”,“a”,“b”],[“aa”,“b”]]
class Solution {
public:
vector<vector<string>> res;
vector<string> an;
bool isHui(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;
}
void backtrack(string s,int startindex){
if(startindex>=s.size()) {
res.push_back(an); return ;
}
for(int i = startindex;i<s.size();i++){
if(isHui(s,startindex,i)){
string tmp = s.substr(startindex,i-startindex+1);
an.push_back(tmp);
}else continue;
backtrack(s,i+1);
an.pop_back();
}
}
vector<vector<string>> partition(string s) {
backtrack( s,0);
return res;
}
};
爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
输入:n = 2 输出:2 解释:有两种方法可以爬到楼顶。
- 1 阶 + 1 阶
- 2 阶
class Solution {
public:
// int dp(int n ){
// if(n ==1) return 1;
// if(n == 2) return 2;
// if(n == 3) return 3;
// return dp(n-1)+dp(n-2);
// }
int climbStairs(int n) {
// return dp(n);
int t1 =2,t2 =3,t3 = 1;
if(n == 3) return 3;
if(n == 2) return 2;
for(int i =4;i<=n;i++){
t3 = t1+t2;
t1 = t2;
t2 = t3;
}
return t3;
}
};
反转链表
反转链表
学习产出:
提示:这里统计学习计划的总量
例如:
- 技术笔记 2 遍
- CSDN 技术博客 3 篇
- 习的 vlog 视频 1 个