1609. 奇偶树 #广搜 #二叉树的层次遍历
题意
如果一棵二叉树满足下述几个条件,则可以称为奇偶树 :
二叉树根节点所在层下标为 0 ,根的子节点所在层下标为 1 ,根的孙节点所在层下标为 2 ,依此类推。
偶数下标 层上的所有节点的值都是 奇 整数,从左到右按顺序 严格递增
奇数下标 层上的所有节点的值都是 偶 整数,从左到右按顺序 严格递减
给定根节点,要求判断该二叉树是否为奇偶树
分析
BFS对于已知根节点,按深度遍历时,十分高效。这里要注意下入队列顺序是从左至右。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
class Solution { public: bool isEvenOddTree(TreeNode* root) { queue<TreeNode*> myque; myque.push(root); int depth = 0; while (!myque.empty()) { int n = myque.size(); TreeNode* pre = nullptr, * cur = nullptr; while (n--) { cur = myque.front(); myque.pop(); if (depth & 1) { if (cur->val & 1) return false; if (pre != nullptr && pre->val <= cur->val) return false; } else { if (cur->val % 2 == 0) return false; if (pre != nullptr && pre->val >= cur->val) return false; } if (cur->left) myque.push(cur->left); if (cur->right) myque.push(cur->right); pre = cur; } depth++; } return true; } };
|
1610. 可见点的最大数目 #极角 #排序 #双指针
题意
你的位置为(posx,poxy),视野角度为angle。现给定一组顶点坐标points。现问你的视野中最多能看到多少个顶点。
样例

分析
感谢@前额叶没长好的讲解!
atan2(y, x)函数,取值范围为(−π,+π](你可以理解为,极径从−π弧度逆时针旋转至π弧度)。当函数返回值为正,即表示从**x轴逆时针旋转的弧度**,返回值为负,表示从x轴顺时针旋转的弧度。atan2(y,x)与arctan(y/x)的关系如下:
atan2(y,x)=⎩⎨⎧arctan(xy)π+arctan(xy)arctan(xy)+2π−2π第一象限或第四象限的点第二象限的点第三象限的点y>0且x=0y<0且x=0
由此我们便可利用atan2(y,x)计算(y,x)的极角了,我们把点集points所有点的极角(此时为角度制)都存到一个数组中,对其从小到大排序后,每个弧度代表的点的顺序恰好xoy平面的逆时针方向,为:第三象限->第四象限->第一象限->第四象限。接着,我们通过双指针,便能找到给定区间angle下的最多点数。
但是注意,我们要求的是区间实际是一个环,还要考虑到下图中的弧ab,如果直接逆时针从小到大遍历的话,就无法覆盖第二象限与第三象限之间的劣弧ab了。我们不妨再给极角数组中所有点+360∘,这样就能将圆环的首尾相连了。由于我们用的是双指针,不会将之前的点重复计入。

另外,建议将角度换算为角度制而不是弧度制,弧度制太小了,容易出现精度问题(wa了8发)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| const double EPS = 1e-8; class Solution { public: int visiblePoints(vector<vector<int>>& points, int angle, vector<int>& location) { vector<double> pangle; int cnt = 0, ans = 0; for (int i = 0; i < points.size(); i++){ double dx = location[0] - points[i][0], dy = location[1] - points[i][1]; if(dx == 0 && dy == 0) cnt++; else{ double tmp = 180 * atan2(dy, dx) / acos(-1.0); pangle.push_back(tmp); pangle.push_back(tmp + 360); } } sort(pangle.begin(), pangle.end()); double ang = angle; int hi = 1; for (int lo = 0; lo < pangle.size(); lo++){ while(hi < pangle.size() && (pangle[hi] - pangle[lo] < angle + EPS)) hi++; ans = max(ans, hi - 1 - lo + 1); } return ans + cnt; } };
|
1611. 使整数变为0的最少操作次数 #记忆化搜索 #格雷码
题意
给定整数$ n$,你需要重复执行多次下述操作将其转换为**0** :
返回将n 转换为$ 0 $的最小操作次数。
分析
再次感谢@前额叶没长好的讲解qaq
我们观察这一组数据
1
| 100 -> 101 -> 111 -> 110 -> 010 -> 011 -> 001 -> 000
|
观察到 100->...->010->...->000是三个位数不同的状态,也就说在操作过程中位数是单调递减的!我们再用多组数据观察发现,要使操作次数最小,他们的操作路径都是固定方向的一条直线!同时上面三个二进制数也是位数发生改变后的首个二进制数 。
不妨设f(xxxxxx)表示将二进制xxxxxx变为0的最小操作次数。
- 当
xxxxxx形如11xxxx时,要使它位数降低,只有题目中的操作二才能做到。我们是不是应该要将后面未知的xxxx变为0000呢,即将11xxxx变为110000,从而满足操作二的执行条件,由此110000通过操作二变为010000。总的来说,就是对11xxxx->…->110000->…->010000->….->000000(只保留了关键步骤),这样的思想就是计算汉诺塔时候用到的思想!故操作次数f(11xxxx)=f(00xxxx)+1+f(010000)(f(00xxxx)表示11xxxx变为110000的操作次数,1表示110000变为010000的操作次数,f(010000)表示从010000变为000000操作次数 - 当
xxxxxx形如10xxxx时,我们就要先将0xxxx转化为10000即将10xxxx变为110000,接下来操作与上面情况相同了。操作次数即为f(10xxxx)=f(10000)-f(xxxx)+1+f(10000),(f(10000)-f(xxxx)表示将10xxxx变为110000的操作次数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| class Solution { private: unordered_map<int, int> f; public: int DFS(int n) { if (n == 0) return 0; if (n == 1) return 1; if (f.count(n) > 0) return f[n]; int hiBit = 1 << 30, loBit = 1 << 29; int ans = 0; while (hiBit > 0) { if (n & hiBit) { if (n & loBit) { ans = DFS(n ^ (hiBit | loBit)) + 1 + DFS(loBit); break; } else { ans = DFS(loBit) - DFS(n ^ hiBit) + 1 + DFS(loBit); break; } } else { hiBit >>= 1; loBit >>= 1; } } return f[n] = ans; } int minimumOneBitOperations(int n) { f.clear(); return DFS(n); } };
|
另外,题目要求的操作实际上,与格雷码的枚举基本一致,可参考此篇题解