这是在LeetCode中文网上第一次刷题,慢慢来吧,希望坚持一段时间之后能有所收获。采用的是随机做题模式,可能序号会有些乱。
845. 数组中最长的山脉- 中等难度
- 题目描述
我们把数组 A 中符合下列属性的任意连续子数组 B 称为 “山脉”:
B.length >= 3
- 存在
0 < i < B.length - 1
使得B[0] < B[1] < ... B[i-1] < B[i] > B[i+1] > ... > B[B.length - 1]
(注意:B 可以是 A 的任意子数组,包括整个数组 A。)
给出一个整数数组
A
,返回最长 “山脉” 的长度。如果不含有 “山脉” 则返回
0
。事例:
输入:[2,1,4,7,3,2,5]
输出:5
解释:最长的山脉是 [1,4,7,3,2],长度时5
输入:[2,2,2]
输出:0
解释:不含山脉
- 解题思路
对数组从前往后遍历和对数组从后往前遍历,如果符合山脉要求,就加1,否则直接置为0,然后再对两个数组遍历,符合两个数组均不为0的位置就是山峰的位置,然后相加之后再加1,就是最终答案。
- 代码
class Solution{
public:
int longestMoutain(vector<int>& A){
if(A.size()<3)
return 0;
int left[100010];
int right[100010];
left[0]=0;
for(int i=1;i<A.size();i++){
if(A[i]>A[i-1])
left[i]=left[i-1]+1;
else
left[i]=0;
}
right[A.size()-1]=0;
for(int i=A.size()-2;i>=0;i--){
if(A[i]>A[i+1])
right[i]=right[i+1]+1;
else
right[i]=0;
}
int ans=0;
for(int i=0;i<A.size();i++){
if(left[i]>0 && right[i]>0 && right[i]+left[i]+1>ans)
ans=left[i]+right[i]+1;
}
return ans;
}
};
357. 计算各个位数不同的数字个数- 中等难度
- 题目描述
给定一个非负整数 n,计算各位数字都不同的数字 x 的个数,其中 0 ≤ x < 1 0 n 0 ≤ x < 10^n 0≤x<10n 。
输入实例:
输入: 2
输出: 91
解释: 答案应为除去 11,22,33,44,55,66,77,88,99 外,在 [0,100) 区间内的所有数字。
- 解题思路
按照排列组合的思路进行,从最开始的一位,到n位,其实说白了,就是找规律:1,10,10+9 * 9,10+9 * 9 *8
- 代码
class Solution{ public: int countNumberswitchUniqueDigits(int n){ int res; if(n<=1){ return pow(10,n); }else{ res=10; int temp=9; for(int i=1;i<n;i++) { temp*=(10-i); res +=temp; } return res; } } };
390. 消除游戏- 中等难度
- 题目描述
给定一个从1 到 n 排序的整数列表。
首先,从左到右,从第一个数字开始,每隔一个数字进行删除,直到列表的末尾。
第二步,在剩下的数字中,从右到左,从倒数第一个数字开始,每隔一个数字进行删除,直到列表开头。
我们不断重复这两步,从左到右和从右到左交替进行,直到只剩下一个数字。
返回长度为 n 的列表中,最后剩下的数字。事例:
输入 n=9 1 2 3 4 5 6 7 8 9 2 4 6 8 2 6 6 输出 6
- 解题思路
其实这个题目需要解决的话,思路可以很简单,就是用for循环来来回回删除,采用的数据结构可以是链表或者其他比较容易删除的数据结构。但是当数据量比较大的时候就不实用了。
当然这个题目还是很规整,很有规律的,我看了一眼,知道不是用这种暴力的解决办法,肯定使用比较有技巧的数学公式来解决,但是想了挺久没有找到一个比较合适的数据公式来解决。
最终还是看了答案,并且看了最后牛逼的证明,感觉也不是很难,高中数列题的水平。但是想在短时间之内做出来也不容易。
- 代码
Class Solution{ public: int lastRemaining(int n){ if(n==1){ return 1; }else{ /* 单纯从左向右剔除数字的结果:f(n) 单纯从右向左剔除数字 b(n) f(n)+b(n)=n+1 f(n)=2*b(n/2) f(n)=2*b(n/2)=2*(n/2+1-f(n/2)) */ return 2*(n/2+1-lastRemaining(n/2)); } } };
623. 在二叉树中增加一行- 中等难度
- 题目描述
给定一个二叉树,根节点为第1层,深度为 1。在其第
d
层追加一行值为v
的节点。添加规则:给定一个深度值
d
(正整数),针对深度为d-1
层的每一非空节点N
,为N
创建两个值为v
的左子树和右子树。将
N
原先的左子树,连接为新节点v
的左子树;将N
原先的右子树,连接为新节点v
的右子树。如果
d
的值为 1,深度 d - 1 不存在,则创建一个新的根节点v
,原先的整棵树将作为v
的左子树。
- 解题思路
首先申明一点就是在OJ之类的题目中,递归很好用,但是在一般项目之中尽量避免使用递归,多用栈或者循环。
这一题两种解法,一种是比较头铁的遍历,还有一种是递归。但是递归又非常巧妙,将层数变成flag方便插入。
- 代码
struct TreeNode{ int val; TreeNode *left; TreeNode *right; TreeNode(int x):val(x),left(NULL),right(NULL){} }; class Solution{ public: TreeNode* addOneRow(TreeNode* root,int v ,int d){ if(!root) return NULL; if(d==1){ //这是新产生的节点作为根节点,然后原先的二叉树作为左子树 //新节点的建立和节点的插入是需要记住的,其他都是一些思路上的东西 TreeNode* newRoot = new TreeNode(v); newRoot->left=root; return newRoot; } //queue队列这种方式进行初始化第一次看到,需要注意一下 //二叉树的遍历方式是层序遍历,因此采用的是队列的方式 /* 这个算法的主体就是一个队列,然后不断朝里面加入新的元素 然后有两个判断的过程,一个是d=0还有一个是d=1,只要d>1就不断往队列里面放东西 如果d=1就将新的一层插入 如果d=0就直接返回最终的头结点,也就是结果 */ queue<TreeNode*>q{{root}}; while(!q.empty()){ if(--d==0)return root; int n=q.size(); for(int i=0;i<n;++i){ auto t= q.front(); q.pop(); if(d==1){ TreeNode *left=t->left; TreeNode *right=t->right; t->left=new TreeNode(v); t->right=new TreeNode(v); t->left->left=left; t->right->right=right; }else{ if(t->left) q.push(t->left); if(t->right) q.push(t->right); } } } return root; } };
上面这种是逐层遍历的方案,解决问题来不是很难懂,但是不够牛逼,下面介绍一种牛逼的,递归方案
class Solution{ public: TreeNode* addOneRow(TreeNode* root,int v,int d){ /* d变成了一个flag用来指示插入左节点还是右节点 */ if(d==1 || d==0){ TreeNode* newRoot =new TreeNode(v); (d ? newRoot->left : newRoot->right)=root; return newRoot; } if(root && d>1){ root->left =addOneRow(root->left,v,d>2 ?d-1 :1); root->right =addOneRow(root->right,v,d>2 ?d-1 :0); } return root; } };
81. 搜索旋转排序数组II -中等难度
- 题目描述
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组
[0,0,1,2,2,5,6]
可能变为[2,5,6,0,0,1,2]
)。编写一个函数来判断给定的目标值是否存在于数组中。若存在返回
true
,否则返回false
。事例:
输入: nums = [2,5,6,0,0,1,2], target = 0
输出: true
- 思路解析
这一题本来是非常简单的,就是求一个数组里是否有相应的值,但是有一个要求就是,需要在符合要求的时间复杂度内找到,直接遍历不合适。因此采用二分法,充分利用数组的特点。
- 代码
class Solution { public: bool search(vector<int>& A, int target) { // write your code here int low = 0, high = A.size()-1, size = A.size(), mid = 0; if(size <= 0 ) { return 0; } while(low <= high) { mid = low + (high - low) / 2; if(A[mid] == target) { return 1; } if(A[mid] > A[low]) { if(A[low] <= target && target < A[mid]) { high = mid - 1; } else { low = mid + 1; } } else if(A[mid] < A[low]){ if(A[mid] < target && target <= A[high]) { low = mid + 1; } else { high = mid - 1; } } else { low++; } } return 0; } };
104. 二叉树的最大深度 -简单难度
- 题目描述
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树[3,9,20,null,null,15,7]
,返回最大深度:3
- 思路
这一题需要应用递归的方法来求,其实就是假装你已经求到了最终的结果,然后需要将其相加得到最终的结果。
- 代码
/* struct TreeNode{ int val; TreeNode* right; TreeNode* left; TreeNode(int x):val(x),left(NULL),right(NULL){} }; */ class Solution{ public: int maxDepth(TreeNode* root){ int depth=0; if(root==nullptr) { return 0; }else{ depth++; int sub_tree_max_depth = max(maxDepth(root->right),maxDepth(root->left)); depth + = sub_tree_max_depth; } return depth; } };
75. 颜色分类 – 中等难度
- 题目描述
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
注意:
不能使用代码库中的排序函数来解决这道题。示例:
输入:[2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
进阶:
- 一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。- 你能想出一个仅使用常数空间的一趟扫描算法吗?
- 思路分析
这一题第一眼看上去好像还挺简单,但是按照题目要求加上一些限制条件之后就没那么简单了,主要是问题是两个一个是空间复杂度是一个常数,这就说明不能多扩展新的的空间来提升排序的效率,还有一个就是一趟就能得出结果的方法并其排序算法不能是代码库里面的。
我看到这些限制条件之后,第一个想法就是用计数法来进行排序,新建一个数组(保证空间复杂度是常数),然后将计数的结果都赋给新的数组一般只要一趟(最后做下来好像需要两趟,还是不够优化)。
后来看网上大佬的解答,惊为天人,太有创意了,其中一个大佬的做法是将三个数的排序看成是三个指针,然后依次向后进行遍历,如果遇到符合条件的就向后++ ,0,1,2分别给定三个优先级。
- 代码
class Solution{ void sortColors(vector<int>& nums){ int i,j,k=-1; for(int m=0;m<nums.size();m++){ if(nums[m]==0){ nums[++k]=2; nums[++j]=1; nums[++i]=0; }else if(nums[m]==1){ nums[++k]=2; nums[++j]=1; }else{ nums[++k]=2; } } } };
522. 最长特殊序列 II -中等难度
- 题目描述
给定字符串列表,你需要从它们中找出最长的特殊序列。最长特殊序列定义如下:该序列为某字符串独有的最长子序列(即不能是其他字符串的子序列)。
子序列可以通过删去字符串中的某些字符实现,但不能改变剩余字符的相对顺序。空序列为所有字符串的子序列,任何字符串为其自身的子序列。
输入将是一个字符串列表,输出是最长特殊序列的长度。如果最长特殊序列不存在,返回 -1 事例:
输入:“aba”,“cbc”,“eae”
输出:3
提示:
- 所有给定的字符串长度不会超过10
- 给定字符串列表长度将在[2,50]之间
- 解题思路
这道题题目首先没看懂,后来看网上解答发现其实就是比较一堆字符串中是不是有其中的子字符串,有的话把最长子字符串的长度输出.
所以首先有一种简单的方法将所有字符串进行对比,如果发现存在子字符串就保存一个结果,最终获取最长子字符串的长度。
还有一种思路就是需要将这一堆字符串进行排序,然后进行比对,最终首先比对出的结果,就是最终的结果。
具体的:
首先给字符串按长度排序,然后将长度大的放在前面,这样找到非共同子序列,直接返回其长度就行。因为当前找到的肯定是最长的,然后用集合来记录已经遍历过的字符串,对于当前遍历到的字符串,我们和集合中所有的字符串进行对比,看是否是某个字符串的子字符串,说明当前就是最长的非公共字符,如果当前的字符串是集合中某个字符串的子序列就直接break出来。
- 代码
class Solution{ public: int findLUSlength(vector<string>& strs ){ int res=-1,j=0,n=strs.size(); for(int i=0;i<n;++i){ for(j=0;j<n;++j){ if(i==j) continue; if(checkSub(strs[i],strs[j])) break; } if(j==n) res=max(res,(int)strs[i].size()); } return res; } //判断sub是否是str的子序列 int checkSub(string sub,string str){ int i=0; for(char c:str){ if(c==subs[i]) ++i; if(i==subs.size()) break; } return i==subs.size(); } };
方法二:
class Solution{ int findLUSlength(vector<string>& strs){ int n=strs.size(); unordered_set<string> s; sort(strs.begin(),strs.end(),[](string a,string b){ if(a.size()==b.size()) return a>b; return a.size() > b.size(); }); for(int i=0;i<n;i++){ if(i==n-1 || strs[i]!=strs[i+1]){ bool found =true; for(auto a:s){ int j=0; for(char c:a){ if(c==strs[i][j]) ++j; if(j==strs[i].size()) break; } if(j==strs[i].size()){ found =false; break; } } if(found) return strs[i].size(); } s.insert(strs[i]); } return -1; } };
95. 不同的二叉搜索树 II- 中等难度
- 题目描述
给定一个整数 n,生成所有由 1 … n 为节点所组成的二叉搜索树。
输入:3
输出:
[
[1,null,3,2],
[3,2,null,1],
[3,1,null,null,2],
[2,1,3],
[1,null,2,null,3]
]
- 解题思路:
这一题参考的也是网上的,大家也都是千篇一律,就是用的递归,或者有的参考code ganker上的代码。主题思路就是将
1-n
的数字划分成两个部分,,i
作为根节点,一个是小于i
的数值全部作为左子树,一个是大于i
的部分全部作为右子树。左子树的生成通过调用构造函数,右子树的生成通过调用生成函数,但是有一个比较困惑的地方就是需要进行左右两个子树进行循环遍历,将其接到根结点上。
- 代码
struct TreeNode{ int val; TreeNode * left; TreeNode * right; TreeNode(int x):val(x),left(NULL),right(NULL){} }; class Solution{ public: vector<TreeNode *>createTrees(int start,int end){ vector<TreeNode* >res; if(start>end) { res.push_back(NULL); return res; } if(start== end){ TreeNode * root = new TreeNode(start); res.push_back(root); return res; } for(int i=start;i<=end;i++){ vector<TreeNode* >left= createTrees(start,i-1); vector<TreeNode* >right= createTrees(i+1,end); for(int j=0;j<left.size();++j){ for(int k=0;k<right.size();++k){ TreeNode *root = new TreeNode(i); root->left= left[j]; root->right= right[k]; res.push_back(root); } } } return res; } vector<TreeNode *> generateTrees(int n){ if(n<1) return (vector<TreeNode *>)NULL; return createTrees(1,n); } };
330 . 按要求补齐数组 -困难
- 题目描述
给定一个已排序的正整数数组 *nums,*和一个正整数 *n 。*从
[1, n]
区间内选取任意个数字补充到 nums 中,使得[1, n]
区间内的任何数字都可以用 nums 中某几个数字的和来表示。请输出满足上述要求的最少需要补充的数字个数。事例:
输入: nums = [1,3], n = 6
输出: 1
解释:
根据 nums 里现有的组合 [1], [3], [1,3],可以得出 1, 3, 4。
现在如果我们将 2 添加到 nums 中, 组合变为: [1], [2], [3], [1,3], [2,3], [1,2,3]。
其和可以表示数字 1, 2, 3, 4, 5, 6,能够覆盖 [1, 6] 区间里所有的数。
所以我们最少需要添加一个数字。
- 解题思路
这道题,有些懵逼,主要的想法就是贪心算法,从两个方面去加和,一个是
nums[i]
,一个是1-n
的数字进行加和,然后只要提供一次1-n
的数字,就统计一次需要补充的数字。
- 代码
class Solution{ public: int minPatchess(vector<int>& nums, int n){ int i=0,count=0; long long maxsum=0; while(maxsum<n){ if(i<nums.size() && nums[i]<maxsum+1){ maxsum=maxsum+nums[i]; i++; }else{ maxsum+=(maxsum+1); count++; } } return count; } };
331. 验证二叉树的前序序列化 - 中等
- 题目描述
序列化二叉树的一种方法是使用前序遍历。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录,例如
#
。例如,上面的二叉树可以被序列化为字符串
"9,3,4,#,#,1,#,#,2,#,6,#,#"
,其中#
代表一个空节点。给定一串以逗号分隔的序列,验证它是否是正确的二叉树的前序序列化。编写一个在不重构树的条件下的可行算法。
每个以逗号分隔的字符或为一个整数或为一个表示
null
指针的'#'
。你可以认为输入格式总是有效的,例如它永远不会包含两个连续的逗号,比如
"1,,3"
。事例:
输入: “9,3,4,#,#,1,#,#,2,#,6,#,#”
输出: true
- 解题思路
可以采用栈的方法,因为搜索二叉树,是右子树大于根大于左子树的元素,因此把数组进行压栈,将根节点压入栈,往后遍历,只要遇到的数字比栈顶元素小,就说明是左子树的节点,继续压栈,如果遇到的数字比栈顶元素大,就是右子树的元素了,然后我们更新最小值,并且删除栈顶元素,继续往后遍历,如果还是大于 ,就继续删除栈顶元素,如果栈空了或者当前栈顶元素大于当前值就停止,并且压入当前值,如果当前值小于最小值就报错。(资料来源:[LeetCode] Verify Preorder Sequence in Binary Search Tree 验证二叉搜索树的先序序列)
还有一个思路是,看规律,因为开头不可能是
#
,通过观察规律是,数组的数目和 数组中的数字个数的关系是 :n=num_n*2 +1
- 代码:
class Solution{ public: bool verifyPreorder(vector<int> & preorder){ stack<int>s; int min_num = INT_MIN; for(auto node : preorder){ if(node<min_num) return false; while(!s.empty() && node > s.top()){ min_num = s.top(); s.pop(); } s.push(node); } return true; } };
class Solution{ public: bool verifyPreorder(string preorder){ preorder = preorder+","; int count = 1; for(int i=0;i<preorder.size();i++){ if(preorder[i] ==',') continue; if((--count)<0) return false; if(preorder[i-1]!='#') count+=2; } return count==0; } };
101.对称二叉树- 简单
- 题目描述
给定一个二叉树,检查是否镜像对称
比如二叉树[1,2,2,3,4,4,3]就是对称的
多利用迭代和递归
- 解题思路
这一题思路上不难,看的很清楚,就是对比对称位置的数值是否相等,可以用递归来做也可以用栈来做,思路也比较好想,但是在实际写的时候。出现了一些问题,主要还是思路不够清晰吧。
- 代码
class Solution{ public: bool isSymmetric(TreeNode * root){ if(!root) return true; } private: bool Dis_symmetric(TreeNode* left, TreeNode * right){ if(!left && !right) return true; if(!left || !right) return false; return (left->val == right->val) && Dis_symmetric(left->left,right->right) && Dis_symmetric(left->right,right->left);1 } };
栈的方法:
class Solution{ public: bool isSymmetric(TreeNode* root){ if(!root) return true; stack<TreeNode *>s; TreeNode *p= root->left,*q = root->right; s.push(p); s.push(q); while(!s.empty()){ p=s.top();s.pop(); q=s.top();s.pop(); if(!p && !q) continue; if(!p || !q) return false; if(p->val != q->val) return false; s.push(p->left);s.push() } } };
728. 自除数 - 简单
- 题目描述
自除数 是指可以被它包含的每一位数除尽的数。
例如,128 是一个自除数,因为
128 % 1 == 0
,128 % 2 == 0
,128 % 8 == 0
。还有,自除数不允许包含 0 。
给定上边界和下边界数字,输出一个列表,列表的元素是边界(含边界)内所有的自除数。
- 解题思路
这题没啥特别的,就是直接将数字拆开,然后除掉就行了。
- 代码
class Solution { public: vector<int> selfDividingNumbers(int left, int right) { vector<int> res; for (int i=left;i<=right;i++) { if (self_div(i)) { res.push_back(i); } } return res; } bool self_div(int n) { int boss = n; while (n!=0) { if (n % 10 !=0 && boss%( n % 10) == 0) { n = n / 10; }else { return false; } } if (n==0) { return true; } } };
856. 括号的分数- 中等
- 题目描述
给定一个平衡括号字符串
S
,按下述规则计算该字符串的分数:
()
得 1 分。AB
得A + B
分,其中 A 和 B 是平衡括号字符串。(A)
得2 * A
分,其中 A 是平衡括号字符串。比如:
输入: “(()(()))”
输出:6
- 解题思路
这一题的思路和括号匹配挺像,第一想法就是用栈来作为匹配的容器,然后第二个想法就是如何将不同的指代特征表示出来。因此需要给出两个栈,一个用来存储括号的下标,还有一个表示现在的计算数值。
思路:
- 维护一个栈和一个数组,栈用来存放’('的位置,数组中用来存放其对应的位置的值
- 把每一个’(‘的位置,也就是其中
s
中的下标,存放在栈中,当来了一个’)‘时,取出栈顶的’(‘的下标,将从栈’(‘的下标开始到’)‘的位置之间的所有value的值相加,存放在value的’)'的位置- 相加过的位置全部置为0
- 最后相加整个value数组的值即可
- 代码
class Solution{ int scoreOfParentheses(string s){ int len = s.size(),res=0; stack<int> Hash; vector<int>value(len,0); for(int i=0;i<len;i++){ int sum=0; if(s[i]='(') Hash.push(i); else{ if(int n.Hash.top();n<i;n++){ sum += value[n]; value[n]=0; } Hash.pop(); sum*=2; value[i]= sum==0 ?1:sum; } } for(int n:value){ res+=n; } return res; } };