链表
思路:
挺简单的。首先判读链表的开头几个节点,是不是target值,是的话把这些节点也都忽视掉。
对于后面的节点也是同样的思路,如果某个节点的下一个节点是target值,那么就让它->next = 它->next ->next
class Solution
{
public:
ListNode* removeElements(ListNode* head, int val)
{
if(head==nullptr)
{
return head;
}
ListNode* Pre = head;
while(Pre!=nullptr && Pre->val==val)
{
ListNode* target = Pre;
Pre = Pre->next;
// delete target;
}
head = Pre;
while(Pre!=nullptr)
{
if(Pre->next!=nullptr && Pre->next->val==val)
{
ListNode* target = Pre->next;
Pre->next = Pre->next->next;
delete target;
}
else
Pre = Pre->next;
}
return head;
}
};
贪心
最短无序连续子数组
题目描述:
解决方案:
可以将整个数组分为三部分,numsa,numsb,numsc,其中a和c都是有序的,b是需要排序的部分,那么我们保证a和c最大,那么b部分就会最小,也就是长度最小。
同时需要知道最终的数组一定是被排序完的,且排序的结果唯一,那么我们可以先得到一个排序后的结果,然后先从前往后比较两个数组,看哪里先不一样,先不一样的地方就是b的起点,再从后往前比较,先不一样的地方就b的终点。
class Solution
{
public:
int findUnsortedSubarray(vector<int>& nums)
{
if(is_sorted(nums.begin(), nums.end()))
{
return 0;
}
vector<int> numssorted(nums);
sort(nums.begin(),nums.end());
int left = 0;
int n = nums.size();
for(int i = 0;i<n;i++)
{
if(nums[i]==numssorted[left])
{
left++;
}
else
{
break;
}
}
int right = n-1;
for(int i = n-1;i>=0;i--)
{
if(nums[i]==numssorted[right])
{
right--;
}
else
{
break;
}
}
return right-left+1;
}
};
解决方案:
可以像填方块一样,解决这个问题。
首先整理每个字符出现的次数,并按次数出现的高低排序。
假设出现最多的字符是A,它出现了X次,那么我们画一个X*(n+1)的格子。前(x-1)个格子肯定会被用上,但是最后一行,要看其余字符出现的次数,还有没有和X一样多的,有多少个出现了X的字符,那么结果就是(x-1)*(n+1)+出现次数。
class Solution
{
public:
int leastInterval(vector<char>& tasks, int n)
{
int len = tasks.size();
vector<int> vec(26);
for(auto a:tasks)
{
vec[a-'A']++;
}
sort(vec.begin(),vec.end(),greater<int>());
int cnt = 1;
while(cnt<vec.size() && vec[cnt]==vec[0])
{
cnt++;
}
return max(len,cnt+(vec[0]-1)*(n+1));
}
};
思路
题目要求是峰谷峰谷…这样排列
相当于大小大小…这样子排列
那么我们另nums数组排序,然后让最后的数字放到新数组第一个,最前面数组放新数组第二个… 依次类推
class Solution
{
public:
void wiggleSort(vector<int>& nums)
{
sort(nums.begin(),nums.end());
vector<int> vec;
for(auto num:nums)
{
vec.push_back(num);
}
nums.clear();
int i = 0,j = vec.size()-1;
while(i<=j)
{
if(i<=j)
{
nums.push_back(vec[j]);j--;
}
if(i<=j)
{
nums.push_back(vec[i]);i++;
}
}
vec.clear();
}
};
动态规划
最大正方形
思路:
首先题目说了,找只包含1的最大正方形,那么包含0的就不算数。
因此我们可以创建一个二维数组dp,这个二维数组中与原matrix一致,在是1的地方为1 ,为0的地方是0
同时创建一个res,如果原matrix中有1的话,res=1,否则res=0,可以直接返回res=0,因为原数组中没有1 。
我们从dp数组的第2行第2列开始,也就是dp[1][1]开始,dp[1][1]=min(dp[0][0],dp[1][0],dp[0][1])+1,如果这4个都是1的话,dp[1][1]=2,res = max(res,dp[1][1]),对于其它的也是这样。
那么我们的转移方差就是
if(matrix[i][j]=='1'&& matrix[i][j]==matrix[i-1][j] && matrix[i][j]==matrix[i][j-1] && matrix[i][j]==matrix[i-1][j-1])
{
dp[i][j] = min(dp[i-1][j],min(dp[i][j-1],dp[i-1][j-1]))+1;
res = max(res,dp[i][j]);
}
最终结果返回res*res
class Solution
{
public:
int maximalSquare(vector<vector<char>>& matrix)
{
int m = matrix.size(),n = matrix[0].size();
vector<vector<int>> dp(m,vector<int>(n,0));
int res = 0;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(matrix[i][j]=='1')
{
dp[i][j] = 1;
res = 1;
}
}
}
for(int i = 1;i<m;i++)
{
for(int j =1;j<n;j++)
{
if(matrix[i][j]=='1'&& matrix[i][j]==matrix[i-1][j] && matrix[i][j]==matrix[i][j-1] && matrix[i][j]==matrix[i-1][j-1])
{
dp[i][j] = min(dp[i-1][j],min(dp[i][j-1],dp[i-1][j-1]))+1;
res = max(res,dp[i][j]);
}
}
}
return res*res;
}
};
思路:
思路很简单,首先创建dp数组,dp数组大小和nums的大小一致
另dp[0]=1
从位置0开始判断,如果dp[i]=1,那么就将从i出发到i+nums[i]的dp[i],都置为1
最后判断dp[n-1]是否为1
class Solution
{
public:
bool canJump(vector<int>& nums)
{
int n = nums.size();
vector<int> dp(n,0);
dp[0] = 1;
for(int i =0;i<n-1;i++)
{
if(dp[i]==1)
{
if(i+nums[i]<n)
{
for(int j=i+1;j<=i+nums[i];j++)
{
dp[j] = 1;
}
}
else
{
for(int j=i+1;j<n;j++)
{
dp[j] = 1;
}
}
}
}
if(dp[n-1]==1)
{
return true;
}
return false;
}
};
看到的另一种更好的答案
class Solution
{
public:
bool canJump(vector<int>& nums)
{
int k = 0;
for(int i = 0;i<nums.size();i++)
{
if(i>k) return false;
k = max(k,i+nums[i]);
}
return true;
}
};
思路:
对于第一行,如果想走到末尾,只能是grid[i][j]+=grid[i][j-1]
对于第一列 只能是grid[i][j]+=grid[i-1][j]
对于其它地方,就是grid[i][j]+=min(grid[i-1][j],grid[i][j-1]);
class Solution
{
public:
int minPathSum(vector<vector<int>>& grid)
{
int m = grid.size(),n=grid[0].size();
for(int i = 1;i<n;i++)
{
grid[0][i]+=grid[0][i-1];
}
for(int i = 1;i<m;i++)
{
grid[i][0]+=grid[i-1][0];
}
for(int i = 1;i<m;i++)
{
for(int j = 1;j<n;j++)
{
grid[i][j]+=min(grid[i-1][j],grid[i][j-1]);
}
}
return grid[m-1][n-1];
}
};
思路:
首先得承认,这个题自己做了好久。动态规划确实有意思哦
另f(i)表示,数字i可以最少用几个完全平方数表示
定义一个dp数组,数组大小为n+1,dp[i] = i;
其中dp[i] = min(dp[i],dp[i-j*j]+1)
class Solution
{
public:
int numSquares(int n)
{
vector<int> dp(n+1,0);
for(int i = 0;i<=n;i++)
{
dp[i] = i;
for(int j = 1;i-j*j>=0;j++)
{
dp[i] = min(dp[i],dp[i-j*j]+1);
}
}
return dp[n];
}
};
思路:
中心向外扩展法,判断共有多少字符串。
另每个字符作为中心,向两边延伸去判断当前子串是否为回文串。
判断字符串是否为回文串有两种情况:
如果字符串的个数为奇数,那么选中间的字符为中心即可
如果字符串的个数为偶数,选择中间的两个字符为中心
class Solution
{
public:
int countSubstrings(string s)
{
int ans = 0,n = s.size();
for(int i = 0;i<2*n-1;i++)
{
int l = i/2,r= i/2+i%2;
while(l>=0&&r<n&&s[l]==s[r])
{
l--;
r++;
ans++;
}
}
return ans;
}
};
思路:
定义一个n+3大小的数组vec
转移方程为vec[i] = vec[i-3]+vec[i-2]+vec[i-1] i>=3
vec[0] = 1;vec[1] = 1;vec[2] = 2;
题中又说vec[i]可能很大,所以要和1000000007取模 即 vec[i] = vec[i]%1000000007
class Solution
{
public:
int waysToStep(int n)
{
vector<long int> vec(n+3);
vec[0] = 1;
vec[1] = 1;
vec[2] = vec[0]+vec[1];
vec[3] = vec[0]+vec[1]+vec[2];
for(int i = 4;i<=n;i++)
{
vec[i] = vec[i-3]+vec[i-2]+vec[i-1];
vec[i] = vec[i]%1000000007;
}
return vec[n];
}
};
思路:
另part为当前元素或者当前元素+前一个元素中的最大值 另res 是 已有res和part的最大值
part = max(num,num+part); res = max(res,part);
class Solution
{
public:
int maxSubArray(vector<int>& nums)
{
int res = nums[0],part = 0;
for(auto num:nums)
{
part = max(num,num+part);
res = max(part,res);
}
return res;
}
};
思路
这里附上我对某个大神代码的理解
我们把题目要求的数字称为丑数,那么一个丑数一定是由前面的丑数 x3/x5/x7得来的
假如我们已经知道了丑数数列的前5个 nums[0] nums[1] nums[2] nums[3] nums[4]
那么后面的丑数一定是
nums[0]*3 nums[1]*3 nums[2]*3 nums[3]*3 nums[4]*3
nums[0]*5 nums[1]*5 nums[2]*5 nums[3]*5 nums[4]*5
nums[0]*7 nums[1]*7 nums[2]*7 nums[3]*7 nums[4]*7
将上面3个数组,合并起来,得到的有序数列。
那么这个过程其实可以用3个指针p3 p5 p7表示,
可以用min(nums[p3]*3,nums[p5]*5,nums[p7]*7),作为下一个丑数,用到了哪一个,那么对应的指针就加1
class Solution
{
public:
int getKthMagicNumber(int k)
{
vector<int> vec(k+10);
vec[1] = 1;
int p3 = 1,p5 = 1,p7 = 1;
for(int i = 2;i<=k;i++)
{
int num3 = vec[p3]*3,num5 = vec[p5]*5,num7 = vec[p7]*7;
vec[i] = min(min(num3,num5),num7);
if(vec[i]==num3)
{
p3++;
}
if(vec[i]==num5)
{
p5++;
}
if(vec[i]==num7)
{
p7++;
}
}
return vec[k];
}
};
深度优先搜索
思路:
如果root本身为空,那就返回nullptr
对于root->left而言,我们要root的右子树(右节点)
对于root->right而言,我们要root的左子树(左节点)
class Solution
{
public:
TreeNode* invertTree(TreeNode* root)
{
if(root==nullptr)
{
return nullptr;
}
TreeNode* temp = root->left;
root->left = invertTree(root->right);
root->right = invertTree(temp);
return root;
}
};
思路:
通过一个vector容器,将二叉树的先序遍历保存起来,然后利用容器中的保存顺序,将节点串起来即可。但是缺陷在于内存空间占用较多。
class Solution
{
public:
void first_dp(TreeNode* root,vector<TreeNode*>& vec)
{
if(root==nullptr)
{
return;
}
vec.push_back(root);
first_dp(root->left,vec);
first_dp(root->right,vec);
}
void flatten(TreeNode* root)
{
vector<TreeNode*> vec;
first_dp(root,vec);
TreeNode* res = root;
for(auto a:vec)
{
if(a==root)
{
continue;
}
else
{
res->right = a;
res->left = nullptr;
res = res->right;
}
}
}
};
思路:
对于每节点o,我们令f(o)是节点o,在选取它的情况下,最大的累计值。
令g(o)是在不选取它的情况下,最大的累计值
f(o)=g(o->left)+g(o->right)
g(o) = max(f(o->left),g(o->left))+max(f(o->right),g(o->right))
这个题我最开始没想明白,看了答案发现确实妙
class Solution
{
public:
unordered_map<TreeNode*,int>f,g;
void dfs(TreeNode* node)
{
if(!node)
{
return;
}
dfs(node->left);
dfs(node->right);
f[node] = node->val+g[node->left]+g[node->right];
g[node] = max(f[node->left],g[node->left])+max(f[node->right],g[node->right]);
}
int rob(TreeNode* root)
{
dfs(root);
return max(f[root],g[root]);
}
};
思路:
这个题的思路比较简单,先前序遍历整颗二叉树的节点值,存到一个vector中。然后获得vector中元素和。
接着对于每一个节点,计算小于它的节点和,另某一节点=vector所有元素和-小于它的节点和
当然这个比较简单,还有较大优化空间
class Solution
{
public:
void first_dfs(TreeNode* root,vector<int>& vec)
{
if(root==nullptr)
{
return;
}
vec.push_back(root->val);
first_dfs(root->left,vec);
first_dfs(root->right,vec);
}
void dfs(TreeNode* root,int sum_all,vector<int> vec)
{
if(root==nullptr)
{
return;
}
int temp = 0;
for(auto a:vec)
{
if(a<root->val)
{
temp+=a;
}
else
{
break;
}
}
root->val = sum_all-temp;
dfs(root->left,sum_all,vec);
dfs(root->right,sum_all,vec);
}
TreeNode* convertBST(TreeNode* root)
{
vector<int> vec;
first_dfs(root,vec);
sort(vec.begin(),vec.end());
int sum_all = accumulate(vec.begin(),vec.end(),0);
dfs(root,sum_all,vec);
return root;
}
};
思路:
这个题我自己做的时候,没做出来。看答案看懂的。下面把我对答案的理解阐述一遍。
amount表示总金额。我们用函数f(x)表示,对于金额x,我们使用最少硬币个数表示的情况
f(x) = min(f(x-coin1),f(x-coin2),f(x-coin3)…)+1;
如果最终的f(x),x=amount情况下,f(x)>amount的话,说明没有能任何一种硬币能组成总金额,则返回1.否则返回正常的结果。
class Solution
{
public:
int coinChange(vector<int>& coins, int amount)
{
int Max = amount+1;
vector<int> dp(amount+1,Max);
dp[0] =0;
for(int i = 1;i<=amount;i++)
{
for(auto a:coins)
{
if(a<=i)
{
dp[i] = min(dp[i],dp[i-a]+1);
}
}
}
return dp[amount]>amount? -1 : dp[amount];
}
};
广度优先搜索
思路:
比较简单,看代码就OK
class Solution
{
public:
vector<vector<int>> dp;
vector<vector<int>> levelOrder(TreeNode* root)
{
if(root==nullptr)
{
return dp;
}
queue<TreeNode* > vec;
vec.push(root);
while(vec.size()>0)
{
vector<int> temp;
int n = vec.size();
for(int i = 0;i<n;i++)
{
TreeNode* node = vec.front();
vec.pop();
temp.push_back(node->val);
if(node->left!=nullptr)
{
vec.push(node->left);
}
if(node->right!=nullptr)
{
vec.push(node->right);
}
}
dp.push_back(temp);
}
return dp;
}
};
思路:
这个题真是,一看就会,一做就错。
class Solution
{
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
if(root==p||root==q||root==nullptr) return root;
TreeNode* left = lowestCommonAncestor(root->left,p,q);
TreeNode* right = lowestCommonAncestor(root->right,p,q);
if(left!=nullptr && right!=nullptr) return root;
if(left==nullptr) return right;
return left;
}
};
快速选择
思路:
挺简单的,但是本来想用map做排序,但是后来还是要用 vector<pair<int,int>>
class Solution
{
public:
vector<int> topKFrequent(vector<int>& nums, int k)
{
unordered_map<int, int> m;
vector<pair<int,int>>dp;
for(auto a:nums)
{
m[a]++;
}
for(auto x:m)
{
dp.push_back(x);
}
sort(dp.begin(),dp.end(),[&](pair<int,int>a,pair<int,int>b)
{
return a.second>b.second;
});
vector<int> vec;
for(int i =0;i<k;i++)
{
vec.push_back(dp[i].first);
}
return vec;
}
};
二分查找
思路:
蛮简单的,感觉不像是中等题
class Solution
{
public:
vector<int> searchRange(vector<int>& nums, int target)
{
int left = 0,right = nums.size()-1;
vector<int> res;
for(int i = 0;i<nums.size();i++)
{
if(nums[i]==target)
{
res.push_back(i);
break;
}
}
for(int i =nums.size()-1;i>=0;i--)
{
if(nums[i]==target)
{
res.push_back(i);
break;
}
}
if(res.empty()==true)
{
res.push_back(-1);
res.push_back(-1);
}
return res;
}
};
思路:
数组中的数据可以分为两部分 partA partB,其中A、B内部都是有序的,如果把B和A调换位置整体数组有序。
假设数组是4,5,6,7,0,1,2
,其实也可以用二分查找
另l = 0,r = nums.size()-1,mid = (l+r)/2
如果nums[l]<=nums[mid],那么说明在这段区域内数组是有序的,判断target 是否在改区域内
如果上面为否,那么nums[mid]到nums[r]这段就是有序的,判断target是否在改区域内
对于target不在有序数组中的情况,要么是对l = mid+1,要么是r = mid-1,
class Solution
{
public:
int search(vector<int>& nums, int target)
{
int left = 0,right = nums.size()-1,n = nums.size();
if(n==0)
{
return -1;
}
else if(n==1)
{
return nums[0]==target? 0:-1;
}
while(left<=right)
{
int mid = (left+right)/2;
if(nums[mid]==target)
{
return mid;
}
if(nums[left]<=nums[mid])
{
if(nums[left]<=target && target<nums[mid])
{
right=mid-1;
}
else
{
left = mid+1;
}
}
else
{
if(nums[mid]<target && target<=nums[right])
{
left = mid+1;
}
else
{
right = mid-1;
}
}
}
return -1;
}
};
思路:
这个题有点难,我从自己的个人理解,分析下我的思路。
首先题目中的数组的范围都在1到n之间,数组中共有n+1个数字,那么就是说数组中有一个数字被至少重复了2次。
那么我们另left = 0 right = n mid = (left+right)/2
我们统计数组中小于等于mid的数字个数cnt,如果cnt>mid,说明这个重复的数字在left到mid中,否咋就在mid到right之间
基于这个思想去寻找这个重复的数字。
可以看出来这里依然使用了二分查找法,但是二分查找的对象不是数组本身
class Solution
{
public:
int findDuplicate(vector<int>& nums)
{
int n = nums.size();
int left = 1,right = n;
while(left<right)
{
int mid = (left+right)/2;
int cnt = 0;
for(auto num:nums)
{
if(num<=mid)
{
cnt++;
}
}
if(cnt>mid)
{
right = mid;
}
else
{
left = mid+1;
}
}
return left;
}
};