刷leetcode300道中等题错题本/难题本
s
,请你找出其中不含有重复字符的 最长子串 的长度。(字符串)s
,找到 s
中最长的回文子串。(字符串)检查括号是否匹配的充要条件:为了检查序列是否有效,我们遍历这个序列,并使用一个变量 balance 表示左括号的数量减去右括号的数量。如果在遍历过程中 balance 的值小于零,或者结束时 balance 的值不为零,那么该序列就是无效的,否则它是有效的。
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/generate-parentheses/solution/gua-hao-sheng-cheng-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
15. 二叉搜索树的后序遍历序列
方法1:分组异或 (excellent idea!)
方法2:(自己想的)使用hashmap
18 . while ((div & ret) == 0)
注意:在进行位运算的时候,要加上括号,否则条件判断可能会这样认为:div 与上 (ret == 0),作为条件判断语句
19. 面试题56 - II. 数组中数字出现的次数 II(位运算 + 有限状态自动机,清晰图解
---
方法1:有限状态自动机
如下图所示,考虑数字的二进制形式,对于出现三次的数字,各 二进制位 出现的次数都是 33 的倍数。
因此,统计所有数字的各二进制位中 11 的出现次数,并对 33 求余,结果则为只出现一次的数字。
作者:jyd
链接:https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-ii-lcof/solution/mian-shi-ti-56-ii-shu-zu-zhong-shu-zi-chu-xian-d-4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
---
方法2:遍历统计
---
20. 队列的最大值
方法:维护单调双端队列
方法:动态规划
方法:一次遍历法,历史最低点
23. 快速乘(俄罗斯农民乘法)
来源:https://blog.csdn.net/iteye_4501/article/details/81682160 (注意,这个答案下的俄罗斯农民乘法不一定等于题目要求的快速乘,具体参考下面的说明)
方法二:快速乘
思路和算法
考虑 A 和 B 两数相乘的时候我们如何利用加法和位运算来模拟,其实就是将 B 二进制展开,如果 B 的二进制表示下第 ii 位为 1,那么这一位对最后结果的贡献就是 A*(1<<i)A∗(1<<i) ,即 A<<iA<<i。我们遍历 B 二进制展开下的每一位,将所有贡献累加起来就是最后的答案,这个方法也被称作「俄罗斯农民乘法」,感兴趣的读者可以自行网上搜索相关资料。这个方法经常被用于两数相乘取模的场景,如果两数相乘已经超过数据范围,但取模后不会超过,我们就可以利用这个方法来拆位取模计算贡献,保证每次运算都在数据范围内。
下面给出这个算法的 C++ 实现:
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/qiu-12n-lcof/solution/qiu-12n-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
方法:构建乘法表:上三角和下三角
25. 防止整数溢出的时候可以试试 double bndry = INT_MAX/10;
更保险的方法如下
if(res > bndry || res == bndry && str.charAt(j) > '7')
return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
作者:jyd
链接:https://leetcode-cn.com/problems/ba-zi-fu-chuan-zhuan-huan-cheng-zheng-shu-lcof/solution/mian-shi-ti-67-ba-zi-fu-chuan-zhuan-huan-cheng-z-4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
26. 面试题 02.08. 环路检测 (要求返回入环点的节点)
方法:快慢指针 + 数学推导( a = c + (n-1)(b+c) )
在fast和slow相遇后,新增一个节点ptr = 链表开头,随后ptr和slow同步移动,它们会在入环点相遇。
27. 旋转矩阵
方法1:使用辅助数组
方法2:原地旋转
方法3:先水平翻转,再主对角线翻转
28. 查找一个数字中二进制表示法中1的个数
方法二:位运算优化
思路及解法
观察这个运算:n~\&~(n - 1)n & (n−1),其预算结果恰为把 nn 的二进制位中的最低位的 11 变为 00 之后的结果。
如:6~\&~(6-1) = 4, 6 = (110)_2, 4 = (100)_26 & (6−1)=4,6=(110)
2
,4=(100)
2
,运算结果 44 即为把 66 的二进制位中的最低位的 11 变为 00 之后的结果。
这样我们可以利用这个位运算的性质加速我们的检查过程,在实际代码中,我们不断让当前的 nn 与 n - 1n−1 做与运算,直到 nn 变为 00 即可。因为每次运算会使得 nn 的最低位的 11 被翻转,因此运算次数就等于 nn 的二进制位中 11 的个数。
代码
C++JavaC#Python3GolangJavaScriptC
class Solution {
public:
int hammingWeight(uint32_t n) {
int ret = 0;
while (n) {
n &= n - 1;
ret++;
}
return ret;
}
};
复杂度分析
时间复杂度:O(\log n)O(logn)。循环次数等于 nn 的二进制位中 11 的个数,最坏情况下 nn 的二进制位全部为 11。我们需要循环 \log nlogn 次。
空间复杂度:O(1)O(1),我们只需要常数的空间保存若干变量。
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/er-jin-zhi-zhong-1de-ge-shu-lcof/solution/er-jin-zhi-zhong-1de-ge-shu-by-leetcode-50bb1/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
29 . 二次二分,我们复制一下
1 class Solution { 2 public: 3 int binarySearch(vector<int>& nums, int target, bool lower) { 4 int left = 0, right = (int)nums.size() - 1, ans = (int)nums.size(); 5 while (left <= right) { 6 int mid = (left + right) / 2; 7 if (nums[mid] > target || (lower && nums[mid] >= target)) { 8 right = mid - 1; 9 ans = mid; 10 } else { 11 left = mid + 1; 12 } 13 } 14 return ans; 15 } 16 17 int search(vector<int>& nums, int target) { 18 int leftIdx = binarySearch(nums, target, true); 19 int rightIdx = binarySearch(nums, target, false) - 1; 20 if (leftIdx <= rightIdx && rightIdx < nums.size() && nums[leftIdx] == target && nums[rightIdx] == target) { 21 return rightIdx - leftIdx + 1; 22 } 23 return 0; 24 } 25 };
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof/solution/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-wl6kr/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
30. 剑指 Offer 62. 圆圈中最后剩下的数字 (挺经典的数学归纳题)
方法:O(n),动态规划,数学归纳法
31. 求nums的所有子集
方法1:二进制遍历
求nums的所有子集可以这样想:
nums中有n个元素,可以使用 n-bit {10}串 来表示nums的子集,表示每一个元素在子集中是否存在,于是总共有2^n个子集
于是乎,0~(2^n-1)十进制数的二进制串实际上与nums的子集构成了双射(一一对应),我们只需要遍历0~2^n-1的数字,并且把它们二进制串中的”1“对应的nums的元素添加到子集中,就完成了对nums所有子集的遍历
算法复杂度:n * 2^n
class Solution { public: vector<vector<int>> subsets(vector<int>& nums) { int n=nums.size(); // 获得nums的长度n,总共有2^n个集合 vector<vector<int>> res; for(int i=0;i<(1<<n);++i) // (1<<n)就是2^n,这里要遍历一次所有集合 { vector<int> temp; for(int j=0;j<n;++j) // 这里的循环是要遍历一次nums数组中的所有元素,看它们是否应该被添加到当前集合,循环n次,因此二进制枚举算法复杂度是 n*2^n { if((i>>j)&1) temp.push_back(nums[j]); } res.push_back(temp); } return res; } };
方法2:非递归
复杂度:2^n
方法3:递归方法/回溯方法
由于集合是不区分排序的,因此{1,2}和{2,1}是同一个集合,在回溯算法中,以2为节点,则它的分支节点只需要考虑3,4,5,6.。。而不需要考虑1.
算法复杂度 2^n
方法4:另一种回溯算法
复杂度 2^n
32. 无重复字符串的排列组合
方法: dfs,枚举每一种在"k"位上的可能性,然后排列字符串之后的部分
复杂度:n! (n的阶乘)
33. 有重复字符的字符串排列组合(其实就是上边的方法剪枝)
复杂度:每个dfs都要调用n次dfs,递归深度达到n次时结束,因此复杂度是n*n*n*...*n(n个n) = n^n
剪枝后是 n!
class Solution { private: // index是起始的编号 void dfs(const string& s, bool* used, int index, int n, string& curr, vector<string>& res) { if (index == n) { res.push_back(curr); return; } for (int i = 0; i <n ; ++i) { if (!used[i]) { // 忽略重复元素的第二次出现的情况 if (i > 0 && !used[i-1] && s[i-1] == s[i]) { continue; } used[i] = true; curr[index] = s[i]; dfs(s, used, index+1, n, curr, res); // 回溯后要考虑恢复回去 used[i] = false; } } } public: vector<string> permutation(string S) { int n = S.size(); // 表示是否被使用 bool used[n]; memset(used, 0, sizeof(bool)*n); // 返回的结果 vector<string> res; // 排序 sort(S.begin(), S.end()); // 当前的结果 string curr = S; dfs(S, used, 0, n, curr, res); return res; } };
作者:ffreturn
链接:https://leetcode-cn.com/problems/permutation-ii-lcci/solution/0808-cshuang-bai-de-di-gui-hui-su-jie-fa-fmpj/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
34. 括号生成题
方法:dfs,剪枝,发现left < right || left > n时就剪枝
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?