本文章旨在用较少且通俗的话说明这些题的做题思路(题目链接直接点击各题目标题即可)。只有思路,不放代码,主要是方便以后的复习和二刷、三刷。以思路为主,答案次之。读者如果想避免边看答案边做的方式,以思路为起点动手学习,这也是个不错的学习资源。
简单难度
1.用两个栈实现队列
定义两个栈,一个大栈,一个小栈,入队列的数直接放入小栈,然后出队的数直接从大栈弹栈,如果大栈为空,就把小栈中所有元素出栈并入栈到大栈中然后大栈弹栈。
2.斐波那契数列
这题比较简单,递归或者动态规划,推荐动态规划,不容易出错且运行更快。f[i]=f[i-1]+f[i-2]
3.数组中重复的数字
使用unordered_map记录数字出现次数,次数大于1即是结果。
4.青蛙跳台阶问题
动态规划,设置dp[i]为跳上i级台阶的跳法,由于只有跳一步或者两步的方式,所以
dp[i]=dp[i-1]+dp[i-2]。详情可以看这篇
5.旋转数组的最小数字
使用双指针l=0,r=n-1,首先需要去重,如果后一位与当前位相同,++l,如果前一位与当前位相同,r--,然后每次计算出mid,如果nums[l]<nums[r],nums[l]就是最小值,如果nums[l]>nums[mid],说明最小值不在mid右边,区间变为[l,mid];如果nums[r]<nums[mid],说明最小值在mid右边,区间变为[mid+1,r]。最后循环终止条件是不满足l<r,输出结果nums[l]。
6.替换空格
比较朴素的题目。碰到空格替换就行。
7.从尾到头打印链表
递归即可,先递归到尾部,再一个个记录打印。
①终止条件:节点为空。
②返回值:无返回值,打印当前节点即可(把当前节点放入向量中)
③本级递归:此时链表看成两个节点,当前cur和下一个next,先递归调用完下一个next,因为是逆序,得打印完下一个后,才打印当前的。
8.合并两个排序的链表
从两个链表的头节点开始,选中节点值较小的作为合并链表的头节点,被选中节点的链表右移继续比较,将较小者加入合并链表中并右移,直到其中一个链表被选完,那么另一个有剩余的链表的剩余部分直接接到合并链表的后面即可。
9.二叉树的镜像
递归
①结束条件:节点为空
②返回值:当前节点
③本级递归:此时看成三个结点root,left和right,首先通过递归得到左子树和右子树做镜像处理后的样子,然后由于本级也需要对称,所以交换root的左右子节点left和right,这样就保证以root为根节点的子树完成镜像,返回root。
10.对称二叉树
使用递归
极端情况,树为空,是对称的。
①结束条件:当前两位置对称节点均为空,是;只有一个为空,不是;都不为空,值不相等不是;
②返回值:返回当前位置对称节点是否值相等
③本级递归:判断当前位置对称的两节点是否是也值对称,如果对称,返回是即可,但是需要保证对称节点的子节点们保持对称,所以需要递归,只有其子节点相关均符合对称,这一级对称才能成立。
11.调整数组顺序使奇数位于偶数前面
使用双指针l=0,r=n-1,右移l知道找到偶数,左移r知道找到奇数,交换两指针位置的数,重复上述步骤,直到不满足l<r。
12.二进制中1的个数
判断每一位是否为1很简单,与一个该位是1其他位是0的数相与,若结果是0,表示该位是0,否则该位是1。
13.顺时针打印矩阵
定义四个变量top=0、bottom=n、left=0、right=n,按照右下左上的顺序走遍历即可,每次走完右,++top;每次走完下,--right;每次走完左,--bottom;每次走完上,++left。
14.链表中倒数第k个节点
先得到链表的长度n,然后从头节点走走n-k步的节点即为结果。
15.打印从1到最大的n位数
这题如果不考虑大数据的话,确实只有简单难度,遍历打印即可,但是考虑大数据的话,可以有个中等难度,可以看成求n位数的全排列。使用回溯解决。
①选择一个列子,画出递归树。(略)
②确定结束条件,因为要选择出n位数,所以结束条件是位数等于n时。
③确定选择列表,选择列表固定是0~9
④剪枝去重,不存在重复的,都可以重复选。
⑤⑥⑦选择选择列表中的数、递归(位数+1)、撤销(撤销前面选择的数)
16.反转链表
定义三个结点指针,当前cur,前一个pre,后一个tmp,初始时cur=head,其余两个为NULL,循环直到cur为空停止,在循环中使tmp指向cur的后一个,cur的后一个指针修改为指向前一个pre,操作完后,pre和cur都后移一位。
17.删除链表中的节点
定义个哨兵结点dummy指向头节点,这样删除节点的时候,无论是头节点还是中间节都可以一视同仁。遍历链表,若当前节点的下一个节点与题目要求删除节点值相同,则直接让当前节点指向下下个节点。最后返回dummy->next。
18.最小k个数
直接排序,输出前k个数或者维护包含k个数的大根堆(优先对列)。
19.包含min函数的栈
维护两个栈,一个栈正常存储,另一个栈保证栈顶始终存储最小值。
入栈时,栈一正常入栈,如果栈二为空,或者栈二当前栈顶元素不小于入栈元素,那么栈二也得入栈该原数;出栈时,栈一正常出栈,如果栈二栈顶元素与该出栈元素相同,栈二也得出栈。这样的话,要得到最小值直接返回栈二的栈顶元素即可。
20.连续子数组的最大和
如果数组中没有大于0的数,返回数组中最大值即可。
由于最大和的连续子数组的左子连续数组和右子连续数组的和不能小于0,否则去掉该部分会更大。所以每次累加sum,碰到结果小于0的,就令sum为0,重新开始计算,如果不小于0,则尝试去累加它,每次循环都会判断这个最大和sum是否更大,更大就更新。最后返回前面循环更新记录的最大值。
21.数组中出现次数超过一半的数字
使用unordered_map记录数字出现的次数,次数大于数组长度的一半就输出。
22.从上到下打印二叉树 II
使用BFS,遍历每一层,依次装入向量中,每一层是一个向量,最后输出二维向量即可。
23.第一个只出现一次的字符
使用unordered_map记录每个数字出现的次数,然后按序遍历输出次数为1的字符即可结束。
24.二叉树的最大深度
方法一:BFS,遍历层数即可得到深度。
方法二:DFS
①终止条件:树为空时,深度为0.
②返回值:返回当前树的最大深度。
③本级递归:此时看成三个结点root,left和right,通过递归调用得到左右子树的最大深度,然后从左右子树中选择较大深度+1并返回就是本级递归的树的最大深度。
25.和为s的两个数字
使用双指针l=0,r=n-1,判断两指针所指位置的和是否等于target,如果是,返回结果,如果大于,则--r,如果小于则++l。
26.和为s的连续正数序列
维护一个滑动窗口,左闭右开,使得窗口内数据等于目标和,其实窗口区间为[1,1)。如果窗口内数据和小于目标,则移动右窗口且更新窗口数据和;若大于,则移动左窗口且更新窗口数据和;如果相等,就记录该窗口内的连续序列为结果之一,然后移动右窗口继续找结果。
27.两个链表的第一个公共节点
想这种涉及到重复、次数相关的一般用unordered_map,key存储节点,value存储次数,先遍历第一个链表,把各节点出现的次数情况记录在map中,然后遍历第二个链表,出现的第一个value>0的节点就是两个链表的第一个公共节点。
28.翻转单词顺序
定义两个指针,l和r都从末尾开始,如果不是空格,一直--l,遇到空格,那么就使用substr(l+1, r-l)把该部分单词截取出来并加上空格放到我们的结果中即可,这是在--l并使得r=l,这两指针又被更新到倒数第二个单词的末尾了,同理进行如下操作。最后一个单词的时候l已经小于0了,不会在循环里处理,所以记得不要加上空格。
不过有两点值得注意:
①每次将单词加入之后还要附加一个空格,但最后一个单词不能加空格。
②因为是逆序处理的,需要注意前缀有空格的情况,如果前缀有空格,那么需要去掉最后一个单词加入的空格。因为前缀有空格会导致最后一个单词时l不会小于0,则是在循环里面加的。
29.在排序数组中查找数字 I
统计次数,使用unordered_map即可。
30.左旋转字符串
s.substr(n,s.length()-n)+s.substr(0,n)
31.0~n-1中缺失的数字
使用二分查找,定义双指针l=0,r=n-1。循环条件是l<=r,如果nums[mid]==mid,说明确实的在区间右边,l=mid+1,反之,在左边,r=mid-1。
32.二叉搜索树的第k大节点
二叉搜索树的中序遍历节点值大小是从小到大排序的,这样通过中序遍历,然后输出排序中第k大的就行,即nums[nums.size()-k]。
33.不用加减乘除做加法
通过(a & b) << 1得到进位c;通过a^b得到非进位赋给a;把c值赋给b,直到进位b等于之后就得到了结果。
也可用递归,每次计算出非进位和进位,直到进位为0结束。
34.扑克牌中的顺子
先排序,然后分类讨论:
①如果没有大小王,存在连续数字相差大于1的,就不是顺子。
②如果全是大小王,则是顺子。
③统计第一个非0的牌(非大小王)的位置t,说明有t张大或小王,这t张可用于弥补不连续。后续的如果连续数差值超过t+1,就不可能连续,反之能连续,就用掉了nums[i]-nums[i-1]-1张大小王,所以t需要减去前者,如果连续数相等则必不是顺子。
35.平衡二叉树
跟二叉树相关的题到部分都可以用递归解决。
①终止条件:节点为空
②返回值:如果左右节点高度差距大于1就返回-1,反之返回左右节点高度较大值+1。
③本级递归:看成三个结点root,left,right。如果左右子树不是平衡子树,或者左右子树是平衡的但是这两子树高度差距大于1,则返回-1,表示以root为根的子树不是平衡子树,反之,则返回以root为根的子树的高度。
36.圆圈中最后剩下的数字
经典约瑟夫环问题。
①最直观的思路是用队列或者链表模拟,但是貌似leetcode上会超时。
②参考leetcode上的题解,写得很好
模拟图:
以0,1,2,3,4为例
我们倒推最后的结果数字3在原数组中的下标位置。
只剩一个是,下标为0;
在第四轮下标为:(0+3)%2=1;
在第三轮下标为:(1+3)%3=1;
在第二轮下标为:(1+3)%4=0;
在第一轮下标为:(0+3)%5=3;
得到最后结果即下标是3.
总结就是(下一轮的下标位置+m)%该轮剩下的人数=该轮的下标位置。
定义一个res,i从2~n
循环求res=(res+m)%i。
最后得到的res就是幸存者的下标。
37.二叉搜索树的最近公共祖先
可以根据二叉搜索树的性质来,从根节点开始遍历判断即可。如果该此时遍历节点比p、q都要大,那么最近公共祖先一定在左子树;若比p、q都要小,那么最近公共祖先一定在右子树;其他情况那么该结点就是最近公共祖先。
38.二叉树的最近公共祖先
这题是二叉树,相对于上题从特殊变一般了。
使用递归解决。
①结束条件,结点为空或者结点等于p或q,结束。
②返回值:如果左子树不存在p或q,就返回右子树;如果右子树不存在,则返回左子树,如果左右子树都存在p或q(因为p、q唯一,所以一定是一边一个),那么就返回当前根节点。
③本级递归:看成三个结点root,left,right,left不等于(存在)p或者q,则返回right;right不等于(存在)p或者q,则返回left;left等于p,eight等于q(或者left等于q,right等于p),则返回root代表最近公共祖先。