2020.4.1 有效的括号嵌套深度(中等)
思路:略
感觉这题应该算作简单才对啊,(没有中等的难度)
class Solution {
public:
vector<int> maxDepthAfterSplit(string seq) {
vector<int> res(seq.size(),0);
int A=0,B=0;
for(int i=0;i<seq.size();i++){
char c=seq[i];
if(c=='('){
if(A>B){
B++;
res[i]=1;
}else
A++;
}else{
if(A>=B)
A--;
else{
B--;
res[i]=1;
}
}
}
return res;
}
};
using pos=pair<int,int>;
class Solution {
public:
void gameOfLife(vector<vector<int>>& board) {
int m=board.size(),n=board[0].size();
set<pos> changepairSet;
for(int x=0;x<m;x++){
for(int y=0;y<n;y++){
int liveCnt=getLiveCnt(board,x,y);
int state=board[x][y];
if(state && (liveCnt>3 || liveCnt<2))
changepairSet.insert(pos(x,y));
else if((!state) && liveCnt==3)
changepairSet.insert(pos(x,y));
}
}
for(pos p:changepairSet){
board[p.first][p.second]=1-board[p.first][p.second];
}
}
int getLiveCnt(vector<vector<int>>& board,int x,int y){
int live=0;
if(x-1>=0){
if(board[x-1][y])
live++;
if(y-1>=0 && board[x-1][y-1])
live++;
if(y+1<board[0].size() && board[x-1][y+1])
live++;
}
if(x+1<board.size()){
if(board[x+1][y])
live++;
if(y-1>=0 && board[x+1][y-1])
live++;
if(y+1<board[0].size() && board[x+1][y+1])
live++;
}
if(y-1>=0 && board[x][y-1])
live++;
if(y+1<board[0].size() && board[x][y+1])
live++;
return live;
}
};
class Solution {
public:
void gameOfLife(vector<vector<int>>& board) {
int m=board.size(),n=board[0].size();
set<int*> changeSet;
for(int x=0;x<m;x++){
for(int y=0;y<n;y++){
int liveCnt=getLiveCnt(board,x,y);
int state=board[x][y];
if(state && (liveCnt>3 || liveCnt<2))
changeSet.insert(&board[x][y]);
else if((!state) && liveCnt==3)
changeSet.insert(&board[x][y]);
}
}
for(int* p:changeSet){
(*p)=1-(*p);
}
}
int getLiveCnt(vector<vector<int>>& board,int x,int y){
int live=0;
if(x-1>=0){
if(board[x-1][y])
live++;
if(y-1>=0 && board[x-1][y-1])
live++;
if(y+1<board[0].size() && board[x-1][y+1])
live++;
}
if(x+1<board.size()){
if(board[x+1][y])
live++;
if(y-1>=0 && board[x+1][y-1])
live++;
if(y+1<board[0].size() && board[x+1][y+1])
live++;
}
if(y-1>=0 && board[x][y-1])
live++;
if(y+1<board[0].size() && board[x][y+1])
live++;
return live;
}
};
2020.4.2 整数转罗马数字(中等)(练习,非打卡)
思路:
把题中列举的6种特殊情况(大数左边是小数)抽出成6个特殊符号,再加上原有的7个特殊符号,总共13个,然后从小到大排序,再从大到小减去该对应的符号,不断逼近目标数字
using p=pair<string,int>;
vector<p> RomanMap={
p("I",1),
p("IV",4),
p("V",5),
p("IX",9),
p("X",10),
p("XL",40),
p("L",50),
p("XC",90),
p("C",100),
p("CD",400),
p("D",500),
p("CM",900),
p("M",1000)
};
class Solution {
public:
string intToRoman(int num) {
vector<int> v(13,0);
int Left=num;
for(int i=12;i>=0;i--){
if(Left==0)
break;
while(Left>=RomanMap[i].second){
v[i]++;
Left-=RomanMap[i].second;
}
}
string res="";
for(int i=12;i>=0;i--){
while(v[i]>0){
v[i]--;
res+=RomanMap[i].first;
}
}
return res;
}
};
2020.4.2 Z字形变换(中等)(练习,非打卡)
思路:遍历每个字符,将其分配至哪一行的问题
class Solution {
public:
string convert(string s, int numRows) {
if(numRows==1)
return s;
vector<string> matrix(numRows,"");
int y=0,isUp=0;
for(char c:s){
matrix[y]+=c;
if(!isUp)
{
y++;
if(y>=numRows-1)
isUp=1;
}else{
y--;
if(y<=0)
isUp=0;
}
}
string res;
for(string s : matrix)
res+=s;
return res;
}
};
2020.4.2 最接近的三数之和(中等)(练习,非打卡)
思路:先排序,再双指针,三个数可以变成一个数固定,另外两个双指针的问题,进行多次双指针逼近答案即可
(好蠢,思路很明确的问题,一开始方向就很明确了,但是刚开始总想着以一次双指针解决问题,过不了最后一个用例,分成多次双指针就好解决了)
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
if(nums.size()<=3)
return nums[0]+nums[1]+nums[2];
int sum=nums[0]+nums[1]+nums[2];
for(int i=0;i<nums.size();i++){
int L=i+1,R=nums.size()-1;
while(R>L){
int s=nums[i]+nums[L]+nums[R];
if(abs(target-sum)>abs(target-s))
sum=s;
if(s>target)
R--;
else if(s<target)
L++;
else
return s;
}
}
return sum;
}
};
2020.4.3 字符串转换整数(atoi)(中等)
思路:略,很简单但是繁琐很多细节的题,做起来让人很气恼。。
class Solution {
public:
int myAtoi(string str) {
string num="";
char sign=' ';
bool isBreak=0;
for(char c :str){
if(c==' '){
if(isBreak)
break;
else
continue;
}
if(c=='+'||c=='-'){
if(isBreak)
break;
else
sign=c;
isBreak=1;
}else if(c<='9' && c>='0'){
isBreak=1;
if(c=='0' && num=="")
continue;
num+=c;
}
else
break;
}
if(num.length()>10){
if(sign=='-')
return INT_MIN ;
else
return INT_MAX;
}
if(num=="")
return 0;
long l=0;
for(int i=0;i<num.size();i++){
l+=(num[i]-'0')*pow(10,num.size()-i-1);
}
if(sign=='-' && l>=(2147483648))
return INT_MIN;
if(sign!='-' && l>=INT_MAX)
return INT_MAX;
long res=l&(INT_MAX);
if(sign=='-')
return -res;
else
return res;
}
};
2020.4.3 两两交换链表中的节点(中等)(练习,非打卡)
思路:略,单纯的链表操作
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(head==NULL || head->next==NULL)
return head;
ListNode* newhead=head->next;
head->next=newhead->next;
newhead->next=head;
ListNode* cur=head;
while(cur->next!=NULL){
if(cur->next->next==NULL)
break;
ListNode* n=cur->next;
cur->next=n->next;
n->next=cur->next->next;
cur->next->next=n;
cur=n;
}
return newhead;
}
};
2020.4.4 接雨水(困难)
思路:通过栈存储索引,核心是保证栈是一个递减状态
当栈为空,或者栈顶索引对应的高度>=当前索引高度时入栈
当前索引高度>=栈顶索引对用的高度时
最低水位为当前栈顶索引对应的高度
此时维护栈,保证栈为空或者栈顶索引对应的高度>最低水位
(如果为空则当前索引入栈,判断下一个索引)
此时再维护栈,每次上升的水位为
(min(当前栈顶索引对应的高度,当前索引对应的高度)-最低水位)*宽度
然后更新最低水位,直到栈顶的索引对应高度大于当前索引对应的高度,或者栈为空
class Solution {
public:
int trap(vector<int>& height) {
int res=trap(height,0,height.size()-1);
return res;
}
int trap(vector<int>& height,int L,int R){
if(L>=R)
return 0;
int M=L;
int res=0;
stack<int> s;
while( M<=R){
if(s.empty() || height[M]<=height[s.top()])
s.push(M++);
else if(height[M]>height[s.top()]){
int minh=height[s.top()];
while(!s.empty() && height[s.top()]==minh)
s.pop();
if(s.empty())
{
s.push(M++);
continue;
}
while(!s.empty()&& M<=R && minh<=height[M])
{
int maxh=min(height[s.top()],height[M]);
int cnt=M-s.top()-1;
res+=cnt*(maxh-minh);
minh=maxh;
if(minh==height[M])
break;
else
s.pop();
}
s.push(M++);
}
}
return res;
}
};
周六双周赛:
好蠢。。。拿女朋友的电脑来不及装IDE,然后只好拿旧电脑在leetCode网站上徒手撸代码,虽然最近家里刷题都是徒手撸代码,但是可以慢慢想。。这次水平发挥实在过于失常。最终四道题只做出了三道,而且最后一道超时了4分钟(相当于只出来了两道),如果有IDE成绩应该不止如此。。气死了
2020.4.4 双周赛第一题 统计最大组的数目(简单)
思路:通过map存储每个数字各位数之和的次数,然后按值排序
class Solution {
public:
int countLargestGroup(int n) {
if(n<10)
return n;
map<int,int> m;
for(int i=1;i<=n;i++){
int sum=0;
int n=i;
while(n!=0){
sum+=n%10;
n/=10;
}
m[sum]++;
}
vector< pair<int,int> > vec;
for(auto it = m.begin(); it != m.end(); it++){
vec.push_back( pair<int,int>(it->first,it->second) );
}
sort(vec.begin(),vec.end(),cmp);
int res=0;
int maxCnt=vec[0].second;
for(auto p:vec){
if(p.second!=maxCnt)
break;
res++;
}
return res;
}
static bool cmp(pair<int,int> a, pair<int,int> b) {
return a.second > b.second;
}
};
2020.4.4 双周赛第三题 圆和矩形是否有重叠(中等)
思路:分为在矩形内和在矩形外两种情况
在矩形外则需要计算八个点:圆心和矩形的四角,以及各边中点的任一一个点的距离超过半径,则说明有重叠
在矩形内:必定重叠
判断是否在矩形内:看圆心的x是否落在矩形的x范围内,y是否落在矩形的y范围内
class Solution {
public:
bool checkOverlap(int radius, int x_center, int y_center, int x1, int y1, int x2, int y2) {
if(isBetween(x_center,x1,x2)&& isBetween(y_center,y1,y2))
return true;
if(getDis(x_center,y_center,x1,y1)<=radius)
return true;
if(getDis(x_center,y_center,x1,y2)<=radius)
return true;
if(getDis(x_center,y_center,x2,y1)<=radius)
return true;
if(getDis(x_center,y_center,x2,y2)<=radius)
return true;
if(getDis(x_center,y_center,x1,(y1+y2)/2.0)<=radius)
return true;
if(getDis(x_center,y_center,x2,(y1+y2)/2.0)<=radius)
return true;
if(getDis(x_center,y_center,(x1+x2)/2.0,y1)<=radius)
return true;
if(getDis(x_center,y_center,(x1+x2)/2.0,y2)<=radius)
return true;
return false;
}
bool isBetween(int x,int a,int b){
int max,min;
if(a>b){
max=a;
min=b;
}else{
min=a;
max=b;
}
return x<=max && x>=min;
}
double getDis(int x,int y,double a,double b){
int dis=sqrt(pow(x-a,2)+pow(y-b,2));
//cout<
return dis;
}
};
2020.4.4 双周赛第四题 做菜顺序(困难)
思路:这题想复杂了,其实很简单,刚开始想到了动态规划,后来想通之后,觉得实属自己犯傻了。首先将数组进行排序,从小到大
然后从最后开始遍历,判断当前菜品是否做出来,等价于判断当前菜品加上前面所有菜品之和加上之前的最大系数是否超过之前的最大系数即可,然后更新最大系数及前面所有菜品之和
class Solution {
public:
int maxSatisfaction(vector<int>& satisfaction) {
sort(satisfaction.begin(),satisfaction.end());
if(satisfaction[satisfaction.size()-1]<=0)
return 0;
int res=0;
int sum=0;
int R=satisfaction.size()-1;
for(int i=R;i>=0;i--){
if(0<=sum+satisfaction[i])
{
sum+=satisfaction[i];
res+=sum;
}
}
return res;
}
};
最终结果:
2020,4,5 周赛第一题 非递增顺序的最小子序列(简单)
思路:排序之后先求和从后遍历,再从后求和,每遍历一个数字,从前求和减去该数字,从后求和加上该数字,反超时返回这个子序列
class Solution {
public:
vector<int> minSubsequence(vector<int>& nums) {
sort(nums.begin(), nums.end());
int sumL = 0;
int sumR = 0;
vector<int> res;
for (int i : nums)
sumL += i;
for (int i = nums.size() - 1; i >= 0; i--) {
sumL -= nums[i];
sumR += nums[i];
res.push_back(nums[i]);
if (sumR > sumL)
return res;
}
return res;
}
};
2020.4.5 周赛 将二进制表示减到 1 的步骤数(中等)
思路:对于二进制,奇数+1则进位,偶数除2则右移1位(对于字符串就是去掉最右边),不需要考虑将字符串转成数字的问题,直接操作字符串即可
class Solution {
public:
int numSteps(string s) {
int step = 0;
getStep(s, &step);
return step;
};
void getStep(string s,int* step) {
if (s=="1")
return;
if (s[(s.size()-1)] == '1') {
int index = s.size() - 1;
while (index >= 0) {
if (s[index] == '1')
s[index--] = '0';
else {
s[index] = '1';
break;
}
}
if (index < 0)
s = '1' + s;
}
else {
s = s.substr(0, s.size() - 1);
}
(*step)++;
getStep(s, step);
}
};
2020.4.5 周赛 最长快乐字符串(中等)
思路:这题超时了。。周赛结束只做出了两道题(一如既往的做题速度慢)
这道题主要难点就是怎么减少重复代码,思路其实很简单,利用字符a,b,c和字符a相减作为索引,就不需要一堆字符的判断了,除此之外,每次插入一个字符,需要判断哪个字符被禁用,然后只需选择剩余数量最大的可选字符即可
class Solution {
public:
string longestDiverseString(int a, int b, int c) {
vector<int> cnt{ a,b,c };
string res = "";
int ban = -1;
while (1) {
if (res.size() >= 2 && res[res.size() - 1] == res[res.size() - 2])
ban = res[res.size() - 2] - 'a';//0:a 1:b 2:c
else
ban = -1;
int select = -1;
for (int i = 0; i < 3; i++) {
if (ban == i || cnt[i] == 0)
continue;
if (select == -1 || cnt[select] < cnt[i])
select = i;
}
if (select == -1)
break;
else {
res += ('a' + select);
cnt[select]--;
}
}
return res;
}
};
2020.4.5 周赛第四题 石子游戏III (困难)
思路:这题一眼看就是用动态规划(可惜还是超时了)
首先Alice 和Bob每次都是最优策略,即能让
自己获益-对方获益是一个最大值
对于dp[i]表示:在第i颗石头,不论谁拿1-3块石头,能获得自己获益-对方获益的最大值
假设在第i颗石头时,Alice或Bob选择拿的石头数量为x
则j=i+x(x=0,1,2)即只拿当前第i颗,或者(i和i+1),或者(i和i+1,i+2)
第j+1颗石头由对方进行拿取,同时dp[i+1]也是对方的最优解,
即
dp[i]=max(dp[i],sumStoneValues(i,j)- dp[j+1])
然后更新dp的方向应该是从后往前的,因为dp[i]和dp[i+1]有关
初始化:因为是求max,所以对于dp,各初值应该为一个最小值(INT_MIN),然后第一个值:dp[n]的值为0
dp[0]>0表示要想赢,应该先拿石头(因为Alice先拿所以Alice赢)
dp[0]<0表示要想赢,应该后拿石头(因为Alice先拿所以Bob赢)
dp[0]=0表示无论先后,都是平局(Tie)
class Solution {
public:
string stoneGameIII(vector<int>& stoneValue) {
int n = stoneValue.size();
vector<int> dp(n+1, INT_MIN);
dp[n] = 0;
for (int i = n - 1; i >= 0; i--) {
int s = 0;
for (int j = i; j < min(i + 3, n); j++) {
s += stoneValue[j];
dp[i] = max(dp[i], s - dp[j + 1]);
}
}
return dp[0] > 0 ? "Alice" : (dp[0] == 0 ? "Tie" : "Bob");
}
};
最终结果:(虽然都做出来了,但是没卵用,限定时间内只出来了两题)
2020.4.5 LFU缓存(困难)
思路:双哈希表,不知道如何吐槽。看似很简单的一题,做起来还是很麻烦很多需要考虑的。
emmm 做这道题得到最大的收获就是:map会自动调用构造函数。不需要从中判断这个key在map中是否存在再通过key获取value(这个比java要方便很多,C#貌似也有)
还有可能要活用迭代器和指针。c++里面这个功能还是蛮好用的,之前C#和java并没有太注重这些的使用
class LFUCache {
public:
int cap;
int minfreq = 1;
class Node {
public:
int key;
int val;
int freq;
Node(int key, int val, int freq) {
this->key = key;
this->val = val;
this->freq = freq;
}
};
unordered_map<int, list<Node>> freqMap;
unordered_map<int, list<Node>::iterator> keyMap;
LFUCache(int capacity) {
this->cap = capacity;
}
int get(int key) {
if (cap == 0 || keyMap.find(key) == keyMap.end())
return -1;
list<Node>::iterator node = keyMap[key];
int freq = node->freq;
int value = node->val;
freqMap[freq].erase(node);
if (freqMap[freq].size() == 0)
{
freqMap.erase(freq);
if (minfreq == freq)
minfreq++;
}
freqMap[freq + 1].push_front(Node(key, value, freq + 1));
keyMap[key] = freqMap[freq + 1].begin();
return value;
}
void put(int key, int value) {
if (cap == 0)
return;
if (freqMap.find(minfreq) == freqMap.end())
freqMap[minfreq] = list<Node>();
auto it = keyMap.find(key);
if (it == keyMap.end())
{
if (keyMap.size() == cap) {
auto deleteit = freqMap[minfreq].back();
keyMap.erase(deleteit.key);
freqMap[minfreq].pop_back();
if (freqMap[minfreq].size() == 0)
freqMap.erase(minfreq);
}
freqMap[1].push_front(Node(key, value, 1));
keyMap[key] = freqMap[1].begin();
minfreq = 1;
}
else {
list<Node>::iterator node = it->second;
int freq = node->freq;
freqMap[freq].erase(node);
if (freqMap[freq].size() == 0)
{
freqMap.erase(freq);
if (minfreq == freq)
minfreq++;
}
freqMap[freq + 1].push_front(Node(key, value, freq + 1));
keyMap[key] = freqMap[freq + 1].begin();
}
}
};
2020.4.6 编辑距离(困难)
思路:
一点思路都没有。。看了题解才会的
动态规划:
对于一个单词,有三种操作:删除一个字符,插入一个字符,替换一个字符
从一个单词变为另一个单词的最少次数=等价于相反过来另一个单词变为单词本身的最少次数
所以求变化的最少次数,可以不是仅修改一个单词,还可以修改另一个单词
那么现在变成了6种操作
A:删除一个字符,插入一个字符,替换一个字符
B:删除一个字符,插入一个字符,替换一个字符
其中对于A删除一个字符,等价于B插入那个被删除的字符
所以可以去掉两种操作
对于A替换一个字符,等价于B替换一个字符,所以最终只有三个操作
A:插入一个字符,替换一个字符
B:插入一个字符
然后我们建立一个矩阵
dp[i][j]表示 单词A的0-i个字母,变化成为单词B的0-j个字母需要的最少次数
初始化:
dp[0][0]=0
dp[i][0]=i(从B0个字符变为A的i个字符需要i次插入操作)
dp[0][j]=j(从A0个字符变为B的j个字符需要j次插入操作)
对于dp[i][j]
可以由dp[i-1][j] 或 dp[i][j-1]插入一个字符变化而来
即dp[i-1][j] + 1 或 dp[i][j-1] + 1
也可以由dp[i-1][j-1]的基础上修改第i/j个字符使其A的第i个字符和B的第j个字符相同(1次操作)变化而来
即dp[i-1][j-1] + 1
如果A的第i个字符 和 B的第j个字符相同时,则不需要+1
即:dp[i-1][j-1]+(A[i-1]==B[j-1])
注:第i个对应的索引是i-1
所以dp[i][j]为上述三个值中的最小值
class Solution {
public:
int minDistance(string word1, string word2) {
if (word1.size() == 0 || word2.size() == 0)
return word1.size() + word2.size();
vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));
for (int i = 1; i <= word1.size(); i++)
dp[i][0] = i;
for (int j = 1; j <= word2.size(); j++)
dp[0][j] = j;
for (int i = 1; i <= word1.size(); i++){
for (int j = 1; j <= word2.size(); j++){
dp[i][j] = min(dp[i - 1][j], min(dp[i][j - 1], dp[i - 1][j - 1] - (word1[i-1] == word2[j-1]))) + 1;
}
}
return dp[word1.size()][word2.size()];
}
};
2020.4.7 旋转矩阵(中等)
思路:
今天第一天入职新公司。整了半天入职和其他乱七八糟的手续,坐班车回宿舍已经是十点了,万幸这题很简单。。
旋转矩阵只需要考虑一层一层的去旋转,每层的边长从N,N-2,N-4…直到边长为0或者1时,最内层就不需要旋转了
然后对于每一层,只需要一次遍历,从第一个到边长-1个分别对应其他三条边的位置的总共四个数进行轮换,而第一个和最后一个的位置又和层数有关
找清楚几个坐标的关系即可
非常庆幸今天不是困难题,要不然可能就打不了卡了,完全没把握短时间内想出来。。希望这个打卡不要断
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int N = matrix.size();
int layerCnt = (N + 1) / 2;
for (int i = 0; i < layerCnt; i++){
for (int j = i; j < matrix.size() - i - 1; j++){
int n0 = matrix[i][j];
matrix[i][j] = matrix[matrix.size() - 1 - j][i];
matrix[matrix.size() - 1 - j][i] = matrix[matrix.size() - 1 - i][matrix.size() - 1 - j];
matrix[matrix.size() - 1 - i][matrix.size() - 1 - j] = matrix[j][matrix.size() - 1 - i];
matrix[j][matrix.size() - 1 - i] = n0;
}
N -= 2;
if (N <= 1)
break;
}
}
};
2020.4.8 机器人的运动范围(中等)
思路:BFS
class Solution {
public:
int movingCount(int m, int n, int k) {
vector<vector<int>> matrix(m,vector<int>(n,0));
queue<pair<int,int>> q;
q.push(pair<int,int>(0,0));
int cnt=0;
while(!q.empty()){
pair<int,int> p=q.front();
q.pop();
int x=p.first,y=p.second;
if(matrix[x][y])
continue;
cnt++;
int sumx=getSum(x),sumy=getSum(y);
matrix[x][y]=1;
if(x-1>=0 && !matrix[x-1][y] && getSum(x-1)+sumy<=k)
q.push(pair<int,int>(x-1,y));
if(x+1<m && !matrix[x+1][y] && getSum(x+1)+sumy<=k)
q.push(pair<int,int>(x+1,y));
if(y-1>=0 && !matrix[x][y-1] && sumx + getSum(y-1)<=k)
q.push(pair<int,int>(x,y-1));
if(y+1<n && !matrix[x][y+1] && sumx + getSum(y+1)<=k)
q.push(pair<int,int>(x,y+1));
}
return cnt;
}
int getSum(int num){
int sum=0;
while(num!=0){
sum+=num%10;
num/=10;
}
return sum;
}
};
2020.4.9 括号生成(中等)
思路:递归(难道4月就周末才是难题嘛。。那我就放心了)
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> res;
getStr(res,n,1,0,"(");
return res;
}
inline void getStr(vector<string> &res,int n,int l,int r,string input){
if(l==n && r==n)
{
res.push_back(input);
return;
}
if(l<n)
getStr(res,n,l+1,r,input+"(");
if(l>r)
getStr(res,n,l,r+1,input+")");
}
};
2020.4.10 翻转字符串里的单词
思路:略
class Solution {
public:
string reverseWords(string s) {
if(s.size()==0)
return "";
string res,cur;
for(char c: s){
if(c==' '&& cur!=""){
res=" "+cur+res;
cur="";
}else if(c!=' ')
cur+=c;
}
if(cur!="")
res=cur+res;
else if(res.size()>0)
res=res.substr(1,res.size()-1);
return res;
}
};
2020.4.11 鸡蛋掉落(困难)
思路:
动态规划
题目:K个鸡蛋,N层楼,在某层楼X之前,鸡蛋掉落不会摔碎,超过X后,会摔碎,求确定X的最小次数
首先对于dp(k,n)表示k个鸡蛋,n层楼时的次数
存在以下关系:
假定在x层掉落(x
dp(k,n)=dp(k,n-x)+1;即能摔碎的楼层数在x层之上
2.蛋碎了
dp(k,n)=dp(k-1,x-1)+1;即能摔碎的楼层数在x层或x层以下
所以综上,dp(k,n)的值应该为这两个的最大值即:
dp(k,n)=max(dp(k,n-x),dp(k-1,x-1))+1
即最坏情况下的次数+1
同时,这个x究竟是多少,这个x可以是1-n的中的任意一个值,x越小,越接近当前n
这一系列值中存在一个最优解,使得
max(dp(k,n-x),dp(k-1,x-1))是最小的
即最坏情况下的最小值+1
首先x:必定是在1-(n-1)之间的一个数,如果全部进行一个比较,则会太慢了
要如何找到这个最优解x
首先:
对于dp(k,n-x) 随着n的增大,是一个递增的数(拿一个鸡蛋进行理解就行了,你必须从第一层,第二层这样一层一层往下试)
对于dp(k-1,x-1) 随着n的增大,永远不会改变(因为这个只和x有关,和n无关)
那么:反过来,当我们锁定了n,x越大,dp(k,n-x)是递减的,dp(k-1,x-1)是递增的
我们不看k和k-1
n-x 和x-1,是一个交叉的(相反递减性),存在一个交叉点,使得这个交叉点是最坏情况的最优解
这个交叉点就在中间
所以只需要从最接近n的地方往回遍历,即可找到最优x
另外:由于k<=100,而每层dp只和上一层有关,所以没必要建立一个过大的数组,只需要用两个数组,表示当前和上一层dp即可
class Solution {
public:
int superEggDrop(int K, int N) {
vector<int> dp(N + 1, 0);
for (int i = 1; i <= N; i++)
dp[i] = i;
for (int e = 2; e <= K; e++){
vector<int> dp2(N + 1, 0);
int x = 1;
for (int n = 1; n <= N; n++){
while (x < n && max(dp[x - 1], dp2[n - x])>max(dp[x], dp2[n - x - 1]))
x++;
dp2[n] = max(dp[x - 1], dp2[n - x]) + 1;
}
dp = dp2;
}
return dp[N];
}
};
2020.4.12 交点(困难)
思路:
看似是很简单的数学,但是实际需要考虑的很多:
首先将这两条线段两头分别都延申,这样就得到两条直线,两条直线要么平行,要么必有交点,那么我们只需要找出非平行的情况下,交点是否都落在了这两条线段内
比如当斜率无法计算(Δx=0)时,应该如何处理
当斜率相同或者两条线段均垂直于x轴时,要判断究竟是平行还是重叠
当两个double斜率在数学上计算结果相同,但是由于计算机的小数点计算方式,可能会导致两个斜率存在细微差异,这时要用乘法代替除法
当有多个解时(即存在重叠),如何按照答案选择x最小/y最小的点
做起来是一道很繁琐的题,想清楚之后其实还是没有难度的
class Solution {
public:
vector<double> intersection(vector<int>& start1, vector<int>& end1, vector<int>& start2, vector<int>& end2) {
double xA = start1[0], yA = start1[1];
double xB = end1[0], yB = end1[1];
double xC = start2[0], yC = start2[1];
double xD = end2[0], yD = end2[1];
vector<double> res;
if (xA == xB || xC == xD)
{
if (xA == xB && xC == xD)
{
if (xA != xC)
return res;
else{
int min1 = min(yA, yB);
int max1 = max(yA, yB);
int min2 = min(yC, yD);
int max2 = max(yC, yD);
if (max1 < min2 || max2 < min1)
return res;
double miny = max(min1, min2);
res.push_back(xA);
res.push_back(miny);
return res;
}
}
if (xA == xB)
{
double minX = min(xC, xD);
double maxX = max(xC, xD);
if (xA > maxX || xA < minX)
return res;
double slope = (yC - yD) / (xC - xD);
double y = slope*(xA - xC) + yC;
double minY = min(yA, yB);
double maxY = max(yA, yB);
if (y>maxY || y < minY)
return res;
res.push_back(xA);
res.push_back(y);
return res;
}
else{
double minX = min(xA, xB);
double maxX = max(xA, xB);
if (xC>maxX || xC < minX)
return res;
double slope = (yA - yB) / (xA - xB);
double y = slope *(xC - xB) + yB;
double minY = min(yC, yD);
double maxY = max(yC, yD);
if (y>maxY || y < minY)
return res;
res.push_back(xC);
res.push_back(y);
return res;
}
}
double slopeAB = (yA - yB) / (xA - xB);
double slopeCD = (yC - yD) / (xC - xD);
if ((yA - yB)*(xC - xD) == (xA - xB)*(yC - yD)){
if ((yA - yB)*(xC - xB) != (yC - yB)*(xA - xB) || (yA - yB)*(xD - xB) != (yD - yB)*(xA - xB))
return res;
int min1 = min(xA, xB);
int max1 = max(xA, xB);
int min2 = min(xC, xD);
int max2 = max(xC, xD);
if (max1 < min2 || max2 < min1)
return res;
double minX = max(min1, min2);
double y = slopeAB*(minX - xB) + yB;
res.push_back(minX);
res.push_back(y);
return res;
}
double x = ((yC - yB) - slopeCD*xC + slopeAB*xB) / (slopeAB - slopeCD);
double y = slopeCD *(x - xC) + yC;
double minX = min(xA, xB);
double maxX = max(xA, xB);
if (x > maxX || x < minX)
return res;
minX = min(xC, xD);
maxX = max(xC, xD);
if (x > maxX || x < minX)
return res;
res.push_back(x);
res.push_back(y);
return res;
}
};
2020.4.12 解数独(困难)(练习,非打卡)
思路:回溯法
从第一行第一个开始,从左到右,到了最右边再回到下一行最左边以此类推,直至到达最后一个格子
如果发现找不到可以填写的数字,那么则返回上一步,上一步换另一个数字填,直至最终到达最后一个格子,每个格子都填了一个数
难点在于怎么储存行,列,格子的状态,其他还好
class Solution {
public:
int row[9][10];
int column[9][10];
int cell[3][3][10];
bool canReach = false;
void solveSudoku(vector<vector<char>>& board) {
for (int x = 0; x < 9; x++){
for (int y = 0; y < 9; y++){
if (board[y][x] != '.'){
row[y][board[y][x] - '0'] = 1;
column[x][board[y][x] - '0'] = 1;
cell[y / 3][x / 3][board[y][x] - '0'] = 1;
}
}
}
Place(0, 0, board);
}
void Place(int x, int y, vector<vector<char>>& board){
bool find = 0;
if (board[y][x] == '.'){
for (int i = 1; i <= 9; i++){
if (canSet(x, y, i)){
Set(x, y, i, board);
setNext(x, y, board);
if (!canReach)
erase(x, y, board);
}
}
}
else
setNext(x, y, board);
}
void setNext(int x, int y, vector<vector<char>>& board){
if (x == 8 && y == 8)
{
canReach = true;
return;
}
if (x == 8){
x = 0;
y++;
}
else
x++;
Place(x, y, board);
}
bool canSet(int x, int y, int val){
return row[y][val] + column[x][val] + cell[y / 3][x / 3][val] == 0;
}
void Set(int x, int y, int val, vector<vector<char>>& board){
row[y][val] = 1;
column[x][val] = 1;
cell[y / 3][x / 3][val] = 1;
board[y][x] = val + '0';
}
void erase(int x, int y, vector<vector<char>>& board){
int val = board[y][x] - '0';
row[y][val] = 0;
column[x][val] = 0;
cell[y / 3][x / 3][val] = 0;
board[y][x] = '.';
}
};
2020.4.12 全排列II(中等)
思路:也是回溯
不需要管原数组的关系
假设有n个数,n个数的排列方式:首先不考虑重复数字的情况下,假设每个数字都是独特的
那么第一个数有n种可能性,第二个数n-1 。。。。
即n!个
从第a个数开始,和第a个数及以后的数进行交换(和自身交换相当于没变,也是一种交换方式)
每交换一次,下一次交换要从a+1开始
当交换完n次后,得到的结果就是其中一种排列方式
如何去重:
我们确定第a个时,先判断这个数字是否进行交换过,才进行下一次交换,用一个set进行存储
比如:对于数组[1,2,1,1]
确定第一个数
自身和自身进行交换,存到set中,同时进行回溯找出第二个数字的交换方式
下一个选择:2
1和2进行交换,将2存到set中,同时进行回溯找出第二个数字的交换方式
后面的两个选择:1,1
1已经交换过了所以不产生任何回溯
假设第一个数自身和自身进行交换了
到第二个数
2和自身交换,或和第三个数1进行交换
第二个数2和第三个数1交换时:
判断第三个数:
2和自身交换或者2和第四个数1交换
总共有4种结果
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> permuteUnique(vector<int>& nums) {
swapNum(nums, 0);
return res;
}
void swapNum(vector<int> &n, int index){
if (index == n.size())
{
res.push_back(n);
return;
}
set<int> s;
for (int i = index; i < n.size(); i++){
if (s.find(n[i]) != s.end())
continue;
swap(n[index], n[i]);
swapNum(n, index + 1);
swap(n[index], n[i]);
s.insert(n[i]);
}
}
};
2020.4.13 设计推特(中等)
思路:
每个人维护最近十条推特信息,存储其发表时间和id(pair)
然后维护一个关注set
当获取推文时,找出自身和关注的发表时间最高的前10条即可
这时可以用优先队列,优先队列支持pair的大根堆
将所有推文入队列即可(如果可以,改成小根堆的话,可以省更多的空间,实际使用中可能要根据具体情况选择)
class Twitter {
public:
/** Initialize your data structure here. */
Twitter() {
timestamp = 0;
}
struct Node {
// 哈希表存储关注人的 Id
unordered_set<int> followee;
// 用链表存储 tweetID
deque<pair<int, int>> tweetInfo;
};
map<int, Node> user;
int timestamp;
/** Compose a new tweet. */
void postTweet(int userId, int tweetId) {
user[userId].tweetInfo.push_back(pair<int, int>(timestamp, tweetId));
if (user[userId].tweetInfo.size() > 10)
{
user[userId].tweetInfo.pop_front();
}
timestamp++;
}
/** Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. */
vector<int> getNewsFeed(int userId) {
priority_queue<pair<int, int>> q;
for (auto i : user[userId].tweetInfo){
q.push(i);
}
for (auto u : user[userId].followee){
if (u == userId)
continue;
auto ulist = user[u].tweetInfo;
for (auto i : ulist)
q.push(i);
}
vector<int> ret;
while (!q.empty() && ret.size() < 10){
ret.push_back(q.top().second);
q.pop();
}
return ret;
}
/** Follower follows a followee. If the operation is invalid, it should be a no-op. */
void follow(int followerId, int followeeId) {
user[followerId].followee.insert(followeeId);
}
/** Follower unfollows a followee. If the operation is invalid, it should be a no-op. */
void unfollow(int followerId, int followeeId) {
user[followerId].followee.erase(followeeId);
}
};
2020.4.14 两数相加(中等)
思路:
两个数是两个链表形式进行存储的,而且还是单向,所以就需要利用到栈,分别入栈,栈顶是最高位,然后注意进位问题即可
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
stack<ListNode*> s1,s2;
ListNode* cur=l1;
while(cur != NULL){
s1.push(cur);
cur=cur->next;
}
cur=l2;
while(cur != NULL){
s2.push(cur);
cur=cur->next;
}
if(s1.size()<s2.size())
{
swap(s1,s2);
swap(l1,l2);
}
bool isadd=0;
while(!s1.empty() && !s2.empty()){
if(s2.empty())
break;
ListNode* top1=s1.top();
ListNode* top2=s2.top();
top1->val=(top2->val+top1->val+isadd);
if(top1->val>=10){
isadd=1;
top1->val%=10;
}else
isadd=0;
s1.pop();
s2.pop();
}
if(isadd){
ListNode* ret;
while(!s1.empty()){
ret=s1.top();
ret->val++;
if(ret->val<10)
return l1;
ret->val%=10;
s1.pop();
}
ListNode* n=(new ListNode(1));
n->next=l1;
return n;
}
else
return l1;
}
};
2020.4.15 01矩阵(中等)
思路:
双队列的BFS
可以简单想象成画一个地理课上的等高线地形图:
第一层的海拔为1,第二层的海拔为2,如此类推
主要难点在于如何区分未标识的和已标识的数字(都是1)
只需要刚开始时对整个矩阵进行一次遍历,是0的入队列,是1的改为-1即可
class Solution {
public:
vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
queue<pair<int, int>> q;
for (int y = 0; y < matrix.size(); y++)
for (int x = 0; x < matrix[y].size(); x++){
if (matrix[y][x] == 0){
q.push(pair<int, int>(y, x));
}
else
matrix[y][x] = -1;
}
int height = 1;
while (!q.empty()){
queue<pair<int, int>> nextq;
while (!q.empty()){
auto p = q.front();
q.pop();
int y = p.first, x = p.second;
if (x - 1 >= 0 && matrix[y][x - 1] == -1){
matrix[y][x - 1] = height;
nextq.push(pair<int, int>(y, x - 1));
}
if (x + 1 < matrix[0].size() && matrix[y][x + 1] == -1){
matrix[y][x + 1] = height;
nextq.push(pair<int, int>(y, x + 1));
}
if (y - 1 >= 0 && matrix[y - 1][x] == -1){
matrix[y - 1][x] = height;
nextq.push(pair<int, int>(y - 1, x));
}
if (y + 1 < matrix.size() && matrix[y + 1][x] == -1){
matrix[y + 1][x] = height;
nextq.push(pair<int, int>(y + 1, x));
}
}
swap(q, nextq);
height++;
}
return matrix;
}
};
2020.4.15 组合总和(中等)(练习,非打卡)
思路:有方向的回溯,每下一层可由自身开始(因为题意允许重复)
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
getSum(vector<int>(), 0, candidates, 0, target);
return res;
}
void getSum(vector<int> cur, int index, vector<int>& cans, int sum, int target){
if (sum == target){
res.push_back(cur);
return;
}
for (int i = index; i < cans.size(); i++){
if (sum + cans[i] <= target){
vector<int> next = cur;
next.emplace_back(cans[i]);
getSum(next, i, cans, sum + cans[i], target);
}
}
}
};
2020.4.16 合并区间(中等)
思路:
首先要对区间进行排序,排序完之后再对其合并即可,值得注意的一点是:
题目返回的是vector类型,而vector在内存中是连续的,所以不能直接对其进行删除(如果是list的话就需要删除,但是相应的,排序就会花更多的时间),正确的做法应该是另建一个vector,将其放入(牺牲空间换取时间,因为连续的内存删除元素效率会很低)
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if (intervals.size() <= 1)
return intervals;
sort(intervals.begin(), intervals.end(), [](vector<int> a, vector<int> b){
return b[0] - a[0] > 0;
});
int i = 0;
vector<vector<int>> res;
while (i + 1 < intervals.size()){
if (intervals[i][0] == intervals[i + 1][0])
{
intervals[i + 1][0] = intervals[i][0];
intervals[i + 1][1] = max(intervals[i][1], intervals[i + 1][1]);
}
else if (intervals[i][1] >= intervals[i + 1][0]){
intervals[i + 1][1] = max(intervals[i][1], intervals[i + 1][1]);
intervals[i + 1][0] = intervals[i][0];
}
else
res.push_back(intervals[i]);
i++;
}
res.push_back(intervals[i]);
return res;
}
};
2020.4.17 跳跃游戏(中等)
思路:贪心算法
何谓贪心?这个一直不太好理解。。感觉还是很虚的一个概念,但是做了这题之后,感觉也就这么一回事
贪心就是:给你1块看着手里的五毛就把手里的丢掉了,拿1块,然后再发现前面有5块就再换
在这题中:
假设第一个数,你能覆盖的范围是i+num[i]
在i - i+num[i] 这num[i]个数中,至少存在另一个,能够到达比i+num[i]更远的地方,我们做的只需要不断地确定这个边界,当这个边界能覆盖最后一个数字时,return true
class Solution {
public:
bool canJump(vector<int>& nums) {
int L = 0;
int R = nums[0];
while (1){
if (R >= nums.size()-1)
return true;
for (int i = L+1; i <= R; i++){
if (i + nums[i]>R){
L = i;
R = i + nums[i];
break;
}
else
L++;
}
if (L >= R)
return false;
}
return false;
}
};
2020.4.17 组合总和II (中等)(练习,非打卡)
思路:和上一题组合总和非常像。
思路是递归
首先:这题不允许一个数字重复出现在一个组合中,那么,我们最好的做法就是先将其进行排序
排序之后,当前总和加上下一个索引小于目标值,则继续开下一个分支
和总和I还有一点区别就是索引不包括自身
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end());
getSum(vector<int>(), 0, candidates, 0, target);
return res;
}
void getSum(vector<int> cur, int index, vector<int>& cans, int sum, int target){
if (sum == target){
res.push_back(cur);
return;
}
for (int i = index ; i < cans.size(); i++){
if (sum + cans[i] <= target){
vector<int> next = cur;
next.emplace_back(cans[i]);
getSum(next, i+1, cans, sum + cans[i], target);
while(i+1<cans.size() && cans[i]==cans[i+1])
i++;
}
}
}
};
2020.4.18 盛水最多的容器(中等)
思路:双指针
先从一头一尾开始
什么时候左指针需要右移?
当左边是矮的一侧时(决定高度),这时需要记录此时左边的高度,然后右移找到一个比左边还高的时候,更新盛水容量(当然有可能此时容量不一定比之前要多)
当右边是矮的一侧时,同理,右指针左移,直到找到比原先更高的时候更新
这两种情况,因为高度<=原指针对应高度时,判断容量是没有意义的,因为另一侧高,自身是决定高度的矮的一侧,找个更矮的而且左右指针更近了,必定是更小的
class Solution {
public:
int maxArea(vector<int>& height) {
int L=0,R=height.size()-1;
int h=min(height[L],height[R]);
int maxarea=h*(R-L);
bool findL=height[L]==h;
while(R>L){
if(findL)
{
if(height[++L]<h)
continue;
}
else if(height[--R]<h)
continue;
h=min(height[L],height[R]);
findL=height[L]==h;
maxarea=max(maxarea,h*(R-L));
}
return maxarea;
}
};
2020.4.19 统计重复个数(困难)
思路:
这题看似思路很明确,但是很难,属于那种可意会不可言传的题目(不太容易用代码表达)
要找循环段,
首先先假设:
s1 和s2的第一个字母是相同的情况下
我们有两种情况:
找到circle1个s1,从中能提取到circle2个s2
并且,circle+1个s1的第一个字母开启下一个循环
这样我们就能得到
n1/circle1circle2 个s2
然后结果则是
n1/circle1circle2 /n2
这个循环开始的索引不在第一个时?
我们需要对s2的索引维护一个map,存储索引对应的circle1,circle2的信息
当这个索引出现过一次时,说明我们找出了这个循环段
第一次出现时:
记录了n1个s1中消耗了多少个s1,从中获取到了多少个s2
第二次出现时:
和第一次出现的相减,说明循环段中有多少个s1,能获取到多少个s2
将这两个出现的键值对记为loopS 和loopE
总的数量为:
int cnt = loopS.second + (n1 - loopS.first) / loopE.first * loopE.second;
之后不能构成循环的段:可能还能构成一定数量的s2,所以后面直接遍历即可
最终我们得到了n1个s1能构成多少个s2,然后再除以n2就是结果
class Solution {
public:
int getMaxRepetitions(string s1, int n1, string s2, int n2) {
int index = 0;
int circle1 = 0;
int circle2 = 0;
if (n1 == 0)
return 0;
map<int, pair<int, int>> repeat;
pair<int, int> loopS, loopE;
while (true) {
for (char s : s1) {
if (s == s2[index])
index++;
if (index >= s2.size()) {
index = 0;
circle2++;
}
}
circle1++;
if (circle1 == n1)
return circle2/n2;
if (repeat.count(index)) {
loopS = repeat[index];
loopE = { circle1 - loopS.first,circle2 - loopS.second };
break;
}
else
repeat[index] = { circle1,circle2 };
}
int cnt = loopS.second + (n1 - loopS.first) / loopE.first * loopE.second;
int restn1 = (n1 - loopS.first) % loopE.first;
while (--restn1 >= 0) {
for (char s : s1) {
if (s == s2[index])
index++;
if (index >= s2.size()) {
index = 0;
cnt++;
}
}
}
return cnt/n2;
}
};
2020.4.20 岛屿数量(中等)
思路:
Bfs
对于每次访问过的位置将其置为0即可避免重复
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
queue<pair<int,int>> q;
int cnt;
for(int Y=0;Y<grid.size();Y++){
for(int X=0;X<grid[0].size();X++){
if(grid[Y][X]=='1'){
q.push({Y,X});
while(!q.empty()){
pair<int,int> p = q.front();
int x=p.second,y=p.first;
q.pop();
if(grid[y][x]=='0')
continue;
grid[y][x]='0';
if(x-1>=0 && grid[y][x-1]=='1')
q.push({y,x-1});
if(x+1<grid[0].size() && grid[y][x+1]=='1')
q.push({y,x+1});
if(y-1>=0 && grid[y-1][x]=='1')
q.push({y-1,x});
if(y+1<grid.size() && grid[y+1][x]=='1')
q.push({y+1,x});
}
cnt++;
}
}
}
return cnt;
}
};
2020.4.20 传递信息(简单)(练习,非打卡)
思路:这题居然是简单。。当时把这道题想复杂了,还以为要用到图什么的,想清楚之后其实也就那么回事,思路是指定长度的DFS,借助递归,加快效率
class Solution {
public:
int numWays(int n, vector<vector<int>>& relation, int k) {
vector<unordered_set<int>> relv(n);
int cnt;
for(auto r :relation){
relv[r[0]].insert(r[1]);
}
update(0,1,k,relv,n);
return c;
}
int c;
void update(int lastNode,int len,int k,vector<unordered_set<int>>& relv,int n){
if(len>k)
{
if(lastNode==n-1)
c++;
return;
}
for(auto to :relv[lastNode])
{
update(to,len+1,k,relv,n);
}
return;
}
};
2020.4.21 统计优美子数组(中等)
思路:只需要关注每个奇数左边有多少个偶数,和最后一段有多少个偶数
对于给定k,中间无论有多少个奇数,长度都是固定的,会变的只有头尾,(所以头左边的偶数个数+1)x(尾右边的偶数个数+1)即为其中一组优美子数组数量
class Solution {
public:
int numberOfSubarrays(vector<int>& nums, int k) {
int Ocnt=0;
vector<int> leftV;
for(int i=0;i<nums.size();i++){
if(nums[i]%2==0)
Ocnt++;
else{
leftV.push_back(Ocnt);
Ocnt=0;
}
}
leftV.push_back(Ocnt);
int cnt=0;
for(int i=0;i + k<leftV.size();i++){
cnt+=(leftV[i]+1)*(leftV[i+k]+1);
}
return cnt;
}
};
2020.4.22 二叉树的右视图(中等)
思路:
DFS,并且是从左到右的方向,每次根据栈的长度覆盖数组的对应位置
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
vector<int> v;
if(root==NULL)
return v;
v.push_back(root->val);
stack<TreeNode* > s;
s.push(root);
unordered_set<TreeNode*> visited;
while(!s.empty()){
if(s.top()->left!=NULL && visited.find(s.top()->left)==visited.end())
{
s.push(s.top()->left);
continue;
}
else if(s.top()->right!=NULL && visited.find(s.top()->right)==visited.end())
{
s.push(s.top()->right);
continue;
}
while(v.size()<s.size())
v.push_back(0);
v[s.size()-1]=s.top()->val;
TreeNode* top=s.top();
s.pop();
visited.insert(top);
}
return v;
}
};
2020.4.23 硬币(中等)
思路:动态规划,每次一种硬币,计算到达i元的组合个数
对于1元,可以省去遍历
class Solution {
const int mod=1000000007;
public:
int waysToChange(int n) {
int coins[3]{5,10,25};
vector<int> dp(n+1,1);
for(int i=0;i<3;i++){
for(int j=1;j<=n;j++){
if(j-coins[i]>=0){
dp[j]=(dp[j]+dp[j-coins[i]])%mod;
}
}
}
return dp[n];
}
};
2020.4.24 数组中的逆序对(困难)
思路:归并排序,分治思想:先分后治
对数组一分二,不能再分后再对其进行排序,排序完的两个子数组再进行排序
逆序对指:左边大右边小,
那么我们就需要对其进行顺序排序,找出左边比右边大的数,每找出一个,产生的逆序对数就是右边剩余的个数
(想像一个这样场景:左右两队在打擂台,两边都是从小到大的,然后谁被比下去了,右边最小的被比下去后,换了个更大的作为右边最小,这说明,这个之前的都是比左边最小的要小的,当右边最小≥左边最小时,说明产生了逆序对,右边迄今为止消耗的都比当前左边最小的要小)
class Solution {
public:
int reversePairs(vector<int>& nums) {
return mergeSort(0,nums.size()-1,nums);
}
int mergeSort(int L,int R,vector<int> &num){
if(L>=R)
return 0;
int i=0,i1=L,mid=L+(R-L)/2,i2=mid+1;
int cnt=mergeSort(L,mid,num) + mergeSort(mid+1,R,num);
int temp[R-L+1];
while(i1<=mid && i2<=R){
cnt+=num[i1]<=num[i2]?i2-mid-1:0;
temp[i++]=num[i1]<=num[i2]?num[i1++]:num[i2++];
}
while(i1<=mid)
{
cnt+=i2-mid-1;
temp[i++]=num[i1++];
}
while(i2<=R)
{
temp[i++]=num[i2++];
}
int index=L;
for(int i:temp){
num[index++]=i;
}
return cnt;
}
};
2020.4.25 全排列(中等)
思路:回溯
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> res;
getpermute(nums, vector<int>(), res);
return res;
}
void getpermute(vector<int> nums, vector<int> current,vector<vector<int>> &res){
if (nums.size() == 0)
{
res.push_back(current);
return;
}
for (int i = 0; i < nums.size(); i++){
vector<int> nextnums = nums;
nextnums.erase(nextnums.begin() + i);
vector<int> nextCurrent=current;
nextCurrent.push_back(nums[i]);
getpermute(nextnums,nextCurrent , res);
}
}
};
2020.4.26 合并K个排序链表(困难)
思路:优先队列
class Solution {
public:
struct cmp{
bool operator () (ListNode* a, ListNode* b)
{
return a->val - b->val>=0;
}
};
ListNode* mergeKLists(vector<ListNode*>& lists) {
priority_queue<ListNode*,vector<ListNode*>,cmp> q;
for(auto node : lists){
auto n=node;
while(n!=NULL){
q.push(n);
n=n->next;
}
}
ListNode head(0);
ListNode* cur=&head;
while(!q.empty()){
//cout<val<<"->"<next<<" ";
auto top=q.top()->val;
cur->next=new ListNode(top);
cur=cur->next;
q.pop();
}
return head.next;
}
};
2020.4.26 去除重复字母/不同字符的最小子序列(困难/中等)(练习,非打卡)
思路:利用栈
首先记录每个字符最后出现的索引(因为字符串仅由小写字母组成,只需要一个长度为26的数组存储即可,不需要使用map)
遍历字符串每一个字符,当前字符比栈顶字符要大,则入栈,否则判断当前栈顶所属的字符在后面是否出现(就需要用到记录的最后出现的索引了)
如果后面仍有出现,则弹出,直到栈为空或者当前栈顶的字符以后不会再出现
class Solution {
public:
string removeDuplicateLetters(string str) {
vector<int> last(26,0);
vector<char> used(26,0);
for(int i=0;i<str.size();i++)
last[str[i]-'a']=i;
stack<char> s;
for(int i=0;i<str.size();i++){
if(!used[str[i]-'a']){
while(!s.empty() && str[i]<s.top() && last[s.top()-'a']>i)
{
used[s.top()-'a']=0;
s.pop();
}
used[str[i]-'a']=1;
s.push(str[i]);
}
}
string res="";
while(!s.empty())
{
res=s.top()+res;
s.pop();
}
return res;
}
};
2020.4.26 字符串相乘(中等)(练习,非打卡)
思路:
字符串相乘,先两两相乘,再处理进位
class Solution {
public:
string multiply(string num1,string num2){
if(num1.size()<num2.size())
swap(num1,num2);
if(num2=="0" || num1=="0")
{
return "0";
}
vector<int> v(num1.size()+num2.size(),0);
for(int i=0;i<num2.size();i++){
for(int j=0;j<num1.size();j++){
v[i+j+1]+=(num2[i]-'0')*(num1[j]-'0');
}
}
int add=0;
for(int i=v.size()-1;i>=0;i--){
int add1=(v[i]+add)/10;
v[i]=(v[i]+add)%10;
add=add1;
}
int startindex=0;
while(v[startindex]==0)
startindex++;
string res;
for(int i=startindex;i<v.size();i++){
char c=v[i]+'0';
res+=c;
}
return res;
}
};
2020.4.26 搜索插入位置(简单)(练习,非打卡)
思路:
二分查找
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
if(nums.empty()|| target<=nums[0]){
return 0;
}else if(target>nums.back())
{
return nums.size();
}else if(target==nums.back())
return nums.size()-1;
int L=0,R=nums.size()-1;
int mid;
while(R>L){
mid=L+(R-L)/2;
if(mid==L || mid==R)
break;
if(nums[mid]==target)
return mid;
else if(nums[mid]>target)
R=mid;
else
L=mid;
}
if(nums[mid]>target){
return mid;
}else
{
return mid+1;
}
}
};
2020.4.27 搜索旋转排序数组(中等)
思路:
本质还是二分查找,区别在于二分查找跨越了转折点的情况
可以想象成是两个直角梯形摆在同一水平线上,矮的一边都靠左侧,同时左边的直角梯形比右边的要更高
图解推算出条件即可
class Solution {
public:
int search(vector<int>& nums, int target) {
//0-x 递增 x-n 递增
int L=0,R=nums.size()-1;
if(nums.size()<=0)
return -1;
if(nums[0]==target)
return 0;
if(nums.back()==target)
return nums.size()-1;
while(R-L>1){
int mid=L+(R-L)/2;
if(nums[mid]==target)
return mid;
if(nums[R]<nums[L]){//出现旋转点
if(nums[mid]<nums[R]){
if(target<nums[mid] ||target >nums[L])
{
R=mid;
continue;
}else
{
L=mid;
continue;
}
}else{
if(target<nums[mid])
{
if(target>nums[L])
R=mid;
else
L=mid;
continue;
}
else{
L=mid;
continue;
}
}
}else{//正常递增
if(target<nums[mid]){
R=mid;
continue;
}else if(target>nums[mid]){
L=mid;
continue;
}
}
}
for(int i=L;i<=R;i++){
if(nums[i]==target)
return i;
}
return -1;
}
};
2020.4.28 数组中数字出现的次数(中等)
思路:
利用map(比较挫的方法)
看题解最优解应该是利用异或(相同为0,不同为1,某个数字和自身异或结果就是0)
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
map<int,int> m;
for(int i: nums)
{
m[i]++;
}
vector<int> res;
for(pair<int,int> p : m){
if(p.second==1)
res.push_back(p.first);
}
return res;
}
};
2020.4.29 山脉数组中查找目标值(困难)
思路:
二分查找,首先先找山峰(最大值)
再对两边二分查找找目标值
即两次或三次二分查找(如果左边找不到则三次)
注意这三次的二分查找的边界缩短都有所不同,首先找最大值:需要判断Mid和Mid+1的关系
如果递增,则左边缩进,递减则右边缩进
左边找目标值:
因为左边是递增,如果中值大于目标值,则右边缩进,小于目标值则左边缩进
右边找目标值:
和左边完全相反(因为右边是递减)
class Solution {
public:
int findInMountainArray(int target, MountainArray &mountainArr) {
int L = 0, R = mountainArr.length()-1;
int M;
int valM,valM_1;
while ( L+1 < R){
M = L + (R - L) / 2;
valM = mountainArr.get(M);
valM_1 = mountainArr.get(M + 1);
if (valM < valM_1)
L = M;
else
R = M;
}
if(valM_1>valM)
M++;
int LIndex = search(mountainArr, 0, M, target,1);
if (LIndex != -1)
return LIndex;
int RIndex = search(mountainArr, M, mountainArr.length()-1, target,0);
if (RIndex != -1)
return RIndex;
return -1;
}
int search(MountainArray &m, int L, int R, int target,int isLeft){
int val;
while (L + 1 < R){
int M = L + (R - L) / 2;
val = m.get(M);
if (val > target)
{
if(isLeft)
R = M;
else
L = M;
}
else if (val < target)
{
if(isLeft)
L = M;
else
R = M;
}
else
return M;
}
if (m.get(L) == target)
return L;
else if (R != L && m.get(R) == target)
return R;
else
return -1;
}
};
2020.4.30 搜索插入位置(简单)
思路:通过hashset,如果出现重复的数字,则说明是个循环
class Solution {
public:
bool isHappy(int n) {
set<int> s;
while (s.find(n) == s.end()){
if (n == 1)
return 1;
s.insert(n);
string str = to_string(n);
int nextn = 0;
for (char c : str){
nextn += (c - '0')*(c - '0');
}
n = nextn;
}
return 0;
}
};