地址:
https://leetcode.com/problemset/top-100-liked-questions/?difficulty=Easy
done 表示写代码完成了 undone 表示看了思路但是没敲代码 predone 是之前做过了,这一次只看没做 neverdone 表示没做也没看
Easy:
predone
(1)两数之和——用哈希表做,键存数字,值存位置
predone
(20)有效的括号——用栈实现
undone
(21)合并两个有序链表——递归,非递归
done
(53)最大子序和——简单的动态规划
undone
(70)爬楼梯——简单动态规划(用非递归求解)
undone
(101)对称二叉树——递归(左右都为空,左右中有一个为空,左右不为空但是值不相等),非递归(类似层序遍历,用两个队列实现,分别存左右孩子)
undone
(104)二叉树的最大深度——递归(深度搜索),广度搜索
predone
(121)买卖股票的最佳时机——以f(i)表示以第i天结尾卖出的最大收益,用一个值pre记录第i天之前的最低价格,则f(i)=d[i]-res d[i]表示第i天的价格,最后求所有f(i)的最大值即为所求
done
(136)只出现一次的数字——异或求解(^)
undone
(141)环形链表——判断链表是否有环,快慢指针做
。。。(没看完)
Medium
undone
(2)两数相加——链表加法
done
(3)无重复字符的最长子串——动态规划
undone
(5)最长回文子串——动态规划,P[i][j]表示第i到第j组成的序列是回文串(是为true,不是为false),P[i,i]=true:当且仅当P[i+1,j-1] = true && (s[i]==s[j]),矩阵填充方式可以学习一下
undone
(11)盛水最多的容器——双指针法,每次将较短的那根线段向内侧移动
predone
(15)三数之和——先排序,然后固定一个数,另外两个数用双指针法找
undone
(17)电话号码的字母组合——从每个桶里取一个元素的全部组合(递归和非递归)
undone
(19)删除链表的倒数第N个节点——快慢指针
undone
(22)括号生成——递归方式,当时没太看明白
predone
(31)下一个排列——如: 8 4 7 6 5 3 1。从后往前找第一个不是升序排列的数字4,那么下一个比他大的数字在该位置上的数字应该大于4,故从后往前找第一个比他大的数5并交换位置,交换位置后,该位置的5已经保证了他要比之前的数字大了,因此将其后的升序序列反序。
neverdone
(33)搜索旋转排序数组——二分查找(剑指offer上有类似的)
neverdone
(34)在排序数组中查找元素的第一个和最后一个位置——二分查找
predone
(39)组合总数——回溯法,深度优先搜索,递归。找和为target的序列,对于节点i来说,下一步是找和为target-i的序列
done
(46)全排列——回溯法,递归。先将序列的每个元素和第一个元素交换,求除第一个元素外的全排列,最后将交换的元素换回去。
neverdone
(48)旋转图像——找规律。先按照从左上角到右下角的斜对角线为对称线换元素。然后根据中线换元素
neverdone
(49)字母异位词分组——将异位词分成一组,异位词是指字母相同但是顺序不同的词。先对每个词进行排序,那么所有异位词排序后都相同。然后用unordered_map实现,键是排序后的词,值是保存原词的vector
predone
(55)跳跃游戏——贪心算法。对于每个点,求出可到达的最远距离。
predone
(56)合并区间——按照区间的开头进行排序,每次合并相邻区间
done
(62)不同路径——简单的动态规划
done
(64)最小路径和——简单动态规划,和62类似。主要看答案中对动态规划空间复杂度的优化
predone
(75)颜色分类——双指针做,分别指向第一个1和最后一个1.
neverdone
(78)子集——回溯法,分支为取当前数字和不取当前数字
done
(79)单词搜索——回溯法,DFS。1从空节点开始的分支要写在主函数里,之后的分支在递归函数里。2由于最终不用输出具体结果字符串,因此没保存路径。3每次搜索的下一个分支有4种,分别为上,下,左,右。4需要标记矩阵中已经走过的点,避免重复走
predone
(94)二叉树的中序遍历——栈实现。口诀:不为空,压栈指左,为空,弹栈指右
neverdone
(96)不同的二叉搜索树——递归,以i为根节点的二叉搜索树总和,而以i为根节点的二叉树数目为左子树的数目*右子树的数目。卡塔兰数。
neverdone
(98)验证二叉搜索树——递归。递归函数规定了下一个节点的数值范围,左子树节点应该小于当前节点,右子树节点应该大于当前节点。(代码还是和自己想的不一样,还需要看一看)
done
(102)二叉树的层次遍历——一个队列实现,还需要一个数值保存当前层有多少个节点
done
(105)从前序和中序遍历序列构造二叉树——递归
neverdone
(114)二叉树展开为链表——递归,非递归,前序遍历三种方法
done
(139)单词拆分——动态规划,二维。以i结尾的词是否可以被拆分由i之前的某个以j结尾的序列是否可拆分成功和j-i这段序列是否在字典中来确定。
done
(142)环形链表——快慢指针看是否有环,一个从头走,一个从相遇点走,步幅都为1,相遇点就是环的入口
done
(148)合并排序链表——归并排序链表,时间复杂度nlogn,常数空间复杂度,因为是链表,合并时不需要开辟额外空间。思路:找中间节点断开,各自排序,然后合并两个有序链表。
neverdone
(152)乘积最大子序列——动态规划。需要保存以每个点结尾的最大乘积和最小乘积,因为之后的负数有可能让最小的(负数)变成最大的。因此需要两个数组。优化,可以用两个变量来替代数组。
done
(207)课程表——图判断是否有环(拓扑排序),两种方式,深度优先和广度优先(当时照着答案敲了一边代码,但是未单独敲过)
done
(208)前缀树——节点的孩子存在unordered_map里(vector里也行,下标为字母,值为指针),节点还需要一个表明是否是单词结尾的属性(照着答案敲了一遍)
predone
(215)数组中的第k个最大元素——优先队列或者partition来做
neverdone
(221)最大正方形——动态规划(二维数组动态规划)。第(i,j)位置的正方形边长等于(i-1,j), (i,j-1), (i-1,j-1)
位置处边长最小值+1。
neverdone
(236)二叉树的最低公共祖先——没看懂
Easy:
21.合并两个有序链表
递归:https://leetcode.com/problems/merge-two-sorted-lists/discuss/9713/A-recursive-solution
非递归:(新加一个节点作为头结点)https://leetcode.com/problems/merge-two-sorted-lists/discuss/9714/14-line-clean-C%2B%2B-Solution
70.爬楼梯
https://leetcode.com/problems/climbing-stairs/discuss/25299/Basically-it’s-a-fibonacci.
101.对称二叉树
递归:https://leetcode.com/problems/symmetric-tree/discuss/33056/15-lines-of-c%2B%2B-solution-8-ms
非递归:https://leetcode.com/problems/symmetric-tree/discuss/33089/My-C%2B%2B-Accepted-code-in-16ms-with-iteration-solution
141.环形链表
https://leetcode.com/problems/linked-list-cycle/discuss/44604/My-faster-and-slower-runner-solution
medium
3.无重复字符的最长子串
自己博客中有
5.最长回文子串
自己博客中有
https://leetcode.com/problems/longest-palindromic-substring/discuss/147548/Direct-c%2B%2B-DP
https://www.cnblogs.com/leavescy/p/5878336.html
11.盛水最多的容器
两线段之间形成的区域总是会受到其中较短那条长度的限制。此外,两线段距离越远,得到的面积就越大。我们在由线段长度构成的数组中使用两个指针,一个放在开始,一个置于末尾。 此外,我们会使用变量 maxareamaxarea 来持续存储到目前为止所获得的最大面积。 在每一步中,我们会找出指针所指向的两条线段形成的区域,更新 maxareamaxarea,并将指向较短线段的指针向较长线段那端移动一步。(来源于leetcode官方解析)
15.三数之和
自己博客中有
17.电话号码的字母组合
自己博客有
19.删除链表的倒数第N个节点
https://leetcode.com/problems/remove-nth-node-from-end-of-list/discuss/8843/C%2B%2B-solution-easy-to-understand-with-explanations.
22.括号生成
https://www.cnblogs.com/ariel-dreamland/p/9133613.html
33.下一个排列
自己博客有
34.在排序数组中查找元素的第一个和最后一个位置
自己博客有二分及变形的总结
39.组合总数
自己博客有
46.全排列
回溯法的博客里有
https://leetcode.com/problems/permutations/discuss/18247/My-elegant-recursive-C%2B%2B-solution-with-inline-explanation
48.旋转图像
https://www.cnblogs.com/jimmycheng/p/7199624.html
49.字母异位词分组
https://blog.csdn.net/hua111hua/article/details/87921568
55.跳跃游戏
自己博客有
56.区间合并
自己博客有
62.不同路径
https://leetcode.com/problems/unique-paths/discuss/22954/C%2B%2B-DP
64.最小路径和
https://leetcode.com/problems/minimum-path-sum/discuss/23457/C%2B%2B-DP
答案中有对空间复杂度的优化。二维矩阵;两个一维矩阵;一个一维矩阵。
75.颜色分类
自己博客有
78.子集
https://blog.csdn.net/camellhf/article/details/73551410
class Solution {
public:
void dfs(vector<vector<int>> &res, vector<int> &nums, vector<int> temp, int i) {//将nums中i位以后的序列求子集,用tmp保存一条路径的结果,当走到叶节点时,将结果保存到res中
if (i == nums.size()) {
res.push_back(temp);
return ;
}
dfs(res, nums, temp, i + 1); //不选当前数字
temp.push_back(nums[i]);
dfs(res, nums, temp, i + 1);//选当前数字
}
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> res;
vector<int> temp;
dfs(res, nums, temp, 0);
return res;
}
};
79.单词搜索
自己博客有
94.二叉树的中序遍历
自己博客有
96不同的二叉搜索数
https://blog.csdn.net/qq874455953/article/details/82811832
使用两个状态来记录:
G(n):长度为n的序列的所有唯一的二叉树。
F(i,n),1<=i<=n:以i作为根节点的二叉树的数量。
G(n)就是我们要求解的答案,G(n)可以由F(i,n)计算而来。
G(n)=F(1,n)+F(2,n)+…+F(n,n) (1)
G(0)=1,G(1)=1
对于给定的一个序列1…n,我们取i作为它的根节点,那么以i作为根节点的二叉树的数量F(i)可以由下面的公式计算而来:
F(i,n)=G(i-1)*G(n-i) 1<=i<=n (2)
综合公式(1)和公式(2),可以看出:
G(n) = G(0) * G(n-1) + G(1) * G(n-2) + … + G(n-1) * G(0)
98.验证二叉搜索树
https://blog.csdn.net/hlk09/article/details/81320283
class Solution {
public:
bool isValidBST(TreeNode * root) {
return isValidBSTHelper(root, INT64_MIN, INT64_MAX);
}
bool isValidBSTHelper(TreeNode *root, long lower, long upper){
if(root == NULL) return true;
if(root->val>=upper || root->val<=lower) return false; //当前节点范围不对
return isValidBSTHelper(root->left,lower,root->val)&&isValidBSTHelper(root->right,root->val,upper);//左节点应该小于当前节点,即upper为root->val.右节点应该大于当前节点
}
};
102.二叉树的层次遍历
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if(root==nullptr) return res;
queue<TreeNode*> q;
q.push(root);
int count=1;
while (!q.empty())
{
vector<int> cur_val; //保存当前层的节点值
for(int i=0;i<count;++i) //count为当前层的节点数目,如果没有count则不知道当前层应该对队列pop几次
{
TreeNode* tmp=q.front();
if(tmp->left) q.push(tmp->left); //左不为空入左
if(tmp->right) q.push(tmp->right); //右不为空入右
q.pop();
cur_val.push_back(tmp->val);
}
res.push_back(cur_val);
count=q.size(); //由于上一层的节点全部pop了,因此队列的大小就是当前层的节点数目
}
return res;
}
};
105.从前序和中序遍历序列构造二叉树
class Solution {
public:
/* from Preorder and Inorder Traversal */
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return helper(preorder,0,preorder.size(),inorder,0,inorder.size());
}
TreeNode* helper(vector<int>& preorder,int ps,int pe,vector<int>& inorder,int is,int ie)
{
if(ps>=pe) //注意是大于等于
return nullptr;
int root_val = preorder[ps]; //根节点
auto pos = find(inorder.begin()+is,inorder.begin()+ie,root_val); //在中序遍历序列里找root_val的位置
int left_num = pos-(inorder.begin()+is); //左子树元素个数
TreeNode* root = new TreeNode(root_val);
root->left = helper(preorder,ps+1,ps+left_num+1,inorder,is,is+left_num);//左子树对应序列在中序和后序的起始位置
root->right = helper(preorder,ps+left_num+1,pe,inorder,is+left_num+1,ie);//右子树对应序列在中序和后序的起始位置
return root;
}
};
114.二叉树展开为链表
自己博客有
139.单词拆分
自己博客有
148.排序链表
https://leetcode.com/problems/sort-list/discuss/46720/Share-my-C%2B%2B-concise-solutionseasy-to-understand
主要看双指针找中间点的写法,以及循环合并有序链表的写法
class Solution {
public:
ListNode* sortList(ListNode* head) {
if(head== nullptr || head->next== nullptr) //节点为空或者只有一个节点
return head;
ListNode* fast = head, *slow = head;
while (fast->next && fast->next->next) //注意这里都是fast,而不是一个fast,一个slow,因为fast合理slow一定合理。这样写是为了保证循环内的指针一定有效。
{
slow=slow->next; //节点个数为基数时,slow指向中间元素,为偶数时,slow指向中间两个数靠前的那个。比如4个元素,slow最终指向第2个
fast=fast->next->next;
}
fast = slow->next;
slow->next=nullptr; //从slow处断开,fast为第二段链表的开头
ListNode* fir = sortList(head); //排序第一段链表,因为slow之前断开了,因此此时head包含前半部分链表
ListNode* sec = sortList(fast);
return MergeList(fir,sec);
}
ListNode* MergeList(ListNode* fir,ListNode* sec) {
ListNode tmp_node(0); //新建一个头结点
ListNode *tmp = &tmp_node;
while (fir!=nullptr && sec!= nullptr)
{
if(fir->val<sec->val)
{
tmp->next=fir;
fir=fir->next;
}
else
{
tmp->next=sec;
sec=sec->next;
}
tmp=tmp->next;
}
if(fir!=nullptr) tmp->next=fir;
if(sec!=nullptr) tmp->next=sec;
return tmp_node.next; //注意这里是tmp_node的next,而不是tmp->next
}
};
152.乘积最大子序列
https://blog.csdn.net/fuxuemingzhu/article/details/83211451
以i结尾的最大乘积为(以i-1结尾的最大乘积当前数,以i-1结尾的最小乘积当前数,当前数)三个数的最大数
以i结尾的最小乘积为(以i-1结尾的最大乘积当前数,以i-1结尾的最小乘积当前数,当前数)三个数的最小数
207.课程表
自己博客有
208.前缀树
自己博客有
215.数组中的第K个最大元素
自己博客有
221.最大正方形
二维动态规划,可以用二维数组或者两个一维数组或者一个一位数组保存中间结果,后两者在空间复杂度上进行了优化。
参考:https://leetcode.com/problems/maximal-square/discuss/61803/C%2B%2B-space-optimized-DP
状态转移方程:https://blog.csdn.net/u014694994/article/details/80524103
还可以只用三个变量优化为常数空间复杂度,但是会修改原输入矩阵,不用仔细看了 https://leetcode.com/problems/maximal-square/discuss/61811/Clear-C%2B%2B-solution-no-extra-space-12-ms.