大佬估计对水题没什么兴趣,所以就轮到我刷刷水题,提高一点自信心了:D
1021.有效括号字符串为空 ("")、"(" + A + “)” 或 A + B,其中 A 和 B 都是有效的括号字符串,+ 代表字符串的连接。例如,"","()","(())()" 和 “(()(()))” 都是有效的括号字符串。
如果有效字符串 S 非空,且不存在将其拆分为 S = A+B 的方法,我们称其为原语(primitive),其中 A 和 B 都是非空有效括号字符串。
给出一个非空有效字符串 S,考虑将其进行原语化分解,使得:S = P_1 + P_2 + … + P_k,其中 P_i 是有效括号字符串原语。
对 S 进行原语化分解,删除分解中每个原语字符串的最外层括号,返回 S 。
思路:很像是以前的一道匹配括号是否合格的题,用stack,当为’(‘压入,当为’)'弹出,增加一个辅助sring,当不为外层括号时,将字符加入
class Solution {
public:
string removeOuterParentheses(string S) {
string::iterator it = S.begin();
while(it != S.end())//注意因为有pop所以不能使用it++ != S.end()否则会产生错误
{
if(*it == ')')
{
opp.pop();
}
it++;
if(opp.empty()!=1)
{
res+=*it;
}
if(*it == '(')
opp.push(*it);
}
return res;
}
private:
stack<char> opp;
string res;
};
- 二叉搜索树的范围和
给定二叉搜索树的根结点 root,返回 L 和 R(含)之间的所有结点的值的和。
二叉搜索树保证具有唯一的值。
思路:我觉得出题人的语文需要重新学习,这道题的意思是遍历该二叉树并且返回L和R间的节点的值的和。
/**
* 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 rangeSumBST(TreeNode* root, int L, int R) {
if(root != nullptr)
{
if(root->val>=L&& root->val <= R)
sum += root->val;
}
if(root->left != nullptr)
rangeSumBST(root->left, L, R);
if(root->right != nullptr)
rangeSumBST(root->right, L, R);
return sum;
}
private:
int sum = 0;
};
- 二叉树的层次遍历 II
给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
思路:用迭代的方式,设置一个辅助队列,先把根节点传进来,再把队列的size作为循环条件,每次循环就要判断节点的左右是否为空,不为空就传入队列中,循环size次,每将该队列的值传入到vector中,结束当前循环后,将vector传入到vector<vector>,并且清空vector,直到queue为空,将vector<vector>逆转,并且返回。
/**
* 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:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> sum;
vector<int> nod;
queue<TreeNode*> mid;
if(root == nullptr)
return sum;
mid.push(root);
while(!mid.empty())
{
int size = mid.size();
for(int i=0;i<size;i++)
{
nod.push_back(mid.front()->val);
TreeNode* front = mid.front();
mid.pop();
if(front->left)
{
mid.push(front->left);
}
if(front->right)
{
mid.push(front->right);
}
}
sum.push_back(nod);
nod.clear();
}
reverse(sum.begin(),sum.end());
return sum;
}
};
给定两个字符串 s 和 t,它们只包含小写字母。
字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。
请找出在 t 中被添加的字母。
思路:一般的思路是用数组来解,每一个字母可以作为数组的下标,出现一次++,最后统计哪个下标增加,返回就行了。但是,看了网上的“奇技淫巧”,大开眼界,下面是我觉的脑回路惊奇的代码。
class Solution {
public:
char findTheDifference(string s, string t) {
int count = 0;
for(auto& i: t)
{
count +=i;
}
for(auto& i: s)
{
count -=i;
}
return count;
}
};
- 位1的个数
输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
思路:对传入的值进行位操作,只要不是刚学编程的,估计都能做对。
class Solution {
public:
int hammingWeight(uint32_t n) {
int i=0;
while(n!=0)
{
if(n&1)
i++;
n = n>>1;
}
return i;
}
};
- Pow(x, n)
实现 pow(x, n) ,即计算 x 的 n 次幂函数。
看起来很简单的题,但是暗藏杀机,因为测试案例里有,类似-1.00000
-2147483648这样的输入参数,想要计算不超时,只能考虑复杂度位logn的算法。
使用折半计算,每次把n缩小一半,这样n最终会缩小到0,任何数的0次方都为1,这时候我们再往回乘,如果此时n是偶数,直接把上次递归得到的值算个平方返回即可,如果是奇数,则还需要乘上个x的值。还有一点需要引起我们的注意的是n有可能为负数,对于n是负数的情况,我们可以先用其绝对值计算出一个结果再取其倒数即可。我们让i初始化为n,然后看i是否是2的倍数,是的话x乘以自己,否则res乘以x,i每次循环缩小一半,直到为0停止循环。最后看n的正负,如果为负,返回其倒数。
class Solution {
public double myPow(double x, int n) {
double res = 1.0;
for(int i = n; i != 0; i /= 2){
if(i % 2 != 0){
res *= x;
}
x *= x;
}
return n < 0 ? 1 / res : res;
}
}
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
思路:可以认为每个元素都有两种选择,加入子集或者不加入,用状态符0和1表示,有size个元素,就有size^2种情况,就遍历1到size 与1到size ^2进行&运算,如果为真将该元素加入子集,否则跳过。
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> end;
vector<int> temp;//空
int k = 1;
int size = nums.size();
int hash = (1<<size);
while(k<=hash)
{
temp.clear();
for(int i=0;i<size;i++)
{
int m = (1<<i);
if(k& m)
{
temp.push_back(nums[i]);
}
}
end.push_back(temp);
k++;
}
return end;
}
};
- 翻转二叉树
翻转一棵二叉树。
示例:
输入:
4
/
2 7
/ \ /
1 3 6 9
输出:
4
/
7 2
/ \ /
9 6 3 1
思路:前中后序遍历,选一个,交换左节点和右节点指针。
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root == nullptr)
return root;
if(root->left)
invertTree(root->left);
if(root->right)
invertTree(root->right);
TreeNode* m = root->left;
root->left = root->right;
root->right = m;
return root;
}
};
- 除自身以外数组的乘积
给定长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。
可以用左右累乘法来解决
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
vector<int> num2(nums.size(),1);
int left = 1;
int right = 1;
int n = nums.size();
for(int i=0;i<nums.size();i++)
{
num2[i] *= left;
left *= nums[i];
num2[n-i-1] *= right;
right *= nums[n-i-1];
}
return num2;
}
};
有序数组中的单一元素
给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
挺有趣的题目,注意有序数组的条件,复杂度为n的解法是,每两个单位为一组,从前往后判断是否相等,如果相等跳过,如果不相等,返回第一个不相等的元素。
复杂度为logn的解法利用了快排的思想,判断中间的元素和左边的是否相同,如果相同,判断左边除中点的元素是否为偶数,如果为偶数说明,答案在左边,更新左右边界值,同理如果不相同,再判断是否和右边的元素相同,步骤和上面一样,如果左右都不相同,直接返回中点值。
class Solution {
public:
int singleNonDuplicate(vector<int>& num) {
int left = 0;
int right = num.size()-1;
while(left < right)
{
int mid = (left +right)/2;
if(num[mid] == num[mid-1])
{
if((mid - left)%2 == 0)//判断左边的元素个数是否为偶数
{
right =mid - 2 ;
}
else
{
left = mid+1;
}
}
else if(num[mid] == num[mid+1])
{
if((right - mid)%2 == 0)
{
left = mid+2;
}
else
{
right = mid-1;
}
}
else
{
return num[mid];
}
}
return num[left];
}
};
- Nim 游戏
你和你的朋友,两个人一起玩 Nim 游戏:桌子上有一堆石头,每次你们轮流拿掉 1 - 3 块石头。 拿掉最后一块石头的人就是获胜者。你作为先手。
你们是聪明人,每一步都是最优解。 编写一个函数,来判断你是否可以在给定石头数量的情况下赢得游戏。
你们都是聪明人就我不聪明(。≖ˇェˇ≖。)。
开始的时候想利用动态规划解决,后来发现只要堆的数量为4的整数倍,后手必赢,否则先手必赢,叫做巴什博奕,面对4的整数倍的人永远无法翻身,你拿N根对手就会拿4-N根,保证每回合共减4根,你永远对面4倍数,直到4. 相反,如果最开始不是4倍数,你可以拿掉刚好剩下4倍数根,让他永远对面4倍数。
class Solution {
public:
bool canWinNim(int n) {
return n%4;
}
};
- 买卖股票的最佳时机
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
注意你不能在买入股票前卖出股票。
递归思想,
1.计算截至今天的最低价格
2.计算今天卖出的收益与之前的最大收益进行比较
3.循环, 返回计算结果
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size() <= 1)
return 0;
int max = 0;
int min = prices[0];
for(int i=0;i<prices.size();i++)
{
min = min<prices[i]?min:prices[i];
max = max>prices[i] - min?max:prices[i] - min;
}
return max;
}
};