力扣Hot100题单个人计划c++版(一)
力扣Hot100题单个人计划c++版(二)
力扣Hot100题单个人计划c++版(三)
力扣Hot100题单个人计划c++版(四)
力扣Hot100题单个人计划c++版(五)
刷题链接:力扣Hot 100
每日一题,每日一更,白板手写。
力扣Hot 100
41.二叉树的层序遍历
10.11打卡
层序遍历很基础的算法了,按照每层返回一个vector的话,一开始想到用两个队列循环取每一层和下一层,然后在转成vector,当然题解更巧妙的做法是记住每层的大小即可。
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
if(root==nullptr)return {};
vector<vector<int>> ans;
queue<TreeNode*> q;
q.push(root);
int cnt=-1;
while(!q.empty()){
int clen=q.size();
++cnt;
ans.push_back(vector<int>());
while(clen--){
auto pos=q.front();q.pop();
ans[cnt].push_back(pos->val);
if(pos->left)q.push(pos->left);
if(pos->right)q.push(pos->right);
}
}
return ans;
}
};
42.二叉树的最大深度
10.12打卡
递归可以有两种写法,一种是像下面这段代码一样,根节点初始化为0,从上往下搜索,每搜索一层就加一。
class Solution {
public:
int treehigh(TreeNode* pos,int n){
if(!pos)return n;
return max(treehigh(pos->left,n+1),treehigh(pos->right,n+1));
}
int maxDepth(TreeNode* root) {
return treehigh(root,0);
}
};
也可以直到搜索至叶节点,让叶节点的下一层空节点为0,这样叶节点就是最低层为1,往上返回的过程中不断加1。
class Solution {
public:
int maxDepth(TreeNode* root) {
if(!root)return 0;
return max(maxDepth(root->left),maxDepth(root->right))+1;
}
};
还有可以用BFS,不断想下扩展层数,到边界就返回层数。广搜写太多了,此处就不写了。
43.从前序与中序遍历序列构造二叉树
10.15打卡。
递归好写,迭代难想,待补。
44.二叉树展开为链表
10.15打卡
递归的前序遍历很好写了。迭代待补。
class Solution {
public:
void flatten(TreeNode* root) {
vector<TreeNode*> list;
pre(root,list);
int n=list.size();
for(int i=1;i<n;i++){
TreeNode* t=list[i-1];
t->left=nullptr;
t->right=list[i];
}
}
void pre(TreeNode* r,vector<TreeNode*>& l){
if(!r)return;
l.emplace_back(r);
pre(r->left,l);
pre(r->right,l);
}
};
45.买卖股票的最佳时机
10.17打卡
《挑战程序设计》第二章入门题,经典dp,找到j之前的最小值即可。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int maxv=0;
int minv=prices[0];
int n=prices.size();
for(int i=1;i<n;i++){
if(minv>prices[i])minv=prices[i];
else maxv=max(maxv,prices[i]-minv);
}
return maxv;
}
};
46.二叉树中的最大路径和
10.17打卡
对于一个节点连接左右子树的路径,明显可以分成子问题,递归求左右子树的最大路径和即可。
class Solution {
public:
int maxv;
int dfs(TreeNode* pos){
if(pos==nullptr)return 0;
int t1=max(dfs(pos->left),0);
int t2=max(dfs(pos->right),0);
maxv=max(maxv,t1+t2+pos->val);
return max(t1,t2)+pos->val;
}
int maxPathSum(TreeNode* root) {
maxv=root->val;
dfs(root);
return maxv;
}
};
47.最长连续序列
10.18打卡
原本以为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)排序后再线性找是最快的。题解用哈希表直接
O
(
n
)
O(n)
O(n)是最快的。
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
int maxv=0;
int n=nums.size();
unordered_set<int> hash;
for(auto x:nums)hash.insert(x);
for(const int& x:hash){
if(hash.count(x-1))continue;
int nxt=x+1;
int clen=1;
while(hash.count(nxt)){
++nxt;++clen;
}
maxv=max(maxv,clen);
if(maxv>=(n>>1)+1)break;
}
return maxv;
}
};
48.只出现一次的数字
10.19打卡
异或的独特性质。
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ans=0;
for(const int& x:nums)ans^=x;
return ans;
}
};
49.单词拆分
10.20打卡
待补,经典题。三种写法都试一遍。题解:剖析三种解法: DFS, BFS, 动态规划 |139.单词拆分
49.环形链表
10.21打卡
正常思路应该都是哈希表判断是否当前节点是否被访问过。
class Solution {
public:
bool hasCycle(ListNode *head) {
unordered_set<ListNode*> hash;
ListNode* pos=head;
while(pos!=nullptr&&!hash.count(pos)){
hash.insert(pos);
pos=pos->next;
}
if(pos)return true;
return false;
}
};
但这类题还有一个巧妙思路就是快慢指针。快指针走两步,慢指针走一步,如果有环那一定会相遇。这样就可以避免使用 O ( n ) O(n) O(n)的空间存储遍历过的节点。这个问题还有一个拓展:如何找出环的入口处呢?思路很简单,先用上面的方法找出快慢指针的相遇点,然后一个指向头结点,一个指向相遇点,两个节点同步移动,再次相遇处就是环的入口处。很好证明,画个草图就能明白。
class Solution {
public:
bool hasCycle(ListNode *head) {
if (head == nullptr || head->next == nullptr) {
return false;
}
ListNode* low = head;
ListNode* fast = head->next;
while(low!=fast){
if(!fast||!fast->next)return false;
low=low->next;
fast=fast->next->next;
}
return true;
}
};
51.环形链表II
10.23打卡
正是上一题的拓展。。。预言成真。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(!head||!head->next)return nullptr;
ListNode* low=head;
ListNode* fast=head;
while(fast){
if(!fast->next)return nullptr;
low=low->next;
fast=fast->next->next;
if(low==fast){
ListNode* pre=head;
while(low!=pre){
low=low->next;
pre=pre->next;
}
return pre;
}
}
return nullptr;
}
};
52. LRU 缓存机制
10.23打卡
待补,考研复习到OS回过头补。
53.链表排序
10.24打卡
归并排序。但是想要达到常数级别的空间复杂度,需要自底向上归并,否则自顶向下递归会产生
O
(
l
o
g
n
)
O(logn)
O(logn)空间复杂度。简单来说,自底向下就是每次把链表分成小段,每次迭代长度依次增倍。合并相邻的链表即可。
class Solution {
public:
ListNode* merge(ListNode* h1,ListNode* h2){
ListNode* dummy=new ListNode(0);
ListNode* t=dummy;
ListNode* t1=h1,*t2=h2;
while(t1&&t2){
if(t1->val<t2->val){
t->next=t1;
t1=t1->next;
}else{
t->next=t2;
t2=t2->next;
}
t=t->next;
}
if(t1)t->next=t1;
else if(t2)t->next=t2;
return dummy->next;
}
ListNode* sortList(ListNode* head) {
if(!head)return nullptr;
int len=0;
ListNode* tmp=head;
while(tmp){
tmp=tmp->next;
++len;
}
ListNode* dummy=new ListNode(0,head);
for(int p=1;p<len;p<<=1){
ListNode* pre=dummy;
ListNode* cur=dummy->next;
while(cur){
ListNode* h1=cur;
for(int i=1;i<p&&(cur->next!=nullptr);++i)cur=cur->next;
ListNode* h2=cur->next;
cur->next=nullptr;
cur=h2;
for(int i=1;i<p&&cur!=nullptr&&cur->next!=nullptr;++i)cur=cur->next;
ListNode* nxt=nullptr;
if(cur){
nxt=cur->next;
cur->next=nullptr;
}
ListNode* ml=merge(h1,h2);
pre->next=ml;
while(pre->next!=nullptr)pre=pre->next;
cur=nxt;
}
}
return dummy->next;
}
};
54.乘积最大子数组
10.26
动态规划永远的痛!
class Solution {
public:
int maxProduct(vector<int>& nums) {
int n=nums.size();
int maxv,minv,ans;
maxv=minv=ans=nums[0];
for(int i=1;i<n;++i){
int t=maxv;
maxv=max(nums[i]*maxv,max(minv*nums[i],nums[i]));
minv=min(nums[i]*minv,min(t*nums[i],nums[i]));
ans=max(maxv,ans);
}
return ans;
}
};
55.最小栈
10.27打卡
很经典的解法,会者不难难者不会。确实很难想到用辅助栈存当前最小值。思路难想但是很简单,很巧妙的解法。
class MinStack {
public:
stack<int> s;
stack<int> mins;
MinStack() {
mins.push(INT_MAX);
}
void push(int val) {
s.push(val);
mins.push(min(mins.top(),val));
}
void pop() {
s.pop();mins.pop();
}
int top() {
return s.top();
}
int getMin() {
return mins.top();
}
};
56.相交链表
10.28打卡
原本思路是遍历a和b的长度,然后令一个节点从长链表移动k=len(a)-len(b)+1;然后另一个链表从头节点开始就可以相遇到。
不过也有只需要遍历更少的解法,即等一个节点到尾部后就把它放到另一个链表的头结点即可。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(headA==nullptr||headB==nullptr)return nullptr;
ListNode *ha=headA,*hb=headB;
while(ha!=hb){
if(ha)ha=ha->next;
else ha=headB;
if(hb)hb=hb->next;
else hb=headA;
}
return ha;
}
};
57.多数元素
10.30打卡
找出超过一半的数字,经典问题,投票算法可解,投票算法其实也可以这么理解:当减掉两个相异元素时,超过一半的数不会变。因为如果是目标数和其他数,去掉这两个结果仍不变,如果都不是目标数,那结果也不变。这样消去相异元素直到剩下最后相同的数,那个数就是目标数。
class Solution {
public:
int majorityElement(vector<int>& nums) {
int ans=-1;
int cnt=0;
for(int &x:nums){
if(x==ans)++cnt;
else if(--cnt<0){
ans=x;;
cnt=1;
}
}
return ans;
}
};
58.打家劫舍
10.30打卡
动态规划,应该很好想出来。对于当前的数,要么取这个加前n-2个最大的,要么不取加前n-1最大的。
class Solution {
public:
int rob(vector<int>& nums) {
int n=nums.size();
if(n==1)return nums[0];
if(n==2)return max(nums[0],nums[1]);
vector<int> dp(n,0);
dp[0]=nums[0];
dp[1]=max(nums[0],nums[1]);
for(int i=2;i<n;++i){
dp[i]=max(dp[i-1],nums[i]+dp[i-2]);
}
return dp[n-1];
}
};
59.岛屿数量
10.31打卡
求连通集,深搜广搜查并集均可。以下为深搜。
class Solution {
public:
vector<int> dx={1,0,0,-1};
vector<int> dy={0,1,-1,0};
void dfs(int cx,int cy,int n,int m,vector<vector<char>>& g){
if(g[cx][cy]!='1')return;
g[cx][cy]='0';
for(int i=0;i<4;++i){
int nx=cx+dx[i],ny=cy+dy[i];
if(nx<0||ny<0||nx>=n||ny>=m)continue;
dfs(nx,ny,n,m,g);
}
}
int numIslands(vector<vector<char>>& grid) {
int n=grid.size();
if(!n)return 0;
int m=grid[0].size();
int cnt=0;
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
if(grid[i][j]=='1'){
++cnt;
dfs(i,j,n,m,grid);
}
}
}
return cnt;
}
};
60.反转链表
11.1打卡
这应该是必刷题了吧。面试常见简单题。
- 迭代版
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *pre=nullptr;
ListNode *cur=head;
while(cur){
ListNode* nxt=cur->next;
cur->next=pre;
pre=cur;
cur=nxt;
}
return pre;
}
};
- 递归版
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(!head||!head->next)return head;
ListNode* newhead=reverseList(head->next);
head->next->next=head;
head->next=nullptr;
return newhead;
}
};