226. 翻转二叉树(0301)
递归题,稀里糊涂的,不过抓准目标,每次递归的内容是什么;什么是结束条件。随时记着这两点,递归问题很好解决
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root == NULL)
return NULL;
else
{ //交换左右子树
TreeNode* temp = root->left;
root->left = root->right;
root->right = temp;
//递归
root->left = invertTree(root->left);
root->right = invertTree(root->right);
}
return root;
}
};
263. 丑数(0302)
我寻思这也不难啊,就是找因数
class Solution {
public:
bool isUgly(int num) {
if(num == 0)return false;//忘记考虑了,该死
while(num % 5 == 0)num = num / 5;
while(num % 3 == 0)num = num / 3;
while(num % 2 == 0)num = num / 2;
return num == 1;
}
};
num
为0的情况忘记了,大题就是要考虑这种特殊样例哇
283. 移动零(0303)
还是两个pointer,变化无穷多呀
在原数组上操作,就是不断地把0换到后面去,需要一个普通的i指针和一个用于寻找/记录0位置的指针
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int i = 0, zp = 0;//还是双指针的思路
int temp;
while(i != nums.size())
{
if(nums[i] != 0)
{//如果当前元素不为0
if(zp != i)
{//两个指针不在一块儿,就进行交换动作,否则没有必要交换
temp = nums[i];
nums[i] = nums[zp];
nums[zp] = temp;
}
zp++;//注意这里 最开始加了个else wa了,不应有else,无论如何当前只要不是0,这个pointer就得++
}
i++;
}
}
};
看到个过分的:
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
for i in range(nums.count(0)):
nums.remove(0)
nums.append(0)
# 或者:
nums.sort(key=bool, reverse=True)
342. 4的幂(0304)
数论题
注意小括号和优先级
class Solution {
public:
bool isPowerOfFour(int n) {
//先判断是不是2的幂:n & (n-1) == 0 则为2的幂
if(n < 0 || (n & (n - 1)) != 0)
return false;
//是不是4的幂,1总在奇数位上
return n & 0x55555555;
}
};
349. 两个数组的交集(0305)
这是哈希表,用的是unordered_set
,特点是:无序+元素唯一。其他的就没啥了,无非是c++的优美但不会用的使用方法了
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> result_set;//用来放结果
unordered_set<int> nusm1_set(nums1.begin(), nums1.end());
for(int num : nums2)
{
if(nusm1_set.find(num) != nusm1_set.end())
{//这就是在num1_set种找到了的意思
result_set.insert(num);//则把它放进去,随便放,不用担心重复,哈希set不允许重复的
}
}
return vector<int>(result_set.begin(), result_set.end());
//很好,基本上是照着抄的
}
};
像下面这些用法,可以记上一记
if(nusm1_set.find(num) != nusm1_set.end())
{//这就是在num1_set种找到了的意思
result_set.insert(num);//则把它放进去,随便放,不用担心重复,哈希set不允许重复的
}
unordered_set<int> nusm1_set(nums1.begin(), nums1.end());
return vector<int>(result_set.begin(), result_set.end());
371. 两整数之和(0306)
这是一道加法器的题,但是也不是完全按照位来进行运算的。是集中运用&和^来的,加法器的思想
另外编译时,AddressSanitizer对有符号数左移做了保护,但是对于进位和做异或操作来说,有无符号最终之取决怎么解读,不影响实际操作,因此先转成unsigned来做,即可绕开
class Solution {
public:
int getSum(int a, int b) {
while(b)
{
//报错了,运行时错误,评论说 编译的时候使用了 AddressSanitizer 工具,会对有符号数的左移位操作做保护,强制转成无符号数做移位可绕过
// auto carry = (a & b) << 1;//'位'的进位
auto carry = (unsigned int)(a & b) << 1;
a = a ^ b;
b = carry;
}
return a;
}
};
这还有一种递归解法,更好理解一些
class Solution {
public:
int getSum(int a, int b) {
if (!b) return a;
return getSum(a^b, ((unsigned int)a&b) << 1);
}
};
401. 二进制手表(0307)
有点烦这道题,但是我很喜欢这一种解法,反向思考,虽然不是正统的回溯的解法,但是由于它本身性质限制了数据量的大小,反其道而行之也挺好的。
基本上就是穷举这12 x 60种时间搭配,然后算出对应的num,如果和输入的num相等,ze加到vector种去
class Solution {
public:
vector<string> readBinaryWatch(int num) {
vector<string> res;
string s;
//反过来想
for(int i = 0; i < 12; ++i)
{
for(int j = 0; j < 60; ++j)
{
//数他们二进制下有多少个1
if(bitset<32>( (i << 6) | j ).count() == num)
{
s = to_string(i);
s.append(":");
s.append( j > 9 ? "" : "0" );
s.append(to_string(j));
res.push_back(s);
}
}
}
return res;
}
};
🥑437. 路径总和 III(0308-0309)
可恶,它困住了我整整一天,而一天之后我还是没能学会前缀和的写法。
双递归,就是字面意思,并且很好理解,外面一层里面一层,甚至它再来几层还是很好理解。
class Solution {
public:
int count = 0;
int pathSum(TreeNode* root, int sum) {
//偷师学艺 双重递归
if(root == NULL)//第一层递归结束条件
return 0;
//深搜,从第一个结点开始
dfs(root, sum);
//开始递归
pathSum(root->left, sum);
pathSum(root->right, sum);
return count;
}
void dfs(TreeNode* node, int sum)
{
if(node == NULL)
return;//第二层递归结束条件
if(sum - node->val == 0)
count++;//找到一条成功的路了
//开始递归
dfs(node->left, sum - node->val);
dfs(node->right, sum - node->val);
}
};
455. 分发饼干(0310)
我的孩子们会打起来的,没有人会满意的,因为人的欲望不可满足,这是在世必须体会的道理…
就是贪心,具体是:排序+两个指针,每一次就只管当前匹不匹配,如果不匹配,那只能往后移动饼干的指针,因为移动孩子更不可能。直到某一数组遍历完。
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
//贪心:排序+两个指针
int res = 0;
//排序
sort(g.begin(), g.end());
sort(s.begin(), s.end());
//两个指针
int i=0, j=0;
while(i < g.size() && j < s.size())
{
if(g[i] <= s[j])
{
res++;
i++;
j++;
}
else
j++;
}
return res;
}
};
开一下脑洞的话,一个孩子可以分得多个饼干,并且最终不是数满意孩子的个数,而是他满意且满意值之和,似乎会很难了。
504. 七进制数(0311)
很好理解,无需多言,就是那个0我没考虑到,这很低级了
class Solution {
public:
string convertToBase7(int num) {
//处理正负号,虽然有点粗糙
bool isneg = num < 0 ? true : false;
num = abs(num);
string s = "";
//没考虑到0,所以补救一下
if(num == 0)return "0";
while(num)
{
s.append(to_string(num % 7));
num /= 7;
}
s.append(isneg ? "-" : "");
reverse(s.begin(), s.end());
return s;
}
};
这JAVA就…好吧
public String convertToBase7(int num) {
return Integer.toString(num, 7);
}
我有时候就在想,这些高级语言封装这些东西是为了什么呢,真有这些孤僻函数的用武之地吗
575. 分糖果(0312)
妹妹能得到的糖果种类的制约因素:糖果种类数
种类数 >= n / 2,则可以得到n / 2种;小于,则最多只能得到糖果种类数
class Solution {
public:
int distributeCandies(vector<int>& candyType) {
unordered_set<int> catset(candyType.begin(), candyType.end());
int s = catset.size();
if(s >= candyType.size() / 2)
return candyType.size() / 2;
else
return s;
}
};
665. 非递减数列(0314)
贪心还可以这么贪呀。
第一次做的时候单纯的判断前一个和后一个的大小比较,但这是不完全的考虑,测试点[3,4,2,3]
不通过。这里有两对减序对(4,2)
和(4,3)
,想要消除减序对,最优的办法就是把nums[i]
的值改为nums[i-1]
的值,这样对后面的影响最小。所以基于此,完备的算法生成了。
次题得复习
class Solution {
public:
bool checkPossibility(vector<int>& nums) {
if(nums.size() <= 2)
return true;
int count = 0;
for(int i = 1; i < nums.size(); ++i)
{
if(nums[i] < nums[i - 1])
{
count++;
if(count >= 2)
return false;
if(i >= 2 && nums[i] < nums[i-2])//这里其实我并不能理解为什么是[i]和[i-2]相比
nums[i] = nums[i-1];
}
}
return true;
}
};
🥝821. 字符的最短距离(0316)
双向for循环走一遍,首先是被这个思路困住了,因为我不会…其次是负无穷和正无穷两个数字我设小了,导致第一次wa了
class Solution {
public:
vector<int> shortestToChar(string s, char c) {
//两遍循环
vector<int> res(s.size(), 0);
//第一遍循环,单向的得到了第一个e出现之后,大家离它的距离
int prev = -99999;//wc,这个数字小了居然也会出错,不过情理之中,他本本来就是要代表负无穷
for(int i = 0; i < s.size(); ++i)
{
if(s[i] == c)
prev = i;
res[i] = i - prev;
}
//第二遍循环,是完全相反的方向,并且和原来res中的做大小比较,取最小值
prev = 99999;
for(int i = s.size() - 1; i >= 0; --i)
{
if(s[i] == c)
prev = i;
res[i] = min(prev - i, res[i]);
}
return res;
}
};
昨天咕了,在准备大创中期答辩ppt
874. 模拟行走机器人(0317)
看上去比较复杂的一道题,没有难度,按部就班就行了。然后这次这个ordered_set的成员不能是pair类型的,没法哈希,所以要换成set,就可以了
class Solution {
public:
int robotSim(vector<int>& commands, vector<vector<int>>& obstacles) {
//把障碍物放到哈希表里面去
set<vector<int>> ob(obstacles.begin(), obstacles.end());
int x = 0, y = 0, tempx = 0, tempy = 0;
int dire = 0;//0123分别是东南西北
int res = 0;
//开始执行指令
for(int i = 0; i < commands.size(); ++i)
{
//更新方向
if(commands[i] == -1)
dire = (dire + 1) % 4;
else if(commands[i] == -2)
dire = ((dire - 1) % 4 + 4) % 4;
//坐标
else
{
for(int j = 0; j < commands[i]; ++j)
{
tempx = x;
tempy = y;
if(dire == 0) y++;
else if(dire == 1) x++;
else if(dire == 2) y--;
else x--;
if(ob.find({x, y}) != ob.end())
{//在hash集合中找到了障碍物,那么就得还原回去,并且提前终止指令
x = tempx;
y = tempy;
break;
}
}
res = max(res, x * x + y * y);
}
}
return res;
}
};
另外还存在一个对负数取模的问题,对于负数最好就这样写
(a % max + max) % max
就很稳
1128. 等价多米诺骨牌对的数量(0318)
本来打算用hash set做,做到一半发现不对劲,set会删除重复元素,所以它并不是一道合适的题。
学到了状态压缩+一次遍历
,DP+OnePass
,这个题有1 <= dominoes[i]<= 9
,很小,每一个书数字可以用5bit来表示,那么一个就可以用10来表示,10bit一共有1024中可能,那么开辟一个初始全为0,大小为1024的list就可以表征其全部状态,著需要遍历一一遍就可以记录下所有出现的数字。
细节是两个数字前后可以调换,所以可以比一下大小,再进行编码表示,怎么表示都无所谓,只要规则一致就行。
python真简便呀
class Solution:
def numEquivDominoPairs(self, dominoes: List[List[int]]) -> int:
count = [0] * 1024
res = 0
for a, b in dominoes:
if a <= b:
v = (a << 5) | b
else:
v = (b << 5) | a
res += count[v]
count[v] += 1
return res
1260. 二维网格迁移(0319)
唉果然这种题就得看数学找规律,把这个二维网格拉抻成一位,就会发现就是k个元素从尾巴依次跑到头的事情
class Solution:
def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]:
m = len(grid)
n = len(grid[0])
mid = []
for l in grid:
mid = mid + l
for _ in range(k):
t = mid[-1]
mid.pop()
mid.insert(0, t)
for i in range(m):
for j in range(n):
grid[i][j] = mid[i * n + j]
return grid
1332. 删除回文子序列(0320)
funny🙂,回文子序列,不是回文子串。前者可以不连续,后者可以。也就是说,对于非回文字符串,就可以先删去a,再删去b,最多就只要两次。挺无聊,但是又说明读题要仔细。
class Solution:
def removePalindromeSub(self, s: str) -> int:
if(len(s) == 0):
return 0
else:
return 1 if s == s[::-1] else 2
(0321)复习
除了有个数据结构的题我跳过去了,其余简单题好像做完了,周末了,复习