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;
}
};