Leetcode Top 100 Liked Questions(序号198~234)

​198. House Robber ​

题意:你是一个强盗,你要去抢劫,每个房子都有特定金额的钱,但是不能拿相邻的房子的钱

我的思路

有点像动态规划,但是首先把每个结点空一格的后缀和得到,2n

之后从(i=n-1;i>=0;i--)dp[i]=(nums[i]-nums[i+2])+max(dp[i+2],dp[i+3]);

代码 Runtime0 ms Beats 100% Memory7.9 MB Beats 19.97%

哈哈哈这都给我做出来了.jpg

class Solution {
public:
    int rob(vector<int>& nums) {
        int n=nums.size();
        if(n==1)return nums[n-1];
        else if(n==2)return max(nums[0],nums[1]);
        vector<int>dp(n,0);
        for(int i=n-3;i>=0;i=i-2) nums[i]=nums[i]+nums[i+2];
        for(int i=n-4;i>=0;i=i-2) nums[i]=nums[i]+nums[i+2];
        dp[n-1]=nums[n-1]; dp[n-2]=nums[n-2]; dp[n-3]=nums[n-3];
        if(n==3)return max(nums[0],nums[1]);
        for(int i=n-4;i>=0;i--)
            dp[i]=(nums[i]-nums[i+2])+max(dp[i+2],dp[i+3]);
        return max(dp[0],dp[1]);
    }
};

标答 更简洁的动态规划

注意dp是从1开始的,因为这样,dp[i-2]就不用特判了;dp[i]表示的是在i结点之前拿到的最大的钱

状态转移方程为dp[i]=max(dp[i-1],dp[i-2]+nums[i-1]);因为当前的结点要么拿要么不拿

如果拿了,dp[i]=dp[i-2]+num[i-1];如果没拿,dp[i]=dp[i-1]

代码 Runtime 0 ms Beats 100% Memory7.9 MB Beats 6.50%

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.size()==0)return 0;
        int n=nums.size(); vector<int> dp(n+1,0);
        dp[1]=nums[0];
        for(int i=2;i<=n;i++)
            dp[i]=max(dp[i-1],dp[i-2]+nums[i-1]);
        return dp[n];
    }
};

标答 空间更少的动态规划

不创建dp了,用prev和curr

代码 Runtime0 ms Beats 100% Memory7.7 MB Beats 76.93%

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.size()==0)return 0;
        int n=nums.size(); int prev=0,curr=nums[0],tmp;
        for(int i=2;i<=n;i++){
            tmp=max(curr,prev+nums[i-1]);
            prev=curr; curr=tmp;
        }
        return curr;
    }
};

199. Binary Tree Right Side View

题意:

我的思路

先层序遍历,之后把每一层的最右边加入

看了标答 dfs也可以

代码 Runtime 0 ms Beats 100% Memory 12 MB Beats 61.69%

class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        if(root==NULL)return {};
        queue<TreeNode* >q;vector<int> ans;
        ans.push_back(root->val);
        if(root->left!=NULL)q.push(root->left);
        if(root->right!=NULL)q.push(root->right);
        while(!q.empty()){
            int n=q.size();
            for(int i=0;i<n;i++){
                TreeNode* tmp=q.front();
                if(i==n-1)ans.push_back(tmp->val);
                if(tmp->left!=NULL)q.push(tmp->left);
                if(tmp->right!=NULL)q.push(tmp->right);
                q.pop();
            }
        }
        return ans;
    }
};

标答 DFS

ans是答案,如果root为空,return;因为一层一个答案,所以当ans需要第一个答案,id=0的时候,就把当前根的值放入;注意之后先递归right的结点,这样之后到当前层的时候可以先把这个节点的val放入ans中

代码 Runtime3 ms Beats 65.44% Memory11.9 MB Beats 81.60%

class Solution {
public:
    void sol(TreeNode* root,vector<int> &ans,int id){
        if(!root)return ;
        if(ans.size()==id)ans.push_back(root->val);
        sol(root->right,ans,id+1);//注意先递归右边
        sol(root->left,ans,id+1);
    }
    vector<int> rightSideView(TreeNode* root) {
        vector<int> ans;
        sol(root,ans,0);
        return ans;
    }
};

200. Number of Islands

题意:有多少片陆地,1是陆地,0是水

我的思路

BFS,看看有几次

代码 Runtime29 ms Beats 76.28% Memory18.2 MB Beats 30.61%

class Solution {
public:
    bool vis[302][302];
    void BFS(int x,int y,vector<vector<char>>& grid,int n,int m){
        queue <pair<int,int>>q;
        int dx[]={0,0,1,-1};
        int dy[]={1,-1,0,0};
        vis[x][y]=1;q.push({x,y});
        while(!q.empty()){
            int mx=q.front().first;
            int my=q.front().second;
            q.pop();
            for(int i=0;i<4;i++){
                int nx=dx[i]+mx;
                int ny=dy[i]+my;
                if(0>nx||n<=nx||0>ny||m<=ny||grid[nx][ny]=='0'||vis[nx][ny])continue;
                vis[nx][ny]=1;
                q.push({nx,ny});
            }
        }
        return;
    }
    int numIslands(vector<vector<char>>& grid) {
        int n=grid.size();int m=grid[0].size();int cnt=0;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(!vis[i][j]&&grid[i][j]!='0'){
                    cnt++; BFS(i,j,grid,n,m);
                }
            }
        }
        return cnt;
    }
};

标答 DFS

标答的空间复杂度小了,其实上面的代码也可以不要vis的,grid改成2说不定也可以起到vis的效果

代码 Runtime21 ms Beats 98.21% Memory12.3 MB Beats 66.71%

class Solution {
public:
    void DFS(int i,int j,vector<vector<char>>& grid,int n,int m){
        if(0>i||n<=i||0>j||m<=j||grid[i][j]!='1')return;
        grid[i][j]='2';    //省去了vis
        DFS(i+1,j,grid,n,m);
        DFS(i-1,j,grid,n,m);
        DFS(i,j+1,grid,n,m);
        DFS(i,j-1,grid,n,m);
    }
    int numIslands(vector<vector<char>>& grid) {
        int n=grid.size();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,grid,n,m);
                }
        return cnt;
    }
};

206. Reverse Linked List

题意:把链表倒过来

我的思路

递归,DFS返回的是从右到左的倒过来的指针,尾指针是返回的头部

哈哈这都被我做出来了.jpg

class Solution {
public:
    ListNode* DFS(ListNode* head){
        if(!(head->next))return head;
        ListNode* q=DFS(head->next);
        q->next=head;
        return head;
    }
    ListNode* reverseList(ListNode* head) {
        if(!head)return NULL;
        ListNode* tail=head;
        while(tail->next!=NULL)tail=tail->next;
        DFS(head)->next=NULL;
        return tail;
    }
};

标答 更简洁的递归

递归如图所示,主要就是head->next->next=head;head->next=NULL比较关键

h2指针从最深的递归返回后就一直没变过,所以h2就是最后的尾指针

代码 Runtime4 ms Beats 74.12% Memory8.6 MB Beats 6.1%

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head == NULL || head->next == NULL) return head;
        ListNode* prev = NULL;
        ListNode* h2 = reverseList(head->next);
        head->next->next = head;
        head->next=prev;
        return h2;
    }
};

标答 不用递归

初始化一个past指针和curr指针,之后一个个向后处理;我用了三个指针让它看起来更易于理解

代码 Runtime4 ms Beats 74.12% Memory8.3 MB Beats 66.48%

class Solution {
public:
    ListNode* reverseList(ListNode* head)
    {
        if(!head) return NULL;
        ListNode* past = head;
        ListNode* current = head -> next;
        past -> next = NULL;
        while(current){
            ListNode* next = current -> next;
            current -> next = past;
            past = current;
            current = next;
        }
        return past;
    }
};
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head==NULL)return head;
        ListNode* past=NULL;ListNode* curr=head;
        ListNode* prev=head->next;//初始化三个指针
        while(curr!=NULL){
            curr->next=past;
            past=curr;curr=prev;
            if(curr)prev=curr->next;
        }
        return past;
    }
};


207. Course Schedule

题意:判断环路,用拓扑排序

我的思路

不记得拓扑排序了,首先用vector建个图,2000个结点,5000条边

prerequisites[i] = [ai, bi] indicates that you must take course bi first if you want to take course ai

有一个vector pair<int,int> 进入的次数,点的序号;【遍历一次】进入的次数为0的时候,次数记为-1,vector[点的序号]中的所有点在vector的次数-1

代码  Runtime23 ms Beats 58.79% Memory13.3 MB Beats 84.44%

一开始以为要先ai才能bi,搞反了,但ac了,想了想反正只是判断有无环

class Solution {
public:
    bool canFinish(int n, vector<vector<int>>& pre) {
        int mp[20004]={0};  vector<int>vec[20004];
        for(int i=0;i<pre.size();i++){
            vec[pre[i][1]].push_back(pre[i][0]);
            mp[pre[i][0]]++;//入度++
        }//建完了
        bool flag=1;int cnt=0;//flag判断是否还有为0的,cnt是消掉的点
        while(flag){
            flag=0;
            for(int i=0;i<n;i++){
                if(mp[i])continue;
                for(int j=0;j<vec[i].size();j++){
                    mp[vec[i][j]]--;
                    if(mp[vec[i][j]]==0)flag=1;
                }
                mp[i]=-1;cnt++;
            }
        }
        if(cnt!=n)return 0;
        return 1;
    }
};

标答 拓扑排序

首先建立邻接表,degree计算每个点的入度;之后用队列判断,先扫一遍入度表,如果为0就放到队列中;当队列不为0时,把队列中的节点一个个拿出来,之后把这个点pop掉,并且把它前往的点的入度--;如果前往的节点的入度为0,把它放到队列中;

队列为空的时候看看是否pop掉所有节点

代码 Runtime16 ms Beats 90.55% Memory13.2 MB Beats 84.44%

class Solution {
public:
    bool canFinish(int n, vector<vector<int>>& pre) {
        vector<vector<int>> vec(n, vector<int>());
        vector<int> mp(n, 0);
        for(int i=0;i<pre.size();i++){
            vec[pre[i][1]].push_back(pre[i][0]);
            mp[pre[i][0]]++;//入度++
        }//建完了
        queue<int>q;
        for(int i=0;i<n;i++)
            if(!mp[i])q.push(i);
        while(!q.empty()){
            int tmp=q.front();q.pop();n--;
            for(auto& p:vec[tmp]){
                mp[p]--;if(!mp[p])q.push(p);
            }
        }
        return n==0;
    }
};

标答 DFS 没看

DFS看上去好难,算了

代码 Runtime15 ms Beats 94.18% Memory14.7 MB Beats 17.31%

class Solution {
public:
   bool detectcycle(vector<vector<int>>&v,int src,vector<int>&rst,vector<int>&vis){
        vis[src]=1;rst[src]=1;
        for(auto x:v[src]){
            if(!vis[x]&&detectcycle(v,x,rst,vis)) return true;
            else if(rst[x]==1) return true;
        }
        rst[src]=0;
        return false;
    }
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<vector<int>>v(numCourses); stack<int>s;
        vector<int>vis(numCourses),rst(numCourses);
        for(auto x: prerequisites) v[x[1]].push_back(x[0]);
        for(int i=0;i<numCourses;i++){
            if(!vis[i])
                if(detectcycle(v,i,rst,vis)) return false;
        }
        return true;
    }
};

208. Implement Trie (Prefix Tree)

题意:建一个前缀树,实现insert,search,startwith功能

我的思路

模板题 

代码 Runtime 31 ms Beats 99.69% Memory 78.8 MB Beats 11.17%

class Trie {
public:
    int nxt[30904][26]={0};int cnt=0;
    bool ex[30904]={0};
    Trie() {}
    void insert(string word) {
        int p=0;
        for(int i=0;word[i];i++){
            if(!nxt[p][word[i]-'a'])
                nxt[p][word[i]-'a']=++cnt;
            p=nxt[p][word[i]-'a'];
        }
        ex[p]=1;
    }
    bool search(string word) {
        int p=0;
        for(int i=0;word[i];i++){
            if(!nxt[p][word[i]-'a'])return 0;
            p=nxt[p][word[i]-'a'];
        }
        if(ex[p])return 1;
        return 0;
    }
    bool startsWith(string prefix) {
        int p=0;
        for(int i=0;prefix[i];i++){
            if(!nxt[p][prefix[i]-'a'])return 0;
            p=nxt[p][prefix[i]-'a'];
        }
        return 1;
    }
};

标答 链表 没细看

思路差不多,主要是要建立TrieNode这个节点,要注意指针初始化为NULL

代码 Runtime 30 ms Beats 99.81% Memory 43.4 MB Beats 76.60%

class TrieNode{
public:
    TrieNode *child[26];
    bool isword;
    TrieNode(): isword(false){for(auto &a:child) a=nullptr;}
};
class Trie {
public:
    Trie() {root = new TrieNode();}
    void insert(string word) {
        TrieNode *p = root;
        for(auto c:word){
            if(p->child[c-'a'] == nullptr)
                p->child[c-'a'] = new TrieNode();
            p = p->child[c-'a'];
        }
        p->isword = true;
    }
    bool search(string word) {
        TrieNode *p = root;
        for(auto c:word){
            if(p->child[c-'a'] == nullptr) return false;
            p = p->child[c-'a'];
        }
        return p->isword;
    }
    bool startsWith(string prefix) {
        TrieNode *p = root;
        for(auto c:prefix){
            if(p->child[c-'a'] == nullptr) return false;
            p = p->child[c-'a'];
        }
        return true;
    }
private:
    TrieNode* root;
};

215. Kth Largest Element in an Array

 题意:返回数组中第K大的元素,数组中有重复元素,数组未排序,要求不排序

我的思路

直接哈希做,一共就2e4个数字

代码 Runtime61 ms Beats 96.2% Memory55.4 MB Beats 12.47%

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        int vis[20001]={0};
        for(int q:nums) vis[q+10000]++;
        for(int i=20000;i>=0;i--){
            k=k-vis[i];
            if(k<=0)return i-10000;
        }
        return -1;
    }
};

标答 优先队列

stl的优先队列最小堆greater,先给前k个建堆;之后遍历剩下的数组,如果数组v[i]比q.top()小,就把q.top()弹出,v[i]加入,最后返回堆顶就可以了

代码 Runtime 88 ms Beats 59.41% Memory 56.4 MB Beats 11.13%

class Solution {
public:
    int findKthLargest(vector<int>& v, int k) {
        priority_queue<int,vector<int>,greater<int> >q(v.begin(),v.begin()+k);
        for(int i=k;i<v.size();i++){//是从k开始
            if(q.top()<v[i]){
                q.pop();q.push(v[i]);
            }
        }
        return q.top();
    }
};

标答 Quicksort TLE

虽然不知道为什么TLE了,但是理论上这个是O(n);注意find的输出是第k小,k从0开始

原理:因为快排一次(假设排序是从小到大的)可以确定一个位置,这个位置tmp的左边都是比它小的,右边都是大于等于它的;

如果tmp==k,返回a[tmp];如果tmp+1>k,find(left, tmp-1);如果tmp+1<k,find(tmp+1, right)

代码 TLE 不过听说随机化快排能过,之后研究一下

class Solution {
public:
    int quicksort(vector<int>& a,int left,int right){
        int mid=a[left];
        while (left<right){
            while (left<right&&mid<=a[right]) right--;
            a[left] = a[right];
            while (left<right&&a[left]<=mid)left++;
            a[right] = a[left];
        }
        a[left]=mid;
        return left;
    }
    int find(vector<int>& a,int left, int right, int k)
    {	
        int tem=quicksort(a,left,right);
        if(k==tem) return a[k];
        else if(k-1<tem) return find(a,left,tem-1,k);
        return find(a,tem+1,right,k);
    }
    int findKthLargest(vector<int>& v, int k) {
        ios::sync_with_stdio(0);cin.tie(0);
        int n=v.size();
        return find(v,0,n-1,n-k);
    }
};

226. Invert Binary Tree

 题意:

我的思路

递归,类似于第101题 Leetcode Top 100 Liked Questions(序号75~104)

但是对于这个例子不知道怎么做,我只知道swap值

root=[1, 2],Expected=[1,null,2]

看了标答后:swap时,应该是swap(root->left, root->right),而不是swap(l, r)

标答 DFS

不得不说,它确实和101题不太一样

代码 Runtime0 ms Beats 100% Memory 9.7 MB Beats 65.11%


class Solution {
public:
 TreeNode* invertTree(TreeNode* root) {
     if (root) {
         invertTree(root->left);
         invertTree(root->right);
         swap(root->left, root->right);
     }
     return root;
 }
};

看了标答后,我修改了我蹩脚的代码

class Solution {
public:
    void sol(TreeNode* r){
        if(r->left==NULL&&r->right==NULL)return;
        swap(r->left,r->right); 
        if(r->left==NULL) sol(r->right);
        else if(r->right==NULL) sol(r->left);
        else{sol(r->right);sol(r->left);}
    }
    TreeNode* invertTree(TreeNode* root) {
        if(!root)return root;
        sol(root); return root;
    }
};

标答 不递归 用栈(队列也可以)

类似于层序遍历

代码 Runtime0 ms Beats 100% Memory9.9 MB Beats 5.3%

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        stack<TreeNode*> stk;
        stk.push(root);    
        while (!stk.empty()) {
            TreeNode* p = stk.top();
            stk.pop();
            if (p) {
                stk.push(p->left);
                stk.push(p->right);
                swap(p->left, p->right);
            }
        }
        return root;
    }
};

230. Kth Smallest Element in a BST

题意:给一个二叉排序树,给出第K小的元素

我的思路

根据第215题,做法有哈希,优先队列

因为是排序树,所以中序序列就是顺序

所以中序遍历一遍就可以了

代码 Runtime7 ms Beats 98.62% Memory24.1 MB Beats 71.82%

用来两个引用

class Solution {
public:
    void dfs(TreeNode* root,int &k,int& ans){
        if(root==NULL||ans!=-1)return ;
        dfs(root->left,k,ans);
        k--;if(k==0)ans=root->val;
        dfs(root->right,k,ans);
    }
    int kthSmallest(TreeNode* root, int k) {
        int ans=-1;
        dfs(root,k,ans);
        return ans;
    }
};

234. Palindrome Linked List

题意:看看链表是不是回文的

我的思路

可以用vector.map 也许递归也可以,结点个数1e5

不会dfs,用vector吧

代码 Runtime 187 ms Beats 66.11% Memory 128.3 MB Beats 20.36%

class Solution {
public:
    vector<int>ans;
    bool isPalindrome(ListNode* head) {
        ListNode* p=head;
        while(p!=NULL){
            ans.push_back(p->val);
            p=p->next;
        }
        int n=ans.size();
        for(int i=0;i<n;i++){
            if(ans[i]!=ans[n-i-1]) return 0;
        }
        return 1;
    }
};

标答 Reverse 

标答说可以用Reverse,那么就可以206的方法做这题,不过这样破坏了原来的链表,写到一半不想写了,Reverse的思路是用快慢指针找到中点,之后两端都指向中点,最后slow到尾,fast=head,两遍同时向中间遍历判断

直接贴上来,不自己写了

代码 Runtime 117 ms Beats 99.85% Memory 110.4 MB Beats 98.10% 

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        ListNode* slow = head; ListNode* fast = head;
        ListNode* prev, * next = new ListNode();
        while(fast && fast -> next){
            slow = slow -> next;
            fast = fast -> next -> next;
            next = head -> next;
            head -> next = prev;
            prev = head;
            head = next;
        }
        if(fast) slow = slow -> next;
        head = prev;
        while(slow){
            if(slow->val!=head->val) return false;
            slow = slow -> next; head = head -> next;
        }
        return true;
    }
};

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值