刷题疑惑2
1、整数除法:先转化为unsigned int ;通过逆向乘法,使除数左移至不大于被除数的最左位,再相减;此时的商就是1 左移对应的位数,继续循环判断左移,商 用或 逻辑相加,最终判断此值是否超过 INT_MAX INT_MIN;
2、只出现一次的数字,其余为三次:DFA,有限状态自动机,
3、单词长度的最大乘积:利用位运算,将原string都转化为 二进制位,相同的string则 相与之后不会为0,直接跳过;
4、前n 个数字二进制中1的个数:动态规划+最高有效位、动态规划+最低有效位、n&n-1若为0,则此时最高位为1,且只有一个1;
5、和为0的三个数:排序+双指针,注意去重操作------两层循环中都有判断,下一个数不与上一数相同,再者左指针一直小于右指针,若右指针所在下标已经大于目标值,则缩小右指针;
6、和大于等于target的最短子数组:前缀和判断(得益于数组都是非负整数,具有连续性)、或者 滑动窗口双指针,缩小左指针扩大右指针;
7、乘积小于k的子数组个数:双指针,从左往右遍历,不断累乘,若大于,则不断除去左指针所指的数,并且每次循环都 加上左右指针之间的个数(不理解相 减的原理);
8、和为k的连续子数组: 使用unordered_map记录前缀和为 sum-k的键值,利用前缀和的性质,mp[ 0] = 1, i - j 区间和为 : sum[ i] - k = sum[ j -1 ] 变换得: sum[ i] - k == sum[ j -1];(二刷还是没做出)
9、0和1个数相同的子数组:同上述问题8,将0记为-1,前缀和为0的最大下标差,初始时,mp[ 0] == -1;
10、二维子矩阵的和:使用二维前缀和,利用以(i,j)为下标的二维矩阵和相加减,减去左侧矩阵,上侧矩阵,加上一次以(i-1,j-1)结尾的矩阵数组和;
11、最长不包含重复字符的子串:动规+线性遍历;(三刷 不会);
12、API函数: isalnum:该字符是否包含字母或数字; toupper :转大写; tolower:转小写;
13、回文子字符串的个数:双循环、Manacher 马拉车算法(没看懂)(还没看):
14、含所有字符的最短字符串:滑动窗口 类型 多写写;
15、最多删除一个字符得到回文:有一个方法没通过(问题未解决);
16、链表中环的入口:边界判断条件 看看;
17、重排链表:思路一:线性表,使用vector存储节点,重构链表;思路二:快慢指针寻找链表中点 + 翻转链表后半段 + 合并链表;
18、链表两数相加:方法一:翻转链表,循环边界判断条件;方法二:辅助栈使用,逆序相加;两种方法都新建节点构成链表;
19、回文链表: 使用 递归方法,很巧妙; 利用全局一个指针,多写写;
20、扁平化多级双向链表:多写写;dfs递归方法;
21、std::funtion包装类的使用;(41条消息) C++11 function类模板_c++ function模板_xqs_123的博客-CSDN博客
22、srand(unsigned int)time(NULL) :随机数种子, int ran = random()%100;
23、插入、删除、随机访问都是O(1)的容器:vector和哈希;删除时将最后一个值赋值到删除的下标处;然后再pop_back();
24、最近最少使用缓存:哈希+链表,自己实现链表,该方法多写写,熟练 自己实现链表容器的操作,双端链表;
25、变位词组:使用 排序加哈希 或者 计数法:自建哈希函数,限制于单词长度,学习一下操作方法;
26、字典序排序 规则 : 哈希 + 排序;
27、后缀表达式: 使用栈操作;switch 操作,表达式只能是整形;
28、小行星碰撞:使用vector操作,设置一个变量,判断行星是否爆炸;while条件判断,
29、直方图最大矩形面积:使用单调栈,从左到右记录左边界,从右到左记录右边界;或者一次循环,记录左右边界;二刷的困难题;
30、矩阵中的最大矩形:(二刷题目,没想起来用上题的思路);按层遍历,我单独写一个api接口函数, 就不超时,但是写在一起会超时;官解用的按列遍历 写一起就不超时,不明白为什么?
思路:将矩阵按层遍历,每一行的值相当于上题中的矩形高度,求最大值;
31、往完全二叉树添加节点:使用队列添加节点,左右子树都存在的情况;规定节点的左右孩子均存在时才将它们一起先后压入队列;插入操作时,检查队列头部的节点的左右子节点,插入;
32、二叉树每层的最大值:方法一: dfs、先序遍历,深度与当前vector同样大小就入栈,然后遍历左右子节点,不相等时,比较当前节点和对应深度的已入栈的节点值,取大值;
方法二:bfs、广度优先遍历、使用队列,先入先出的特性、先设置一个变量 = =当前深度的vector大小,循环出栈并将其子节点入栈;
33、二叉树最底层最左边的值:双端队列+bfs; 先右后左; dfs遍历,先左后右;也是设置一个变量,记录当前深度,当大于根节点的深度时就开始赋值;赋值情况只会在叶子节点处;
34、二叉树的右侧视图:bfs、按层遍历好写,记录每一层最后一个节点的值;dfs、深度优先搜索、根结点之后,先右后左,当深度与当前vector大小相同时记录节点值;
35、二叉树剪枝:dfs+后序遍历;先处理左右子节点,再处理根节点;
36、子集的数目:递归解决,回溯解决、选或不选 、、或者枚举;(美丽子集的数目、<递推、动规没看>337周赛);这个题的边界判断,再想一想;78. 子集 - 力扣(LeetCode)
37、执行操作后的最大MEX(数组中缺失的最小非负整数):知识点:同余:若(x - y)mod m == 0;则称 x 与 y 对模m 同余,记作 x ≡ y (mod m) ;若为负数,则执行 ( x mod m + m);
用map记录同余的个数,map的键值范围肯定为 [ 0 , m - 1]; 对于同处于一个类别的每一个数(可以统计为个数),可以变成 i + v、i + 2v、、对于非负整数,从零开始加,并对m取模,
判断这个类别的数在map中 ,是否能够生成这个数;
38、从根节点到叶子节点的路径数字之和:无返回值的dfs已经能做,有返回值的dfs和bfs还没做;
39、向下的路径节点之和:方法一:dfs ,暴利递归每个节点,时间复杂度高,O(N2); 方法二:前缀和 + map、(多写写看看);
40、节点之和最大的路径:维护一个最大变量,更新该节点的路径;返回值 为当前节点和左右分支最大的一侧;收益为负就不计入;拓展:记录路径怎么写?》
41、二叉搜索树的中序后继:自己写了两种,官解有两种;有一种很简单的递归在评论区的、若p的节点值比跟节点的值大于等于,递归右子树;否则就递归左子树、或者就是本身根节点
42、二叉搜索树所有大于等于节点的值之和:逆中序遍历会写;Morris遍历不会写(再多看看);
43、二叉搜索树中两个节点之和:突出方法多;使用unordered_set的结构,存储遍历过的节点,查找有没有对应的值等于k - root->val;
44、二叉搜索树迭代器: 方法二用栈进行中序遍历,可以达到O(1)时间复杂度,O(h)空间复杂度;方法一利用中序遍历存放到数组,是O(N)复杂度;
45、值和下标之差都在给定的范围内:官解方法一:使用 滑动窗口+set有序集合,判断 lower_bound(大于等于x)元素是否小于x + t;方法二:桶排序:还没写;
46、日程表:使用set自定义类型,重载< 操作符(这儿有一个问题,重载形参列表后面的const限定词不能去掉,为什么? (42条消息) C++成员函数形参列表后的const_c++形参列表_爱就是恒久忍耐的 博客-CSDN博客);set二分查找,边界判断;或者暴利vector; 线段树还没看;
47、出现频率最高的k个数字:map+priority_queue实现,此处有一个问题,在定义priority_queue的时候,cmp的类型声明那儿decltype,与push操作; 官解还有一个快排的算法,没做。
48、和最小的k个数对:同样是大顶堆 + 自定义排序;可以 进一步进行剪枝,Loading Question... - 力扣(LeetCode)还没看,;
49、实现前缀树:哈希数组,定义一个26叉树的结构体,用 数组实现,容量为26,成员类型为前缀树类型指针, 还有一个尾结束标志,;二刷不会写,多写写,还有一个是智能指针做的没看;
50、替换单词:方法一:暴利解法,使用unordered_set记录词根,再将句子切分为一个单词的数组,遍历每一个单词,从小到大看是否有词根与之相匹配;方法二:前缀树方法,多写写熟练熟练;
51、神奇的字典:方法一是使用vector数组,遍历每一个单词, 比较是否只有一个字符是不相同的;方法二:使用字典树; 递归判断;还没看,多写写字典树;;;;
52、单词之和:前缀树解法,记录前缀和,这四个题(49 -- 52)都是前缀和题目,多写写;
53、最大的异或:解法也是前缀树解法,先将num 从高位到低位 建树,比较每一个数字的每一位;
54、二分查找:二分查找的边界问题,二分查找细节详解,顺便赋诗一首 - 二分查找 - 力扣(LeetCode),顶峰问题需要去除两个边界;
55、排序数组中只出现一次的数字:时间复杂度是O(logN);二分查找做,判断当前中点是奇数还是偶数,奇数判断其与前一个数是否相等,偶数判断是否与后一个数是否相等;代码可以进一步优化,利用 异或的性质,将mid与1进行异或代替奇数偶数的判断,
56、按权重生成随机数:二分查找 + 前缀和 ;将随机数置为前缀和范围内的随机数,比较当前下标mid对应的值是否大于等于这个随机数,是就将左边界置为mid,否则就将左边界置为mid +1;官解用 了几个新函数(没见过);
57、狒狒吃香蕉:二分查找,属实想不到,再看看;
58、合并区间:先排序,在比较下一个区间的左端点是否小于等于上一个区间的右端点;若是则end取大,否则将区间压入区间;(42条消息) C++ sort函数第三个参数的理解_林夕水心的博客-CSDN博客;
此处的sort函数的第三个参数,若是在类内定义,函数前需要加static,类内成员函数无法作为函数指针传参,若是类外定义则无需加static;
59、数组中的第K大元素:自己实现堆排序(二刷还是没写);自己实现快排;
60、链表排序:二刷还是没想到;迭代法没看;
61、所有子集:经典回溯题;套模板; 进阶解法:二进制位作为mask;
vector<int> t; void dfs(int cur, int n) { if (cur == n) { // 记录答案 // ... return; } // 考虑选择当前位置 t.push_back(cur); dfs(cur + 1, n, k); t.pop_back(); // 考虑不选择当前位置 dfs(cur + 1, n, k); }
62、没有重复元素集合的全排列问题:利用vector<bool>used记录标记过的位置,引用传参,这样输出的答案是字典序;也有不利用辅助数组的基于交换的回溯方法,原数组传参需要按值传参(两次交换可以引用传参),回溯不影响,但输出不是字典序了(进阶方法);利用回溯,每次递归穷举位置;(也是模板)
63、生成匹配的括号:这个可以看灵神的解法---简洁,枚举左括号还是右括号;最基础的是把所有可能的情况列出来,然后判断该字符串是否有效,适合分开写函数;
64、引用传参问题:primer书上16章;例如:函数参数是引用类型string&,不能用string+ string传递;
65、爬楼梯最少成本、房屋偷盗:属于动态规划,基本题;记忆化搜索也能做:如何想出状态定义和状态转移方程?一个视频学会!(Python/Java/C++/Go) - 房屋偷盗 - 力扣(LeetCode)
66、环形房屋偷盗:首尾不能同时偷,两次dp计算,偷第一家,就不偷最后一家,不偷第一家,就偷最后一家,最后取大值;灵神写的模板,套用两次范围,取大值;
int rob1(vector<int> &nums, int start, int end) { // [start,end) int f0 = 0, f1 = 0; for (int i = start; i < end; ++i) { int new_f = max(f1, f0 + nums[i]); f0 = f1; f1 = new_f; } return f1; }
67、粉刷房子:经典原地dp,自己写的动规可以进一步优化空间到O(1),也可以直接在原数组的基础上进行原地dp;;
68、翻转字符:定义两个状态:分别表示以0结尾和以1结尾的字符串最小翻转次数,时间复杂度可以进一步优化到O(1);
-------两个api函数:min_element;max_element;
template <class ForwardIterator> ForwardIterator min_element (ForwardIterator first, ForwardIterator last);
template <class ForwardIterator> ForwardIterator max_element (ForwardIterator first, ForwardIterator last);
69、最长斐波那契数列:动规,使用unordered_map优化查找速率;不太好想到递推公式,多看看;
70、最长公共子序列:利用二维数组,记录当前字符串1和字符串2下标处最长的长度;可以进一步优化到一维数组;
71、两个语法问题,模板参数、返回类型:合并排序链表;方法一是使用优先级队列,方法二是使用自底向上的归并合并,基础是调用合并两个排序链表;
class Solution { public: ListNode* mergeKLists(vector<ListNode*>& lists) { auto compare = [](ListNode* x, ListNode* y) -> bool { return x->val > y->val; }; priority_queue<ListNode*, vector<ListNode*>, decltype(compare)> q (compare);//第一个问题 for (auto node : lists) if (node) q.push(node);
//第二个问题 function<ListNode*()> merge = [&]() -> ListNode* { if (q.empty()) return nullptr; auto node = q.top(); q.pop(); if (node->next) q.push(node->next); node->next = merge(); return node; }; return merge(); } };
72、最少回文分割:知识点:先进行预处理,分割回文子字符串(子问题是medium);在从零下标处到当前下标的字符串是否为回文,进行dp处理;多写写
73、迷路的机器人:路径回溯问题;多写写,方法很多,动规也能做---路径和动规做完,逆推回去;---注意function函数模板;
74、字符串交织、字序列的数目:二维dp定义的两种方式,多写写,可以优化到一维数组,方法是滚动数组优化;都是套路模板,主要是dp怎么定义;
75、三角形中最小的路径之和:要求一维dp;
76、分割等和子集:标记为easy,但是medium;动规方法,进一步是0-1背包问题;定义二维数组dp,行是数组cap,列是目标和的大小,
77、加减的目标值:二维dp做,可以优化,列也是负数和大小;此处dfs做,写在函数里,超时,函数外不超时;
78、最少的硬币数目:二维dp数组,列表示amount,完全背包问题;以上问题都可以优化到一维dp数组实现;
79、排列的数目:一维好操作,二维没做出来;与上一题相似,不同是 :与上述问题是相似的 排列与组合的问题;多写写;总结
80、岛屿的最大面积:dfs能做,bfs还没做;
81、矩阵中的距离:思想是把所有的零入队;把四周的未访问1的先遍历,在将其入队,继续遍历四周;(最短路,超级零点思想);
82、二分图:染色法, dfs做法,bfs还没做。dfs,选定一个节点后,遍历与它直连的节点,并进行染色;
/*class类内定义static const变量 只能声明,不能初始化,只能在类外初始化;static constexpr 变量只能在类内初始化;一般类型的 static 变量,在类内初始化,只能进行声明;*/
kmp算法看看;
83、开密码锁:比较新颖,转化为bfs做法,使用队列进行搜索,看看;模板题,没看优化的A*算法;
84、所有路径:dfs深搜,有向无环图;
85、单词演变:最基本的bfs思路,一一比较字典中的单词,找到只差一个字符的单词,将其入队;每次将队首的出队,比较与目标是否相同;待优化的版本还没看;(有双向的搜索)
86、计算除法:利用unordered_map记录所有的被除数与除数,对于每一个要求的表达式,开启dfs,同时记录已经访问的string,找到连接的中间节点;
87、老鼠和奶酪:贪心思想,先让第二只老鼠都吃掉,然后比较第k个奶酪是第一只老鼠吃掉的,将变化量存储起来,排序,选择最大的k个变化量即可;(339周赛)两地调度类似题;
88、课程顺序:bfs正向直观,先将所有入度为1 的节点入队,之后将其作为第一位的课程,再将删除其所有的出度,即紧接连接的课程的入度减1,若有为0的,即为下一门要 修的课程;这就构成了拓扑排序,若最后的结果不是1,则存在环,不构成拓扑排序;dfs深搜的方法,需要记录每个节点的状态,并且将最后得到的结果reverse,因为栈顶存放的是最后要修的课程;
89、最长递增路径:使用辅助数组,存储每一个节点能到达的最大路径;拓扑排序没看;
90、重建序列:先构建有向图,并将每个节点的入度计算出来,统计入度为0的点,若大于1则直接返回false(入度为0的点只能有一个);之后遍历一遍给定的超序列,判定每个点的入度是否为0;
91、省份数量:使用dfs,同时记录每一个访问过的城市;并查集思路没看;
92、最长连续序列:使用set记录数组,遍历数组,若当前的数字 -1存在就跳过,继续遍历下一个,当不存在时进入循环,while中判断当前序列的长度,也就是只有在序列最开始的数才进入循环进行计算长度;思维巧妙;达到O(N)时间复杂度;
93、多余的边:冗余连接,无向图;并查集的思路,多余的边形成环,且只有一个环,找到这个最后构成环的边,即为解;关键是怎么构造边与融合两个节点;最容易理解的并查集详解 - 冗余连接 - 力扣(LeetCode) 「代码随想录」684. 冗余连接:【并查集基础题目】详解! - 冗余连接 - 力扣(LeetCode)并查集模板;
94、外星文字典:拓扑排序+dfs深搜;先创建字母表的map,键值记录该字母下一个顺序的字母,然后dfs方式访问每一个字母,记录到结果中,dfs顺序的结果是逆着正常顺序输出的,所以需要从尾到前的顺序记录;bfs方法没有看;
95、相似的字符串:默写并查集模板,与省份数量相似的问题,
API函数:iota:顺序赋值,依次加1;
template <class ForwardIterator, class T> void iota (ForwardIterator first, ForwardIterator last, T val);
96、最小操作次数使元素相等Ⅱ:排序之后元素都向中间靠拢,即中位数,移动次数是左右两边依次对应之差;(思维题);
97、找出字符串中第一个匹配项的下标:kmp算法;两个版本算法:「代码随想录」KMP算法详解 - 找出字符串中第一个匹配项的下标 - 力扣(LeetCode)
98、对链表进行插入排序:创建一个dummy节点,方便判断特殊情况;时间复杂度是O(N2),每次插入时间复杂度是O(1);注意链表插入时保存前后节点链表;
99、LCA问题,最近公共祖先节点:二刷了,一个是二叉搜索树,一个是二叉树,前者可以利用搜索树的性质,左子树的值小于根节点,根节点小于右子树的值;二叉树的情况有三种方法;选一即可;可以注意一下记录每个节点的父节点这种方法;
100、删除链表中的节点:不给出头节点,要删除的节点不是最后一个节点,那么下一个节点不然存在,所以将删除的节点的值赋值为下一节点的值,并指向下下个节点,思想巧妙;
浙公网安备 33010602011771号