第一遍复习时间 08-17
第二遍复习时间 08-22
class CQueue {
public:
stack<int> stack1;
stack<int> stack2;
CQueue() {
}
void appendTail(int value) {
stack1.push(value);
}
int deleteHead()
{
if (stack1.empty()) return -1;
//1首先把stack1 循环取出放入第二个
while(stack1.empty()!=true )
{
int temp = stack1.top();
stack1.pop();
stack2.push(temp);
}
//删除最久的元素 保存返回值
int res = stack2.top();
stack2.pop();
//复原
while(stack2.empty()!=true)
{
int temp = stack2.top();
stack2.pop();
stack1.push(temp);
}
return res;
}
};
/**
* Your CQueue object will be instantiated and called as such:
* CQueue* obj = new CQueue();
* obj->appendTail(value);
* int param_2 = obj->deleteHead();
*/
class CQueue {
public:
stack<int> stack1;
stack<int> stack2;
CQueue() {
}
//尾部添加数据
void appendTail(int value)
{
stack1.push(value);
}
//头部删除数据
int deleteHead()
{
int result=-1;
while(stack1.empty()!=true)
{
stack2.push(stack1.top());
stack1.pop();
}
if(stack2.empty()!=true)
{
result = stack2.top();
stack2.pop();
}
else
{
return -1;
}
while(stack2.empty()!=true)
{
stack1.push(stack2.top());
stack2.pop();
}
return result;
}
};
/**
* Your CQueue object will be instantiated and called as such:
* CQueue* obj = new CQueue();
* obj->appendTail(value);
* int param_2 = obj->deleteHead();
*/
class MinStack {
public:
stack<int> stack1;
stack<int> stack2;//辅助
/** initialize your data structure here. */
MinStack() {
stack2.push(INT_MAX);
}
void push(int x) {
stack1.push(x);
if(x < stack2.top()) stack2.push(x);
else stack2.push(stack2.top());
}
void pop() {
stack1.pop();
stack2.pop();
}
int top() {
int res =stack1.top();
return res;
}
int min() {
return stack2.top();
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack* obj = new MinStack();
* obj->push(x);
* obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->min();
*/
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped)
{
stack<int> stk;
int k = 0;
for(int i=0;i<pushed.size();i++)
{
stk.push(pushed[i]);//入栈
while(stk.empty()!= true && stk.top()==popped[k])
{
k++;//匹配上 移动到下一个待匹配的地方
stk.pop();//删除这个
}
}
bool res =stk.empty();//为空表示匹配 当前前提就是数量一致
return res;
}
};
第一次复习:内部的while循环为空忘记写了 根据报错找到了。补充:时间复杂度和空间复杂度都是0(n)
第二次复习:很明显需要一个for循环不断模拟放入,内部需要判断是不是弹出元素,并且可能一次性弹出多个 加个while 代码一次过
if(matrix.size()==0) return false;
先进行判断才能 matrix[0].size()-1
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
int i = 0;
if(matrix.size()==0) return false;
int j = matrix[0].size()-1;
while(i < matrix.size() && j >= 0)
{
if(matrix[i][j] > target) j--;
else if(matrix[i][j] < target) i++;
else return true;
}
return false;
}
};
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
int i = matrix.size() - 1, j = 0;
while(i >= 0 && j < matrix[0].size())
{
if(matrix[i][j] > target) i--;
else if(matrix[i][j] < target) j++;
else return true;
}
return false;
}
};
第一次复习:没什么问题,补充:时间复杂度0(m+n)空间复杂度为0(1)
第二次复习:需要一个循环 移动不固定 用while 代码一次性过
有一个不同就是 返回值是l 不是r 很特别 回头理一理
class Solution {
public:
int minArray(vector<int>& numbers) {
int l = 0;
int r = numbers.size() - 1;
while (l <= r)
{
int mid = l + (r - l) / 2;
//78012的情况
if (numbers[mid] < numbers[r])//如果中间小于右边 说明在前半部分
{
r = mid;
}
else if(numbers[mid] > numbers[r]) //反之在后半部分
{
l = mid + 1;
}
else
{
r--;
}
}
return numbers[l];
}
};
,
第一遍复习没什么问题,时间复杂度0(n)空间复杂度为0(1)
第二遍复习…需要点点记忆 区别于 不含重复值的二分查找 和 旋转数组查好target 都有不同 在汇总复习有写过
class Solution {
public:
vector<int> exchange(vector<int>& nums) {
int k = 0, i = 0;
while (i < nums.size())
{
if (nums[i] & 1 == 1)//表示是奇数
{
//一开始就是奇数 自己和自己交换 或者这边加一个if k != i
swap(nums[k], nums[i]);
k ++;//下一个待填入的位置
i ++;//下一个判断的值
}
else //如果是偶数 不管他
{
i ++;
}
}
return nums;
}
};
class Solution {
public:
vector<int> exchange(vector<int>& nums) {
//双指针----->left指向待比较地址,right指向待放置地址
//1.循环: left
// a.如果num[left]为奇数--->表示放置正确,left++
// b.如果num[left]为偶数--->表示放置错误,num[left] <<-->> num[right] swap-此时right一定放置正确,right--
int left=0;
int right=nums.size()-1;
while(left<right)
{
//奇数
if(nums[left]%2==1)
{
left++;
}
else
{
//swap
int temp=nums[left];
nums[left]=nums[right];
nums[right]=temp;
right--;
}
}
return nums;
}
};
第一遍复习:没啥问题,用for循环写了一遍,时间复杂度0(n)
class Solution {
public:
vector<int> exchange(vector<int>& nums)
{
int k=0;
for(int i=0;i<nums.size();i++)
{
if(nums[i]%2==1)//遇到奇数 就前移
{
swap(nums[i],nums[k]);
k++;
}
//遇到偶数啥都不做 等待后移就好了
}
return nums;
}
};
class Solution {
public:
vector<int> exchange(vector<int>& nums)
{
int k=0;//待填入的位置
for(int i=0;i<nums.size();i++)
{
if(nums[i]%2==1)
{
swap(nums[k++],nums[i]);
}
}
return nums;
}
};
class Solution
{
public:
vector<int> spiralOrder(vector<vector<int>>& matrix)
{
if (matrix.empty()) return {};
vector<int> res;
int l = 0; //左边界
int r = matrix[0].size() - 1; //右边界
int t = 0; //上边界
int b = matrix.size() - 1; //下边界
while (true)
{
//left -> right
for (int i = l; i <= r; i++) res.push_back(matrix[t][i]);
if (++t > b) break;
//top -> bottom
for (int i = t; i <= b; i++) res.push_back(matrix[i][r]);
if (--r < l) break;
//right -> left
for (int i = r; i >= l; i--) res.push_back(matrix[b][i]);
if (--b < t) break;
//bottom -> top
for (int i = b; i >= t; i--) res.push_back(matrix[i][l]);
if (++l > r) break;
}
return res;
}
};
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if(matrix.empty()==true) return {};
vector<int> result;
int l = 0; //左边界
int r = matrix[0].size()-1;//右边界
int t = 0;//上边界
int b = matrix.size()-1;//下边界
while(true)
{
//一阶段 left -> right
for (int i =l;i<= r;i++) result.push_back(matrix[t][i]);
t++; //收缩上边界
if(t > b) break;
//二阶段 top -> bottom
for(int i = t;i <= b;i++) result.push_back(matrix[i][r]);
r--;
if(l > r) break;
//三阶段
for(int i = r;i >= l;i-- ) result.push_back(matrix[b][i]);
b--;
if(t > b) break;
for (int i = b; i >= t; i--) result.push_back(matrix[i][l]);
l++;
if (l > r) break;
}
return result;
}
};
没什么问题,时间复杂度为0(m*n) 空间复杂度为0(1);
第二遍复习 没啥问题
class Solution {
public:
int majorityElement(vector<int>& nums) {
sort(nums.begin(), nums.end());
return nums[nums.size() / 2];
}
};
class Solution {
public:
int maxSubArray(vector<int>& nums)
{
vector<int> dp(nums.size());//初始化大小 因为后面要用下标
dp[0] = nums[0];
int max_ = nums[0];
for(int i = 1 ;i<nums.size();i++)
{
dp[i]=max(dp[i-1]+nums[i],nums[i]);
max_=max(dp[i],max_);
}
return max_;
}
};
class Solution {
public:
int maxSubArray(vector<int>& nums)
{
//vector dp(nums.size());//初始化大小 因为后面要用下标
int dp = nums[0];
int max_ = nums[0];
for(int i = 1 ;i<nums.size();i++)
{
dp=max(dp+nums[i],nums[i]);
max_=max(dp,max_);
}
return max_;
}
};
没什么问题,时间复杂为0(n)空间复杂度为0(1)
to_string c++ 把整形转换成字符串 补充
bool cmp(const string& x,const string& y)
{
return x + y < y + x;
}
class Solution {
public:
string minNumber(vector<int>& nums) {
vector<string> strs;
string res;
for(int i = 0; i < nums.size(); i++)
strs.push_back(to_string(nums[i]));
sort(strs.begin(), strs.end(), cmp);
for(int i = 0; i < strs.size(); i++)
res.append(strs[i]);
return res;
}
};
};
class cmp
{
public:
bool operator()(const string& x,const string& y) const
{
return x + y < y + x;
}
};
class Solution {
public:
string minNumber(vector<int>& nums) {
vector<string> strs;
string res;
for(int i = 0; i < nums.size(); i++)
strs.push_back(to_string(nums[i]));
sort(strs.begin(), strs.end(), cmp());
for(int i = 0; i < strs.size(); i++)
res.append(strs[i]);
return res;
}
};
复习仿函数的写法 时间复杂度nlogn 主要是排序 空间复杂度logn sort栈空间的需要
while((n & m) == 0)
m <<= 1;
- 如果只有一个数出现一次 直接异或运算就可以得到结果
- 我们这个题就是要对他进行分组 ,利用两个数不同的时候全部数进行异或得到的就是x和y的异或 我们通过找最低位的1(就是知道这两个数的二进制再这个位置不同 一个是 0一个是1 那么我们就可以把他分成两组了 其他的数分在那一组无所谓 但是肯定的是相同数肯定分在了一边)
(nums[i] & m) == 0 这个地方一直写错 nums[i] & m == 0 这个还是有区别的
优先级的问题class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int x = 0, y = 0, n = 0, m = 1;
for(int num : nums) // 1. 遍历异或
n ^= num;
while((n & m) == 0) // 2. 循环左移,计算 m
m <<= 1;
for(int num : nums) { // 3. 遍历 nums 分组
if(num & m) x ^= num; // 4. 当 num & m != 0
else y ^= num; // 4. 当 num & m == 0
}
return vector<int> {x, y}; // 5. 返回出现一次的数字
}
};
作者:jyd
链接:https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/solution/jian-zhi-offer-56-i-shu-zu-zhong-shu-zi-tykom/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
int x = 0, y = 0, n = 0, m = 1;
//全部异或
for(int i =0;i<nums.size();i++)
{
//初始值为0 0和任何数异或都是本身
n=n^nums[i];
}
//找到第一个不同的二进制 或者说修改m的值
while((n & m) == 0)//如果是0 那就继续向前找
{
m=m<<1;
}
//分成两个组通过 和m与
for(int i=0;i<nums.size();i++)
{
//其实就两种情况 00000 001000 这样的
if((nums[i] & m) == 0) x=x^nums[i];
else y=y^nums[i];
}
return {x,y};
}
};
重新写了一遍没什么问题,中间一部分修改成这样
时间复杂度度是0(n)几个for循环遍历 空间复杂度0(1)主要就是几个变量
while(true)
{
if((n&m) == 0)
{
m =m <<1;
}
else
{
break;
}
}
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums)
{
int n=0;//第一次异或放的结果
int m=1;
for(int i =0;i<nums.size();i++)
{
n=n^nums[i];
}
while(true)
{
if((n&m) == 0)
{
m =m <<1;
}
else
{
break;
}
}
int x =0;
int y=0;
for(int i =0 ;i<nums.size();i++)
{
if((nums[i] & m) == 0) x=x^nums[i];
else y=y^nums[i];
}
return {x,y};
}
};
'- 如果可以使用额外的空间 无论是用hash 或者自带的sort排序都能做 如果不能的或要么自己写快排或者题解中的位运算
dp【i】=dp【i-1】*num【i-1】正三角的dp
class Solution {
public:
vector<int> constructArr(vector<int>& a) {
vector<int> result;
for(int i=0;i<a.size();i++)//外层循环控制第几个格子
{
int sum = 1;//每次初始化为1
for(int j = 0;j<a.size();j++)
{
if(i == j) continue;//自己不算呗
sum = sum*a[j];
}
result.push_back(sum);
}
return result;
}
};
class Solution {
public:
vector<int> constructArr(vector<int>& a)
{
int n = a.size();
if(n == 0) return {};
vector<int> b(n,1);//第一个正三角
b[0] = 1;//第一行只有一个数
for(int i =1;i<n;i++)
{
//注意这个方程 第二行 用第一个数 所以是i-1;
b[i] =b[i-1] * a[i-1];
}
//处理第二个三角 为了保存中间结果 只能从倒数第二行开始
int temp=1;
for(int i = n-2 ; i >= 0; i--)
{
//需要一个类似b的单独一个数保存上一次的结果 (弄成一个数组浪费空间) //倒数第二行 用倒数第一个数 所以+1
temp = temp*a[i+1];//
b[i] =b[i]*temp;
}
return b;
}
};
class Solution {
public:
vector<int> constructArr(vector<int>& a) {
int len = a.size();
vector<int> b(len, 1);
if(len == 0) return b;
int left = 1, right = 1;
for(int i = 0; i < len; i++){
b[i] *= left;
left *= a[i]; // 持有左边的所有数的乘积
b[len - i - 1] *= right;
right *= a[len -i - 1]; // 持有右边的所有数的乘积
}
return b;
}
};
复习:太粗心了 把*写成了+号找了半天
class Solution {
public:
vector<int> constructArr(vector<int>& a)
{
//n行n列的矩阵 初始化dp[0]
//dp[i]=dp[i-1]*num[i-1];
int n = a.size();
if(n == 0) return {};
if(n == 0) return {};
vector<int> dp(n,1);
dp[0]=1;
//求了上三角
for(int i=1;i<n;i++)
{
dp[i]=dp[i-1]*a[i-1];
}
//开始求下三角 从倒数第二行开始计算 倒数第一个数 下标是n-1
int temp =1;
for(int i = n-2;i>=0;i--)
{
temp=temp*a[i+1];
dp[i]=dp[i]*temp;
}
return dp;
}
};
class Solution {
public:
string replaceSpace(string s) {
string result;
for(int i =0;i<s.length();i++)
{
if(s[i]==' ')
{
result.push_back('%');
result.push_back('2');
result.push_back('0');
}
else
{
result.push_back(s[i]);
}
}
return result;
}
};
这次复习补充了一个不需要额外空间的写法,快慢指针k表示待填入的空格,i表示判断的元素
class Solution {
public:
string replaceSpace(string s)
{
int count=0;
int n=s.size();
for(int i=0;i<s.size();i++)
{
if(s[i]==' ') count++;
}
s.resize(s.size()+count*2);
//快慢指针
int k = s.size()-1;//指向最后一个下标
for(int i=n-1;i>=0;i--)
{
if(s[i] == ' ')
{
s[k--]='0';
s[k--]='2';
s[k--]='%';
}
else
{
s[k--]=s[i];
}
}
return s;
}
};
第一次写用map答案错误。。。估计unordered 无序存储 不能用== 比较把 修改成数组没啥问题,这个有一个前提就是都是小写 如果没说加一个转换大小写就好了
class Solution {
public:
bool checkInclusion(string s1, string s2)
{
unordered_map<char,int> map1;
unordered_map<char,int> map2;
//用来比较的
int n = s1.size();
int m = s2.size();
for(int i=0;i<s1.size();i++)
{
map1[s1[i]]++;
}
for(int i=0;i<n-1;i++)//如果长度为3 就先放2个 固定的
{
map2[s2[i]]++;
}
for(int i=n-1;i<m;i++)
{
map2[s2[i]]++;
if(map1 == map2) return true;
map2[s2[i-n+1]]--;// n =3 2-3+1
}
return false;
}
};
class Solution {
public:
bool checkInclusion(string s1, string s2)
{
vector<int> map1(26,0);
vector<int> map2(26,0);
//unordered_map map1;
//unordered_map map2;
//用来比较的
int n = s1.size();
int m = s2.size();
if(n > m) return false;
for(int i=0;i<s1.size();i++)
{
map1[s1[i]-'a']++;
}
for(int i=0;i<n-1;i++)//如果长度为3 就先放2个 固定的
{
map2[s2[i]-'a']++;
}
for(int i=n-1;i<m;i++)
{
map2[s2[i]-'a']++;
if(map1 == map2) return true;
map2[s2[i-n+1]-'a']--;// n =3 2-3+1
}
return false;
}
};
class Solution {
public:
char firstUniqChar(string s) {
vector<int> a(26,0);
for(int i=0;i<s.length();i++)
{
a[s[i]-'a']++;
}
for(int i =0;i<s.length();i++)
{
if(a[s[i]-'a']==1) return s[i];
}
return ' ';
}
};
补充一个思路 之前做过了一个方法 翻转3次 就可以不使用额外的空间了
class Solution {
public:
string reverseLeftWords(string s, int n) {
int size = s.size();
string a= s.substr(0,n);
string b= s.substr(n,size-n);
return b+a;
}
};
class Solution {
public:
string reverseLeftWords(string s, int n) {
reverse(s.begin(),s.begin()+n);
reverse(s.begin()+n,s.end());
reverse(s.begin(),s.end());
return s;
}
};
class Solution {
public:
string reverseWords(string s) {
//reverse(s.begin(), s.end());//首先翻转整个列表
int n = s.size();//求一下藏毒
int k = 0;//用来向前移动的
for (int i = 0; i < n; i ++ ) //一次循环就是翻转一个单词
{
if (s[i] == ' ') continue;//如果是空格后移
int j = i;//去除出空格后 就可以开始把i复制给j了
//如果没到结尾 那就一直j 这边的j你想想是不是可以表示个数 他就是最后一个字母的后一位
while (j < n && s[j] != ' ') j++;
reverse(s.begin() + i, s.begin() + j);//翻转 是j不是j-1 因为要表示最后有一个字母的后一个
while( i < j) s[k++] = s[i++];//(易错)开始前移动 这边很巧妙 并且是i
if (k != 0) s[k ++ ] = ' ';//用来填充一个空格 一开始不用填充,移动后 马上填充一个空格
i = j;//修改初始点
}
s.erase(s.begin() + k-1, s.end());//删除后面的空格
return s;
}
};
class Solution {
public:
/* 辅助函数:
- 返回整数是否超过整数范围
*/
bool tooLarge(long long res) {
return res >= INT_MAX || res <= INT_MIN;
}
/*
函数功能: 输入字符串, 输出对应的整数.
逻辑:
1. 去除前置空格
2. 检查下一个字符是正还是负, 若两者都不存在, 假设为正.
3. 往后读字符, 直到到达非数字字符, 或是到达字符串的末尾.
4. 把读入的字符转为带符号整数
5. 如果整数超过整数范围, 做截断.
*/
int myAtoi(string s) {
int i = 0;
int len = s.length();
if (len == 0) return 0;
// 1. 去除前置空格
while (i < len && s[i] == ' ')++i;
// 2. 检查下一个字符是正还是负, 若两者都不存在, 假设为正
if(isdigit(s[i]) == false && s[i] != '-' && s[i] != '+') return 0;
int poitiveSign = (s[i] != '-') ? 1 : -1;
if (isdigit(s[i]) == false) ++i;
// 3. 往后读字符, 直到到达非数字字符, 或是到达字符串的末尾.
long long res = 0;
bool beginPos = true;
while (i < len && isdigit(s[i])) {
int digit = s[i] - '0';
// 4. 把读入的字符转为整数
res = res * 10 + digit;
bool stop = tooLarge(res * poitiveSign);
if (stop) return poitiveSign == 1 ? INT_MAX : INT_MIN; // 数字的绝对值已经很大了, 后面的数不用再考虑.
++i;
}
return (int)(res * poitiveSign);
}
};
class Solution {
public:
bool isMatch(string s, string p) {
int m = s.size(), n = p.size();
vector<vector<bool>> dp(m+1, vector<bool>(n+1, false));
dp[0][0] = true;
// 初始化首行 注意题目所说的不可能出现连续的* 也就是说 * 或者**都是不符合的 所以当j=1 不存在if的可能 不会出现数组越界
for(int j = 2; j <= n; j += 2)//只需要确定偶数位置为* 并且 j-2的位置为true即可
dp[0][j] = dp[0][j - 2] && p[j - 1] == '*';
// 状态转移
for(int i = 1; i <= m; i++)
{
for(int j = 1; j <= n; j++)
{
if(p[j - 1] == '*')
{
//其实下面这个代码 写成 三个||更好理解只要有一个匹配上 那就匹配上
//其实就是只要下面三种情况满足一种 那就是为true 表示能匹配上
if(dp[i][j - 2]) dp[i][j] = true; // 1.匹配0次
else if(dp[i - 1][j] && s[i - 1] == p[j - 2]) dp[i][j] = true; // 2.匹配1次或者多次
else if(dp[i - 1][j] && p[j - 2] == '.') dp[i][j] = true; // 3.其实和第二种情况是一样的
}
else
{
if(dp[i - 1][j - 1] && s[i - 1] == p[j - 1]) dp[i][j] = true; // 1.
else if(dp[i - 1][j - 1] && p[j - 1] == '.') dp[i][j] = true; // 2.
}
}
}
return dp[m][n];
}
};
class Solution {
public:
bool isMatch(string s, string p)
{
int m = s.size();
int n = p.size();
vector<vector<bool>> dp(m+1,vector<bool>(n+1,false));
dp[0][0]=true;
//初始化 唯一的只有一种可能是true 偶数为*
for(int j=2;j<=n;j+=2)
{
if(dp[0][j-2] == true && p[j-1] == '*') dp[0][j]=true;
else
{
break;//遇到不是的 直接退出了 后面的无需判断 都是false
}
}
//状态转移 双循环
for(int i = 1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
//大情况就分两种 p的最后一个字母是不是*
if(p[j-1]=='*')
{
if(dp[i][j-2]==true)//匹配0个单词 那就看s少掉一个 p少掉2个的匹配情况(准确的说 如果是false不能决定这边)
{
dp[i][j]=true;
}
//情况二 就是把匹配一个 或者匹配多个的情况写在了一起
//dp[i-1][j-2] &&s[i-1] == p[j-2]这是匹配一个的
//dp[i-2][j-2] &&...这是匹配两个的
//我们推理 其实可以想想 dp[i-1][j-2] 和 dp[i-1][j]是等价的(如果原先的为true 那么后面这个也一定为true 因为变换为匹配0个) 这样的好处在于 我们保留了* 就可以多次复制单词进行匹配
else if(dp[i-1][j]&&s[i-1]==p[j-2])
{
dp[i][j]=true;
}
else if(dp[i-1][j]&&p[j-2]=='.')
{
dp[i][j]=true;
}
}
else//表示是字母或者是.
{
if(dp[i-1][j-1] && s[i-1]==p[j-1] || dp[i - 1][j - 1] && p[j - 1] == '.') dp[i][j]=true;
}
}
}
return dp[m][n];
}
};
时间复杂度和空间复杂度都是o(mn)第二遍写出现一个错误 把赋值 写成判断 == 第二个错误就是初始化j+=2 写错了
class Solution {
private:
// 整数的格式可以用[+|-]B表示, 其中B为无符号整数
bool scanInteger(const string s, int& index){
if(s[index] == '+' || s[index] == '-')
++index;
return scanUnsignedInteger(s, index);
}
bool scanUnsignedInteger(const string s, int& index){
int befor = index;
while(index != s.size() && s[index] >= '0' && s[index] <= '9')
index ++;
return index > befor;
}
public:
// 数字的格式可以用A[.[B]][e|EC]或者.B[e|EC]表示,
// 其中A和C都是整数(可以有正负号,也可以没有),而B是一个无符号整数
bool isNumber(string s) {
if(s.size() == 0)
return false;
int index = 0;
//字符串开始有空格,可以返回true
while(s[index] == ' ') //书中代码没有该项测试
++index;
bool numeric = scanInteger(s, index);
// 如果出现'.',接下来是数字的小数部分
if(s[index] == '.'){
++index;
// 下面一行代码用||的原因:
// 1. 小数可以没有整数部分,例如.123等于0.123;
// 2. 小数点后面可以没有数字,例如233.等于233.0;
// 3. 当然小数点前面和后面可以有数字,例如233.666
numeric = scanUnsignedInteger(s, index) || numeric;
}
// 如果出现'e'或者'E',接下来跟着的是数字的指数部分
if(s[index] == 'e' || s[index] == 'E'){
++index;
// 下面一行代码用&&的原因:
// 1. 当e或E前面没有数字时,整个字符串不能表示数字,例如.e1、e1;
// 2. 当e或E后面没有整数时,整个字符串不能表示数字,例如12e、12e+5.4
numeric = numeric && scanInteger(s ,index);
}
//字符串结尾有空格,可以返回true
while(s[index] == ' ')
++index;
cout << s.size() << " " << index; //调试用
return numeric && index == s.size();
}
};
class Solution {
public:
vector<int> reversePrint(ListNode* head)
{
vector<int> result;
dfs(head,result);
return result;
}
void dfs(ListNode* head,vector<int>& result)
{
//终止条件
if(head==NULL)
{
return ;
}
//进入下一层
dfs(head->next,result);
//后序 处理逻辑
result.push_back(head->val);
}
};
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
stack<int> stack;
vector<int>ans;
while(head!=NULL)
{
stack.push(head->val);
head = head->next;
}
while(stack.empty()!=true)
{
ans.push_back(stack.top());
stack.pop();
}
return ans;
}
};
都写了一遍没什么问题,时间复杂度和空间复杂度两个都是0(n)递归也需要消耗0(n)的栈空间
如果这边作为参数传递 也需要引用 因为 这一层为空 传入下一层是进行了变量拷贝 然后他把变量对应的地址换了 这边的地址就是值 值是临时的 切记切记
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
ListNode* first = head;
ListNode* second = head;
for(int i=0;i<k;i++)
{
second =second->next;
}
while(second!=NULL)
{
first=first->next;
second=second->next;
}
return first;
}
};
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
stack<ListNode*> stack;
ListNode* result;
while(head!=NULL)
{
stack.push(head);
head = head->next;
}
for(int i=0;i<k;i++)
{
if(i == k-1) result = stack.top();
else
{
stack.pop();
}
}
return result;
}
};
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
// 1 2 3 4 倒数1
ListNode* result=NULL;
int dfs(ListNode* head,int k)
{
//终止条件
if(head==NULL)
{
return 1;
}
//进入下一层
int res = dfs(head->next,k);
//后序 处理逻辑
if(res == k)
{
result= head;//保存一下结果
}
return res+1;
}
ListNode* getKthFromEnd(ListNode* head, int k)
{
int res = dfs(head,k);
return result;
}
};
补充说明:双指针写法时间复杂度是0(n)空间复杂度是0(1) 栈的递归时间复杂度一样主要需要额外的空间
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = nullptr;
ListNode* cur =head;
ListNode* next;
while(cur!= nullptr)
{
next = cur->next;//保存下一个
cur->next=pre;//修改当前指向
pre = cur;//修改上一个值
cur = next;//我指向的是下一个待处理的值
}
return pre;
}
};
没什么问题,while的判断条件可以先写循环体再做判断好理解一
ListNode* temp =** 然后temp 1= temp temp = *****。 这个意思是temp这个指针(变量)保存了一个地址然后 temp1 也来保存这个地址 随后temp1这个指针不保存这个地址了 这个地址就只有temp知道了
这个好容易混掉…修改要清楚 temp指针 更换保存的地址 不影响temp1 保存的内容 就是这个地址
然后temp1 这个地址内一个成员是head的地址 所以这个也是固定的 不变的 永远是第一个
cur!=NULL && index <= right
防止right同时表示链表的最后一个class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
// 有心情记得delete或者用智能指针,没空就算了
ListNode* temp = new ListNode(-1);
temp->next = head;
ListNode* temp1 = temp;//保存temp的地址 temp这个地址有head的地址
int index = 1;//
//假设 left是2 right是4
while (index < left && temp->next!=NULL)
{
temp = temp->next; //我这边移动到 1的位置退出了
index++;//此时为2
}
ListNode* pre = nullptr;//前为空
ListNode* cur = temp->next;//指向第一个待修改的值
ListNode* l = temp->next;//这个就是保存一下2这个节点 现在是头 待会是尾
while (cur!=NULL && index <= right)//为了防止right指向最后一个
{
//举个例子 234反转 需要指向3次next index 初始为2 right为4 符合要求 《=
ListNode* next = cur->next;//1 保存下一个节点
cur->next = pre;//2 修改指向
pre = cur;//3 更换前一个节点
cur = next;//4 更换当前节点
index++;
}
//此时 pre是4这个节点 cur是5这个节点(想想正常的反转链表此时cur为空)
temp->next = pre;
if (l)
{
l->next = cur;
}
return temp1->next;
//return temp;
}
};
第一遍复习:自己写代码想要全部考虑到确实有点难,
首先要明确几个变量的意思index表示下标从1开始移动因为我们要和right和left比对找到开始的节点和结束的节点 left和right从0开始的
。 pre 和 cur不用说了 temp1 表示岗哨 temp表示前驱。还有一个就是特殊情况,left=1 和 right=最后一个,所以这边对应两个cur!=nullptr if(l!=nullptr)
容易忘记
哨兵 temp 在 进行 temp->next=l1 地址空间就保存第一个节点的值了 然后 temp=temp->next temp不存储这个地址了 只有temp1 还有 ;
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2)
{
ListNode* temp =new ListNode(-1);
ListNode* temp1 =temp;
//因为 l1表示下一个待处理的值
while(l1!=NULL && l2!= NULL)
{
if(l1->val < l2->val)
{
temp->next=l1;
l1 =l1->next;//移动到下一个节点
}
else
{
temp->next=l2;
l2=l2->next;
}
temp=temp->next;
}
if(l1!=NULL)
{
temp->next = l1;//全部挂上去
}
if(l2!=NULL)
{
temp->next = l2;
}
return temp1->next;
}
};
其实就是构建一个hash 键是原节点 值是神拷贝的新节点(只有val一个信息 没有next 和 random两个的信息 此时都为空 )
map[cur]->next
这个表示当前这个节点的复制体map[cur->next]
这个表示下一个节点分复制体map[cur]->next = map[cur->next];
合起来就表示复制体连接map[cur]->random = map[cur->random];
同理class Solution {
public:
Node* copyRandomList(Node* head) {
if(head == nullptr) return nullptr;
Node* cur = head;
unordered_map<Node*, Node*> map;
// 3. 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
while(cur != nullptr) {
map[cur] = new Node(cur->val);
cur = cur->next;
}
cur = head;//从头开始建立关系
// 4. 构建新链表的 next 和 random 指向
while(cur != nullptr) {
map[cur]->next = map[cur->next];
map[cur]->random = map[cur->random];
cur = cur->next;
}
// 5. 返回新链表的头节点
return map[head];
}
};
第二个方法有点巧妙,循环为 两个节点不想等 就一起后移,当谁为空 就去走另外一个人的陆 因为路径一样一定会相遇 如果不想交的两条路 最后一样能退出循环 因为都走到了空 文字解释如下
太6了,我的理解: 两个链表长度分别为L1+C、L2+C, C为公共部分的长度,按照楼主的做法: 第一个人走了L1+C步后,回到第二个人起点走L2步;第2个人走了L2+C步后,回到第一个人起点走L1步。 当两个人走的步数都为L1+L2+C时就两个家伙就相爱了
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* result = NULL;
unordered_map<ListNode*,int> map;
while(headA!=NULL)
{
map[headA] =1;
headA=headA->next;
}
while(headB!=NULL)
{
if(map.count(headB)==1)
{
result = headB;
break;
}
headB=headB->next;
}
return result;
}
};
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if (headA == nullptr || headB == nullptr)
{
return nullptr;
}
ListNode *pA = headA, *pB = headB;
//一定不会死循环 两种情况
//1 相遇了
//2 同时为空退出了
//他们走过的路径是一样的
while (pA != pB)
{
if(pA == nullptr) pA = headB;
else pA = pA->next;
if(pB == nullptr) pB = headA;
else pB = pB->next;
}
return pA;
}
};
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head==nullptr||head->next==nullptr)
{
return nullptr;
}
ListNode* slow=head;
ListNode* quick=head;
while(quick->next!=nullptr&&quick->next->next!=nullptr)
{
slow=slow->next;
quick=quick->next->next;
if(quick==slow)
{
ListNode *ptr = head;
while (ptr != slow)
{
ptr = ptr->next;
slow = slow->next;
}
return ptr;
}
}
return nullptr;
}
};
时间复杂度为0(n)+0(n)=0(n),补充一种需要额外空间的做法。就是用hash表…差点忘记了还有这岔
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head)
{
if(head==nullptr)
{
return head;
}
ListNode* cur = head;
//不为空就可以比较
while(cur->next!=NULL)
{
if(cur->val == cur->next->val)
{
cur->next=cur->next->next;
}
else
{
cur = cur->next;
}
}
return head;
}
};
没什么问题 时间复杂度0(n)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head)
{
if(head==nullptr)
{
return head;
}
ListNode* cur = head;
//不为空就可以比较
while(cur!=NULL && cur->next!=NULL)
{
if(cur->val == cur->next->val)
{
cur->next=cur->next->next;
}
else
{
cur = cur->next;
}
}
return head;
}
};
class Solution {
private:
unordered_map<int, int> index;
public:
TreeNode* myBuildTree(const vector<int>& preorder, const vector<int>& inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
if (preorder_left > preorder_right) {
return nullptr;
}
// 前序遍历中的第一个节点就是根节点
int preorder_root = preorder_left;
// 在中序遍历中定位根节点
int inorder_root = index[preorder[preorder_root]];
// 先把根节点建立出来
TreeNode* root = new TreeNode(preorder[preorder_root]);
// 得到左子树中的节点数目
int size_left_subtree = inorder_root - inorder_left;
// 递归地构造左子树,并连接到根节点
// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root->left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
// 递归地构造右子树,并连接到根节点
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root->right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = preorder.size();
// 构造哈希映射,帮助我们快速定位根节点
for (int i = 0; i < n; ++i) {
index[inorder[i]] = i;
}
return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
}
};
重新写了一遍 没什么问题 就是细节比较多
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
//先序遍历
TreeNode* dfs(const vector<int>& preorder,vector<int>& inorder,int preorder_left,int preorder_right,int inorder_left,int inorder_right)
{
//递归终止条件
if(preorder_left > preorder_right) return NULL;
//分别找到前序和中序中的跟节点下标
int preorder_root=preorder_left;
int inorder_root=index[preorder[preorder_root]];
TreeNode* root = new TreeNode(preorder[preorder_root]);
//计算左子树的数量 左边界-右边界+1 但是取出上一层的跟节点
int size_left=inorder_root-inorder_left+1-1;
//右边界 =左边界+长度-1
root->left=dfs(preorder,inorder,preorder_left+1,preorder_left+1+size_left-1,inorder_left,inorder_root-1);
root->right=dfs(preorder,inorder,preorder_left+size_left+1,preorder_right,inorder_root+1,inorder_right);
return root;
}
unordered_map<int,int> index;//存放值 下标
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
{
int n =preorder.size();
for(int i =0;i< n;i++)
{
index[inorder[i]]=i;
}
//返回值肯定是节点 要挂上去 参数有两个序列 还有4个左和右
TreeNode* result = dfs(preorder,inorder,0,n-1,0,n-1);
return result;
}
};
第一个想法就是中序遍历完 或者先序 判断一个是不是另外一个子串 优化就像在递归的时候就进行判断。
这个代码比较难理解因为递归的嵌套,因为我需要递归出所有的子结构 并且所有子结构递归得判断是否相同
class Solution {
public:
bool isSubStructure(TreeNode* A, TreeNode* B) {
if(B == NULL || A == NULL)
return false;
//遍历A中每个节点,A树中任一节点包含B就能返回true
return iscontain(A, B) || isSubStructure(A->left, B) || isSubStructure(A->right, B);
}
//包含:以A为根的数是否包含B(必须从A开始)
bool iscontain(TreeNode* A, TreeNode* B){
if(B == NULL)
return true;
if(A == NULL || A->val != B->val)
return false;
return iscontain(A->left, B->left) && iscontain(A->right, B->right);
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isSubStructure(TreeNode* A, TreeNode* B)
{
if(B==NULL || A==NULL) return false;
int res2=dfs(A,B);
int res1=isSubStructure(A->left,B) || isSubStructure(A->right,B);
return res1 || res2;
}
bool dfs(TreeNode* A, TreeNode* B)
{
//先序判断当前值
//后续把判断结果返回给上一层 最后在第一个节点得到消息
if(B==NULL)
{
return true;
}
if(A==NULL)
{
}
if(A->val!=B->val) return false;
return dfs(A->left,B->left) && dfs(B->left,A->left);
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* invertTree(TreeNode* root)
{
//终止条件
if (root == nullptr)
{
return nullptr;
}
//当前层的逻辑处理
TreeNode* temp=root->left;
root->left=root->right;
root->right=temp;
//进入下一层 分别进入左子树和右子树
invertTree(root->right);
invertTree(root->left);
return root;
}
};
没什么问题 修正如下
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
void dfs(TreeNode* root)
{
if(root ==NULL) return;
TreeNode* temp =root->left;
root->left=root->right;
root->right=temp;
dfs(root->left);
dfs(root->right);
}
TreeNode* mirrorTree(TreeNode* root) {
dfs(root);
return root;
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root)
{
vector<vector<int>> ret;
queue<TreeNode*> que;
vector<int> vec;
int flat=1;
if(root!=NULL)
{
que.push(root);
}
while(!que.empty())
{
TreeNode* node = que.front();//每次取出来一个
que.pop();
vec.push_back(node->val);//把值放入
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
flat--;
if(flat==0)
{
ret.push_back(vec);//二维存入一维
flat=que.size();
vec.clear();
}
}
return ret;
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root)
{
vector<vector<int>> ret;
queue<TreeNode*> que;
vector<int> vec;
int flat=1;
int flat_=1;
if(root!=NULL)
{
que.push(root);
}
while(!que.empty())
{
TreeNode* node = que.front();//每次取出来一个
que.pop();
vec.push_back(node->val);//把值放入
if(flat_ % 2 ==0)
{
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
else
{
if (node->right) que.push(node->right);
if (node->left) que.push(node->left);
}
flat--;
if(flat==0)
{
ret.push_back(vec);//二维存入一维
flat=que.size();
vec.clear();
flat_++;
}
}
return ret;
}
};
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root)
{
vector<vector<int>> ret;
queue<TreeNode*> que;
vector<int> vec;
int flat=1;
int flat_=1;
if(root!=NULL)
{
que.push(root);
}
while(!que.empty())
{
TreeNode* node = que.front();//每次取出来一个
que.pop();
vec.push_back(node->val);//把值放入
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
flat--;
if(flat==0)
{
if(flat_%2 == 1)
{
ret.push_back(vec);//二维存入一维
}
else
{
reverse(vec.begin(),vec.end());
ret.push_back(vec);//二维存入一维
}
flat_++;
//ret.push_back(vec);//二维存入一维
flat=que.size();
vec.clear();
}
}
return ret;
}
};
dfs(k,r-1) 不要写成
dfs(k,r) `第二个错误就是l >= r 终止条件是>= 不要忽略大于 因为 举个例子 3 9两个元素 k就会=1 r=1 递归传入 就会dfs(1,0)
class Solution {
public:
vector<int> res;
bool verifyPostorder(vector<int>& postorder) {
res = postorder;
return dfs(0, postorder.size() - 1);
}
bool dfs(int l, int r)
{
if(l >= r) return true;//退出条件
int root = res[r];//最后一个点是根结点
int k = l;//从最左边开始
while(k < r && res[k] < root) k++;//符合左子树的点
for(int i = k; i < r; i++)//此时的k是右子树的第一个点
{
if(res[i] < root)//如果右子树小于根,说明不符合
return false;
}
return dfs(l, k - 1) && dfs(k, r - 1);//逐步缩小范围
}
};
作者:feng-sheng-he-li
链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/solution/jian-zhi-offerdi-33ti-ti-jie-er-cha-sou-qrz3z/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
vector<int> res;//设置成全局数组 就不用传入了
bool verifyPostorder(vector<int>& postorder)
{
res = postorder;
int n = postorder.size();
bool result = dfs(0,n-1);
return result;
}
//返回值 bool 参数左右边界
bool dfs(int l, int r)
{
//1终止条件
if(l >= r) return true; //只有一个元素
// 2先序 当前层的逻辑
int k = l;
while(k < r && res[k] < res[r])
k++;//指向下一个待比较的下标 跳出说明大于了
for(int i =k;i<r;i++)
{
if(res[i] < res[r])
return false;
}
//3进入下一层
int check1 = dfs(l,k-1);
int check2 = dfs(k,r-1);
return check1 && check2;
}
};
有返回值的也算后续遍历,就好像左子树已经return false没有继续深入了,右子树还在继续深入判断,代码其实还可以优化,加两个判断
res[i]
这边的i总是容易写成k…
class Solution {
public:
vector<int> res;
bool dfs(int l ,int r)
{
if(l>=r) return true;
int k =l;
while(k < r && res[k]<res[r]) k++;
for(int i=k;i<r;i++)
{
if(res[i]<res[r]) return false;
}
int check1 = dfs(l,k-1);
if(check1 == false) return false;
int cheac2 = dfs(k,r-1);
if(cheac2 == false ) return false;
return check1 && cheac2;
}
bool verifyPostorder(vector<int>& postorder)
{
res= postorder;
int n=postorder.size();
bool result=dfs(0,n-1);
return result;
}
};
class Solution {
public:
vector<int> res;
int flat=1;
void dfs(int l ,int r)
{
if(l>=r) return;
if(flat==0) return;
int k = l;
int root=res[r];
for(int i=l;i<r;i++)
{
if(res[i]<root)
{
k++;//最后指向第一个大于的
}
}
for(int i=k;i<r;i++)
{
if(res[i]<root)
{
flat=0;
return;
}
}
dfs(l,k-1);
dfs(k,r-1);
}
bool verifyPostorder(vector<int>& postorder)
{
res= postorder;
int n=postorder.size();
dfs(0,n-1);
return flat;
}
};
再来看返回值,递归函数什么时候需要返回值?什么时候不需要返回值?
这个题很容易想到回溯 因为不是选择左就是选择右,需要注意的是这个是找到一个路径所以有返回值 写法有两种 全部遍历 如第二个代码 或者判断直接返回 第三个代码(这个我写的比较好)
还有一个需要注意的就是终止条件 主要看2 3 代码的注释
class Solution {
private:
bool traversal(TreeNode* cur, int count)
{
if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0
if (!cur->left && !cur->right) return false; // 遇到叶子节点直接返回
if (cur->left)
{ // 左
count -= cur->left->val; // 递归,处理节点;
if (traversal(cur->left, count)) return true;
count += cur->left->val; // 回溯,撤销处理结果
}
if (cur->right)
{ // 右
count -= cur->right->val; // 递归,处理节点;
if (traversal(cur->right, count)) return true;
count += cur->right->val; // 回溯,撤销处理结果
}
return false;
}
public:
bool hasPathSum(TreeNode* root, int sum) {
if (root == NULL) return false;
return traversal(root, sum - root->val);
}
};
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum)
{
//1终止条件 如果当前节点为空 就直接返回false
//为什么不用判断 sum 因为走到底的下一个 还没有返回说明肯定不成立了 否则 早在前面就返回了 这个题是由返回值的
if (root == NULL ) return false;
//2当前层的逻辑 如果左右子树都为空并且 要减去的值和当前节点的值相同 返回
if (root->left==NULL && root->right==NULL && sum == root->val)
{
return true;
}
//否则减去当前层的值进入下一层
sum =sum -root->val;
//进入下一层 左
bool check1 =hasPathSum(root->left, sum);
sum =sum +root->val;//回溯
//进入下一层 右
sum =sum -root->val;
bool check2 = hasPathSum(root->right, sum);
sum =sum +root->val;
return check1 ||check2;
}
};
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum)
{
//1终止条件 如果当前节点为空 就直接返回false
//为什么不用判断 sum 因为走到底的下一个 还没有返回说明肯定不成立了 否则 早在前面就返回了 这个题是由返回值的
if (root == NULL ) return false;
//2当前层的逻辑 如果左右子树都为空并且 要减去的值和当前节点的值相同 返回
if (root->left==NULL && root->right==NULL && sum == root->val)
{
return true;
}
//否则减去当前层的值进入下一层
sum =sum -root->val;
//进入下一层 左
bool check1 =hasPathSum(root->left, sum);
if(check1 == true) return true;//我这样写是为了立刻返回
//进入下一层 右
bool check2 = hasPathSum(root->right, sum);
if(check2 == true) return true;
sum =sum +root->val;
return false;//如果
}
};
复习 注意对比第二三种的写法区别。第二种是没有立刻返回,只要左右子树一个满足 所以用|| 第三种是遇到就立刻都返回,如果左右都没返回一定是false
为什么想到回溯呢,因为非左就是右,你走进去左不符合就要回溯路径和进入右子树。
我修改成最后一个代码
总结就三种 一个是for循环的回溯 一个是 if if左右类型 (注意不是if else) 然后两段代码一直 还有一种就是二叉树进入左右 不需要分成两部分写 回溯一次 就要 就像下面最后一次代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> ret;
vector<vector<int>> result;
void hasPathSum(TreeNode* root, int sum)
{
//1终止条件 如果当前节点为空 就直接返回false
//为什么不用判断 sum 因为走到底的下一个 还没有返回说明肯定不成立了 否则 早在前面就返回了 这个题是由返回值的
if (root == NULL ) return;
//2当前层的逻辑 如果左右子树都为空并且 要减去的值和当前节点的值相同 返回
ret.push_back(root->val);
if (root->left==NULL && root->right==NULL && sum == root->val)
{
result.push_back(ret);
}
//否则减去当前层的值进入下一层
sum =sum -root->val;
//进入下一层 左
hasPathSum(root->left, sum);
sum =sum +root->val;//回溯
//进入下一层 右
sum =sum -root->val;
hasPathSum(root->right, sum);
sum =sum +root->val;
ret.pop_back();
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
hasPathSum(root,targetSum);
return result;
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> ret;
vector<vector<int>> result;
void hasPathSum(TreeNode* root, int sum)
{
//1终止条件 如果当前节点为空 就直接返回false
//为什么不用判断 sum 因为走到底的下一个 还没有返回说明肯定不成立了 否则 早在前面就返回了 这个题是由返回值的
if (root == NULL ) return;
//2当前层的逻辑 如果左右子树都为空并且 要减去的值和当前节点的值相同 返回
sum =sum -root->val;
ret.push_back(root->val);
if (root->left==NULL && root->right==NULL && sum == 0) result.push_back(ret);
hasPathSum(root->left, sum);
hasPathSum(root->right, sum);
sum =sum +root->val;
ret.pop_back();
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
hasPathSum(root,targetSum);
return result;
}
};
if(sum == 0 && root->right==NULL && root->left==NULL)
{
result.push_back(ret);
//return;
}
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> ret;
vector<vector<int>> result;
void dfs(TreeNode* root,int& sum)
{
//递归终止条件
if(root == NULL) return;
//放在终止条件前是因为 如果符合再添加很奇怪 没必要固定
ret.push_back(root->val);
sum =sum-root->val;
//递归终止符合条件
if(sum == 0 && root->right==NULL && root->left==NULL)
{
result.push_back(ret);
//return;
}
dfs(root->left,sum);
dfs(root->right,sum);
sum = sum+root->val;
ret.pop_back();
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum)
{
dfs(root,targetSum);
return result;
}
};
其实这个很容易想到用中序 ,然后需要这种前后挂钩需要保存前面一个值 所以有了变量pre这个不断变化的变量(之前有一个题需要不断前后比较也是这么做的)
这个pre从代码看最后都会 pre =root 所以 最后退出一定指向最后一个节点
最需要考虑的是:我们要保存一下头结点才能构成一个环 所以才有if else 如果pre为空 说明此时第一次处理 把root设为头节点
class Solution {
public:
Node* pre = nullptr;// 这个是不断后移表示前一个数
Node* head = nullptr;//这个用来保存头结点 就用了一次
Node* treeToDoublyList(Node* root)
{
if (root== NULL) return root;
dfs(root);
//最后退出 pre表示的是最后一个节点
head->left = pre; //构成一个循环
pre->right = head;
return head;
}
void dfs(Node* root)
{
//递归终止条件
if (root==NULL) return;// 递归边界: 叶子结点返回
//中序遍历 进入左子树
dfs(root->left); //左子树
//当前节点的逻辑处理
if(pre == NULL) //表示进入第一个节点了 pre还没进行任何修改
{
head = root; //1 保存一下头节点
pre = root;//2 前移pre那个数 或者理解成移到已经处理的最后一个节点
}
else//不是第一个点 就要两边挂钩
{
pre->right = root;
root->left =pre;
pre = root;//理解成移到已经处理的最后一个节点
}
dfs(root->right); //右子树
}
};
如果没有循环这个要求,就不用if这个判断了。
class Solution {
public:
int maxDepth(TreeNode* root)
{
if(root == nullptr)//想想初始点
{
return 0;
}
//处理当前层的逻辑
int left = maxDepth(root->left);
int right = maxDepth(root->right);
int ret=max(left,right)+1;
return ret;
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool result = true;
int dfs(TreeNode* root)
{
if(root == NULL)
{
return 0;
}
if(result == false)
{
return 0;//提前返回
}
int res1 = dfs(root->left);
int res2 = dfs(root->right);
if(res1 - res2>1 ||res2 - res1>1)
{
result =false;
}
return max(res1 ,res2)+1;
}
bool isBalanced(TreeNode* root) {
dfs(root);
return result;
}
};
傻了 左中右是递增 右中左就是递减 不就可以了 还是要多想想 忘记了还可以这样子
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int res;
void dfs(TreeNode* root ,int &k) //传引用 这里需要保证所有dfs函数共用一个k
{
if(!root) return;
dfs(root->right,k); //右
k--;//假如 k=1 遇到第一个节点-- =0 合理
if(k == 0) res = root->val; //保存结果
dfs(root->left,k); //左
}
int kthLargest(TreeNode* root, int k) {
dfs(root,k);
return res;
}
};
重新写了一遍 出现了错误 ,
传入的k值必须是引用(或者放入成员变量),否则从左子树返回中序节点 k又回溯了
本题遍历只能是“后序遍历”,因为我们要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。为啥我觉的先序也可以呢 奇怪
正是因为要遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中 但都可以理解算是后序遍历,尽管已经不是严格上在一个树上进行遍历的后序遍历了。
补充 终止条件有多个 而且可以存在true 和 false 我平时喜欢把true 归为当前层的逻辑
class Solution {
public:
bool compare(TreeNode* left, TreeNode* right) {
// 1终止条件首先排除空节点的情况
if (left == NULL && right != NULL) return false;//1一边为空
else if (left != NULL && right == NULL) return false;//2一边为空
else if (left == NULL && right == NULL) return true;//3两边为空
// 4(我觉的这边也可以看做先序的逻辑)
else if (left->val != right->val) return false;
// 此时就是:左右节点都不为空,且数值相同的情况
// 此时才做递归,做下一层的判断
bool outside = compare(left->left, right->right); // 左子树:左、 右子树:右
bool inside = compare(left->right, right->left); // 左子树:右、 右子树:左
bool isSame = outside && inside; // 左子树:中、 右子树:中 (逻辑处理)
return isSame;//这个表示的是内侧 和 外侧同时成立
}
bool isSymmetric(TreeNode* root) {
if (root == NULL) return true;
return compare(root->left, root->right);
}
};
class Solution {
public:
bool compare(TreeNode* left, TreeNode* right) {
if (left == NULL && right != NULL) return false;
else if (left != NULL && right == NULL) return false;
else if (left == NULL && right == NULL) return true;
else if (left->val != right->val) return false;
else return compare(left->left, right->right) && compare(left->right, right->left);
}
bool isSymmetric(TreeNode* root) {
if (root == NULL) return true;
return compare(root->left, root->right);
}
};
class Solution {
public:
int flat = 0;
void compare(TreeNode* left, TreeNode* right)
{
//终止条件
if(flat == 1 ) return;
if (left == NULL && right != NULL)
{
flat=1;
return;
}
else if (left != NULL && right == NULL)
{
flat=1;
return;
}
else if (left == NULL && right == NULL)
{
return;
}
else if (left->val != right->val)
{
flat=1;
return;
}
//其余的就是两边相等的情况
compare(left->left,right->right);
compare(left->right,right->left);
}
bool isSymmetric(TreeNode* root)
{
if (root == NULL) return true;
compare(root->left, root->right);
return !flat;
}
};
注意board需要进行回溯 但是 k 和 i 和 j 传入的是形参 下一层修改的不影响上一层 所以不需要回溯
三种回溯 切记 1for 2 if if剪枝重复结构 3 本题这种 写在一起
第二个代码把k换成引用 超过了时间限制 不知道是不是 + 1 浪费时间了
终止条件本来遇到走过的也要退出的 但是我们设置为 空 所以在判断不想等的时候也会退出
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
rows = board.size();
cols = board[0].size();
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
if(dfs(board, word, i, j, 0)) return true;
}
}
return false;
}
private:
int rows, cols;
bool dfs(vector<vector<char>>& board, string word, int i, int j, int k) {
if(i >= rows || i < 0 || j >= cols || j < 0 || board[i][j] != word[k]) return false;
if(k == word.size() - 1) return true;
board[i][j] = '\0';
bool res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) ||
dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
board[i][j] = word[k];
return res;
}
};
class Solution {
public:
int rows, cols;
bool dfs(vector<vector<char>>& board, string& word, int i, int j, int& k)
{
if(i > rows || i < 0 || j > cols || j < 0 ) return false;
if (board[i][j] != word[k]) return false;
if(k == word.size() - 1) return true;
board[i][j] = '\0';
k=k+1;
bool res1 = dfs(board, word, i + 1, j, k);
bool res2 = dfs(board, word, i - 1, j, k);
bool res3 = dfs(board, word, i, j + 1, k);
bool res4 = dfs(board, word, i , j - 1, k);
k=k-1;
board[i][j] = word[k];//回溯
return res1 || res2|| res3 || res4;
}
bool exist(vector<vector<char>>& board, string word) {
rows = board.size()-1;
cols = board[0].size()-1;
int k =0;
for(int i = 0; i <= rows; i++) {
for(int j = 0; j <= cols; j++) {
if(dfs(board, word, i, j, k)) return true;
}
}
return false;
}
};
需要补充说明的是这个题终止条件的几种情况1 超过了边界 2word元素和走到的元素不一样3走到已经走过了路径4走到最后一个元素了 并且 2 3种情况是合并
补充 特别重要
重写代码老是出现超时的情况 最后发现下面两个代码是有区别的,第一个代码会把四个结果都进行计算最后返回,但是第二个代码如果出现true说明结果必定为true会直接返回不会余下进行计算。(相当于找到一个符合的结果就结束)
board[i][j] = '\0';
int res1=dfs(board,word,i+1,j,k+1);
int res2=dfs(board,word,i-1,j,k+1);
int res3=dfs(board,word,i,j+1,k+1);
int res4=dfs(board,word,i,j-1,k+1);
board[i][j]=word[k];//回溯
return res1||res2||res3||res4;
board[i][j] = '\0';
bool res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) || dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
board[i][j]=word[k];//回溯
return res;
其实这个题有体现回溯 并不明显 我觉的更像是岛屿的题目 看能扩散的最大面积,题目说的是能走过的所有格子 并不是走过最长的一条路径 我理解错了 这个题辅助的数组网格 因为这个题本身没有网格 如果有在网格进行修改 然后判断就好了
就好像 之前的题是走向北 然后发现不对 推导起点走东边类似(只是计算一个路上的) 这边是走北边+走东边的值之和 统计了所有的值,或者说选择东西南北都体现了回溯 在i 和j
class Solution {
public:
int ijsum(int i, int j){
if (i < 10 && j < 10) return i + j;
int sum = 0;
while (i){
sum += (i % 10);
i /= 10;
}
while (j){
sum += (j % 10);
j /= 10;
}
return sum;
}
int dfs(int i, int j, int k, int m, int n, vector<vector<bool>> &flag)
{
//终止条件 1 超过边界 2坐标和不能大于k退出 3重要走过重复的flat退出
if (i > m-1 || j > n-1 || ijsum(i, j) > k || flag[i][j]||i < 0||j<0)
return 0;
flag[i][j] = true;//走过的格子都设置为true
// 自身的位置加上右边和下边符合的
int res1 = dfs(i, j+1, k, m, n, flag);
int res2 = dfs(i+1, j, k, m, n, flag);
int res3 = dfs(i+1, j, k, m, n, flag);
int res4 = dfs(i+1, j, k, m, n, flag);
return 1+res1+res2+res3+res4;
}
int movingCount(int m, int n, int k) {
vector<vector<bool>> flag(m, vector<bool>(n, false));
return dfs(0, 0, k, m, n, flag);
}
};
class Solution {
public:
int rows;
int cols;
int k_new;
int ijsum(int i, int j)
{
if (i < 10 && j < 10) return i + j;
int sum = 0;
while (i){
sum += (i % 10);
i /= 10;
}
while (j){
sum += (j % 10);
j /= 10;
}
return sum;
}
int dfs(int i, int j ,vector<vector<bool>>& flag)
{
//终止条件 1 超过边界 2坐标和不能大于k退出 3重要走过重复的flat退出
if (i > rows-1 || j > cols-1 || ijsum(i, j) > k_new || flag[i][j]||i < 0||j<0)
return 0;
flag[i][j] = true;//走过的格子都设置为true
// 自身的位置加上右边和下边符合的
int res1 = dfs(i, j+1,flag);
int res2 = dfs(i+1, j,flag);
int res3 = dfs(i+1, j,flag);
int res4 = dfs(i+1, j,flag);
return 1+res1+res2+res3+res4;
}
int movingCount(int m, int n, int k)
{
rows =m;
cols =n;
k_new =k;
vector<vector<bool>> flag(m, vector<bool>(n, false));
return dfs(0, 0,flag);
}
};
时间复杂度是0(MN)空间复杂度0(MN)
class Solution {
public:
int fib(int n)
{
if(n == 0 ) return 0;
if(n == 1) return 1;
int n1 = 0;
int n2 =1;
int n3;
for(int i=2;i<=n;i++)
{
n3 = (n1+n2)% 1000000007;
n1=n2;
n2=n3;
}
return n3;
}
};
class Solution {
public:
int numWays(int n) {
// 1 1 2 3 4
if(n == 0) return 1;
if(n == 1) return 1;
int pre = 1;
int pre2 = 1;
int now;
for(int i = 2;i <= n;i++)
{
now =(pre+pre2)%1000000007;
pre2 =pre;
pre = now;
}
return now;
}
};
这题在题库没找到
题目描述: 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
其实在草稿值上就能找到规律 f(n-1)=f(0)+f(1)+···+f(n-2). 或者说f(n)=2*f(n-1)
for(int i=2;i<=target;i++)
res=2*res;
long long N=n
class Solution {
public:
double quick(double x, int n)
{
//1 终止条件
if(n == 0)
{
return 1.0;
}
double y = quick(x ,n/2);
if(n % 2==0)
{
return y*y;
}
else
{
return y*y*x;
}
}
double myPow(double x, int n)
{
double res;
long long N =n;
if (n == 0)
{
return 1;
}
if(n < 0)
{
res =1.0/quick(x,-N);
}
else
{
res =quick(x,N);
}
return res;
}
};
比较容易想到递归把 dp【i】 =dp【i-1】+i 但是不让用if 头大
class Solution {
public:
int dfs(int n)
{
if(n == 1 ) return 1;
int res = dfs(n-1)+n;
return res;//后续 结果返回给上一层
}
int sumNums(int n)
{
return dfs(n);
}
};
class Solution {
public:
int add(int a, int b) {
//因为不允许用+号,所以求出异或部分和进位部分依然不能用+ 号,所以只能循环到没有进位为止
while(b!=0)
{
//保存进位值,下次循环用
int c=(unsigned int)(a&b)<<1;//C++中负数不支持左移位,因为结果是不定的
//保存不进位值,下次循环用,
a^=b;
//如果还有进位,再循环,如果没有,则直接输出没有进位部分即可。
b=c;
}
return a;
}
};
class Solution {
public:
int hammingWeight(uint32_t n) {
int count =0;
while(n!=0)
{
n=n&(n-1);
count++;
}
return count;
}
};
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
priority_queue<int, vector<int>, greater<int> > pq;
vector<int> ans;
for( int v : arr ) pq.push(v);
while(k--){
ans.push_back( pq.top() );
pq.pop();
}
}
};
作者:jasonchiu
链接:https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/jian-zhi-offer-40zui-xiao-de-kge-shu-c-j-gus3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
sort( arr.begin(), arr.end());
vector<int> ans;
for(int i = 0 ; i < k; ++i) ans.push_back( arr[i] );
return ans;
}
};
作者:jasonchiu
链接:https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/jian-zhi-offer-40zui-xiao-de-kge-shu-c-j-gus3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
vector<int> vec(k, 0);
if (k == 0) { // 排除 0 的情况
return vec;
}
priority_queue<int> Q;
for (int i = 0; i < k; ++i) {
Q.push(arr[i]);
}
for (int i = k; i < (int)arr.size(); ++i) {
if (Q.top() > arr[i]) {
Q.pop();
Q.push(arr[i]);
}
}
for (int i = 0; i < k; ++i) {
vec[i] = Q.top();
Q.pop();
}
return vec;
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/zui-xiao-de-kge-shu-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
添加链接描述
case 1: cur=0
2 3 0 4
千位和百位可以选00 01 02....22 十位可以取到1( 形如[00|01..|22]1[0-9] 都是<2304 ) 个位可以选0-9 共有 23 * 10 中排列
当千位和百位取23,如果十位取1 那就是形如 231[0-9] > 2304,所以当千位和百位取23,十位只能能取0,个位取0-4即 2300 2301 2302 2303 2304
但是2301不应该算进来,这个1是 单独 出现在个位的(而11,121,111这种可以被算多次)
即 23*10
case 2: cur=1
2 3 1 4
千位和百位可以选00 01 02....22 十位可以取到1 个位可以选0-9 共有 23 * 10 中排列
当千位和百位取23,十位取1,个位可以去0-4 即 2310-2314共5个
即 23 *10 + 4 +1
case 3: cur>1 即2-9
2 3 2 4
千位和百位可以选00 01 02....22 十位可以取到1(形如 [00|01...|22]1[0-9] 都是<2324) 个位可以选0-9 共有 23 * 10 中排列
当千位和百位取23,十位取1,个位可以去0-9 即 2310-2319共10个 (其中2311,被计算了两次,分别是从个位和十位分析得到的1次)
即 23 *10 + 10
总结 -当cur =0
high×digit
但cur=1
high×digit+low+1
当cur >1
(high+1)×digit
class Solution {
public:
int countDigitOne(int n)
{
//
int high = n;//只是初始值 第一次计算要除以10
int low = 0;//初始低位就是0 啥都没有
int cur = 0;//当前位的数字
long long digit =1;//1 10 100
int count =0;//统计的数量
while(high!=0 ||cur!=0)//因为我们统计的是当前位的1数量
{
cur =high%10;// 例如123 得到cur=3
high = high/10; //123/10 =12
//开始判断
if(cur == 0) count = count+high*digit;
else if(cur == 1) count = count + high*digit+low+1;
else count=count+(high+1)*digit;
//更换低位 就好像字符串变成整数 区别在于这边是从低位补到高位
low = low+cur*digit;
digit =digit*10;
}
return count;
}
};
时间复杂度是:时间复杂度log(n): 循环内的计算操作使用O(1) 时间;循环次数为数字 n 的位数,即 log10n
class Solution {
public:
int nthUglyNumber(int n) {
//下标表示第几个丑书-1
vector<int> dp(n,0);
int index2=0;//初始化三个索引下标
int index3=0;
int index5=0;
dp[0]=1;//初始化
//从第二个数开始
for(int i =1;i<n;i++)
{
int next2=dp[index2]*2;
int next3=dp[index3]*3;
int next5=dp[index5]*5;
dp[i]=min(min(next2,next3),next5);
// if(dp[i]==next2) index2++;
// else if(dp[i]==next3) index3++;
// else index5++;
if (dp[i] == next2) index2++;
if (dp[i] == next3) index3++;
if (dp[i] == next5) index5++;
}
return dp[n-1];
}
};
class Solution {
public:
int nthUglyNumber(int n)
{
vector<int> dp(n);//下标 0-》第一个 n-1-》第n个
int index2=0;
int index3=0;
int index5=0;
dp[0]=1;
for(int i =1;i<n;i++)
{
dp[i]=min(min(dp[index2]*2,dp[index3]*3),dp[index5]*5);
if(dp[i]==dp[index2]*2) index2++;
if(dp[i]==dp[index3]*3) index3++;
if(dp[i]==dp[index5]*5) index5++;
}
return dp[n-1];
}
};
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target)
{
unordered_set<int> set;
vector<int> result;
for(int i=0;i<nums.size();i++)
{
if(set.find(target-nums[i])==set.end())
{
set.insert(nums[i]);
}
else
{
//只要找到一对 所以 break
result.push_back(nums[i]);
result.push_back(target- nums[i]);
break;
}
}
return result;
}
};
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target)
{
vector<int> result;
int i =0;
int j=nums.size()-1;
while(i<j)
{
if(nums[i]+nums[j]==target)
{
result.push_back(nums[i]);
result.push_back(nums[j]);
break;
}
else if(nums[i]+nums[j] < target)
{
i++;
}
else
{
j--;
}
}
return result;
}
};
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target)
{
//初始窗口大小为2
//题目要求长度最少为2
int i = 1;//滑动窗口的左边界
int j = 2;// 滑动窗口的右边界
int sum=3;//初始为3
vector<vector<int>> res;
while(i < j)//如果滑动窗口的大小=1 退出
{
if(sum == target)
{
vector<int> ans;
for(int k = i; k <= j; k++)
ans.push_back(k);
res.push_back(ans);
sum=sum-i;//注意这边
i++;
}
else if(sum > target)
{
sum=sum-i;//先减去
i++;//再前移
}
else
{
j++;//先前移
sum =sum+j;//在加
}
}
return res;
}
};
重新写了一遍 两个错误一个是ans在main里面定义 每次用完 需要clear 2sum记得减去l
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target)
{
int l=1;
int r=2;
int sum=3;
vector<int> ans;
vector<vector<int>> result;
while(l < r)
{
if(sum <target)
{
r++;
sum=sum+r;
}
else if( sum > target)
{
sum =sum -l;
l++;
}
else
{
for(int i =l;i<=r;i++)
{
ans.push_back(i);
}
result.push_back(ans);
ans.clear();
sum= sum -l;
l++;
}
}
return result;
}
};
class Solution {
public:
bool isStraight(vector<int>& nums)
{
unordered_set<int> set;
int min_=14;
int max_=0;
for(int i =0;i<nums.size();i++)
{
if(nums[i]==0) continue;
if(set.find(nums[i])!=set.end())
{
return false;
}
else
{
set.insert(nums[i]);
min_=min(min_,nums[i]);
max_=max(max_,nums[i]);
}
}
int res = max_ - min_;
if(res < 5) return true;
else return false;
}
};
class Solution {
public:
bool isStraight(vector<int>& nums) {
sort(nums.begin(),nums.end());
int count=0;//存储0的个数
//这个循环作用1 统计0的个数 判断重复
for(int i=0;i<nums.size()-1;i++)
{
if(nums[i]==0)
{
count++;
continue;
}
if(nums[i]==nums[i+1]) return false;
}
if(nums[nums.size()-1]-nums[count] < 5) return true;
return false;
}
};
class Solution {
int f(int n, int m) {
if (n == 1) {
return 0;
}
int x = f(n - 1, m);
return (m + x) % n;
}
public:
int lastRemaining(int n, int m) {
return f(n, m);
}
};
class MedianFinder {
public:
// 最大堆,存储左边一半的数据,堆顶为最大值
priority_queue<int, vector<int>, less<int>> maxHeap;
// 最小堆, 存储右边一半的数据,堆顶为最小值
priority_queue<int, vector<int>, greater<int>> minHeap;
/** initialize your data structure here. */
MedianFinder() {
}
// 维持堆数据平衡,并保证左边堆的最大值小于或等于右边堆的最小值
void addNum(int num) {
/*
* 当两堆的数据个数相等时候,左边堆添加元素。
* 采用的方法不是直接将数据插入左边堆,而是将数据先插入右边堆,算法调整后
* 将堆顶的数据插入到左边堆,这样保证左边堆插入的元素始终是右边堆的最小值。
* 同理左边数据多,往右边堆添加数据的时候,先将数据放入左边堆,选出最大值放到右边堆中。
*/
if (maxHeap.size() == minHeap.size()) {
minHeap.push(num);
int top = minHeap.top();
minHeap.pop();
maxHeap.push(top);
} else {
maxHeap.push(num);
int top = maxHeap.top();
maxHeap.pop();
minHeap.push(top);
}
}
double findMedian() {
if (maxHeap.size() == minHeap.size()) {
return (maxHeap.top()+minHeap.top())*1.0/2;
} else {
return maxHeap.top()*1.0;
}
}
};
没什么问题 写了一遍
class Solution {
public:
class MyQueue
{
public:
//单调队列
deque<int> que;
MyQueue()
{}
void push(int value)//我们指定从头部放入
{
//先把比我小的都去除掉
while(que.empty()!= true && value > que.front())
{
que.pop_front();
}
//处理好后放入
que.push_front(value);
}
void pop( int value)//我们指定从尾部去除 去除旧的
{
//这边做判断 是因为 这个元素可能已经不存在了
if( que.empty()!= true && que.back() == value)
{
que.pop_back();
}
}
//返回尾巴的元素 最大值 返回值为整形
int back()
{
return que.back();
}
};
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
MyQueue que;
vector<int> result;//存放结果
//为什么两个循环呢 两个阶段被 前面k个是一直放入 一个最大值
//后面都是放入一个取出一个 最大值每次都有一个
for(int i =0 ; i<k ; i++)
{
que.push(nums[i]);
}
//记得把第一个加入进去 老是忘记
result.push_back(que.back());
for(int i =k;i<nums.size();i++)
{
//这两个顺序无所谓吧
que.push(nums[i]);
que.pop(nums[i-k]);
result.push_back(que.back());
}
return result;
}
};
class MyQueue
{
public:
deque<int> que;
MyQueue(){}
void push(int value)
{
while(que.empty()!=true && value > que.front())
{
que.pop_front();
}
que.push_front(value);
}
void pop(int value)
{
if(que.empty()!=true && que.back() == value)
{
que.pop_back();//表示过期了
}
}
int back()
{
return que.back();
}
};
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
MyQueue que;
vector<int> result;
for(int i =0 ; i<k-1 ; i++)
{
que.push(nums[i]);
}
//记得把第一个加入进去 老是忘记
//result.push_back(que.back());
for(int i =k-1;i<nums.size();i++)
{
//这两个顺序无所谓吧
que.push(nums[i]);
result.push_back(que.back());
que.pop(nums[i-k+1]);
}
return result;
}
};
第一遍复习 重新写了一遍换了一种写法 就是第一个加入k=3 第一个for循环放入2个,第二个for循环每次放入一个 取出一个 删除一个 唯一的不同在于
que.pop(nums[i-k+1])
和que.pop(nums[i-k])
比较难发现
我们这边 时间复杂度是0(n)空间复杂度是0(k)
和题解不同的是 我把新的元素放在开头 旧的元素在尾部 队列是单调递增 每次取尾部的数据为当前窗口的最大值。