1. 二维数组中的查找
题目大意
一个二维数组,每一行都是递增的,每一列也是递增的。给定一个数,判断该二维数组中是否含有该数。
思路
从右上角开始,如果当前数大于target则往左边走一步,如果小于,则往下走一步,直到找到或者找不到该数为止。
1 |
|
相关题目
按行递增
2. 替换空格
题目大意
将字符串中的空格替换为 %20
1 |
|
3. 从尾到头打印链表
思路
使用栈或者使用递归
1 |
|
4. 重建一颗二叉树
题目大意
根据前序和中序遍历的结果,恢复一棵二叉树,二叉树不含重复节点。
1 |
|
5. 两个栈实现一个队列
思路
stack2存储从stack1中pop出来的数,这样stack2的栈顶就是最开始push进stack1的数。
1 |
|
6. 旋转数组中的最小数字
题目大意
在一个旋转排序数组中寻找最小的数(456123)
思路
- 和最后一个数比
- high下标的处理
- 重复数的处理
1 |
|
7. 斐波那契数
思路
注意不要用递归,因为递归有很多的重复计算。
1 |
|
8. 跳台阶
1 |
|
9. 变态跳台阶
题目大意
每次可以跳1-n级台阶,问跳到n级台阶,一共有多少种跳法。
思路
列举n的几种情况可知,每次都是前一次的两倍的种数。
1 |
|
10. 矩形覆盖
题目大意
使用n个2 1的小矩形覆盖2 n的大矩形,一共有多少种方法。
1 |
|
11. 二进制中1的个数
题目大意
给定一个32位无符号整数,求该整数二级制形式1的个数。
思路
遍历每一位,记录1的个数即可。
不要使用除操作,耗时较久,使用移位和与操作。
如果是有符号整数,可能会导致循环无法结束,可以左移1来遍历。
n & (n-1)的操作能够将最右边的1变为0,可以将所有的1都变为0,需要的次数即为1的个数。
1 |
|
1 |
|
1 |
|
12. 数的整数次方
题目大意
实现标准库中的pow运算。
思路
考虑一些边界条件以及优化
- 指数为负的情况;
- 底为零指数为负的情况,不能做除法;
- int的指数变负为正时,考虑最大的负数变为整数会越界;
- 提高效率,将$2^{2k+1}$装换为$2^k$的平方乘2
1 |
|
13. 使得数组的奇数位于偶数前面
题目大意
给定一个数组,调整数的顺序,使得偶数都在奇数后面,且奇数和偶数内部的相对位置不变。
思路
遍历数组,将两个奇数之间的偶数往后挪动一个位置,挪出的位置放置后一个奇数。
1 |
|
14. 链表中的倒数第k个结点
思路
使用快慢指针,对于k越界情况的处理。
1 |
|
15. 翻转链表
1 |
|
16. 合并排序链表
思路
如果某条链表已经排序完,剩下链表的合并可以放到while内部也可以放到外面,外面效率更高。
1 |
|
17. 树的子结构
题目大意
给定两棵二叉树A和B,判断B是不是A的子结构。认为空树不是任意一棵树的子结构。
注意此题和subtree of another tree不同,A的子树B要求B的根节点是A中的某个结点,且叶子结点是A的叶子结点。
子结构只需要在B的结构在A中出现过即可。
思路
遍历A的每个节点作为B的根节点,如果遍历完所有节点仍然没有找到A的子结构是B,那么就返回false
1 |
|
18. 二叉树的镜像
思路
左子树右子树交换,且左子树和右子树的子树再继续交换。
1 |
|
19. 顺时针打印矩阵
题目大意
给定一个二维矩阵,返回顺时针打印的结果。
思路
传入左上角和右下角的坐标,每次打印这一圈的结果。这样一圈一圈的将矩阵打印出来。
1 |
|
20. 包含min函数的栈
题目大意
定义一个栈的数据结构,使得可以以O(1)的时间复杂度得到栈中最小元素是多少。
思路
用一个minStack记录每个位置栈中的最小元素
1 |
|
21. 栈的弹出顺序是否合理
题目大意
给定两个数组,第一个数组表示入栈的顺序,第二个数组表示出栈的顺序。试判断出栈数组是否合理。
思路
模拟出栈过程,如果栈顶元素不是当前出栈元素,则入栈一直到栈顶元素为出栈元素为止;如果入栈元素都入栈了还找不到出栈元素,则不是一个合理的出栈顺序。
1 |
|
22. 层次遍历二叉树
1 |
|
23. 判断是否是合法的搜索二叉树后序遍历序列
题目大意
给定一个序列,判断是否是合法的搜索二叉树的后序遍历序列。
思路
根据后序找到根节点,判断左子树是否都小于根节点,右子树是否多大于根节点。再递归判断左子树和右子树
1 |
|
24. 二叉搜索树与双向链表
题目大意
给定一棵二叉搜索树,将其转换为一个双向链表,要求不能创建新的节点。
思路
- 递归,转换为三个节点的二叉搜索树
1 |
|
25. 二叉树中和为某一值的路径
题目大意
找出二叉树中所有路径和为给定值的路径,路径的开始和结束分别是是根节点和叶子节点。
思路
递归
1 |
|
26. 复杂链表复制
题目大意
带有random指针的链表的复制
思路
- 使用cache记录复制前后节点的对应关系
- 插空 拆分法
1 |
|
1 |
|
27. 字符串全排列
题目大意
- 没有重复字符的字符串全排列
- 有重复字符的字符串全排列
- 没有重复字符的字符全组合(不一定用上所有字符,且不考虑组合之间的字母顺序,如12和21属于同一种)
- 有重复字符的字符全组合
1 |
|
28. 数组中出现次数超过一半的数
题目大意
如果数组中存在一个数,出现次数超过数组长度的一半,则输出该数;如果不存在则输出0
思路
- 计数法
- 记录当前数出现的次数,如果下一个数与当前数不等,次数则减1
- 如果存在该数,则最后记录的数就是出现次数超过一半的数
- 需要遍历一遍数组,判断该数出现的次数
- 按位计数法
- 对每一位进行统计,如果该位出现1的次数超过数组长度的一半,设置最后返回的结果该位为1;
- 最后判断位计数的结果是否是真的出现了超过数组长度一半的次数
1 |
|
1 |
|
补充
给定一个长度为n的数组,找出数组中出现次数超过n/3的数(https://leetcode.com/problems/majority-element-ii/description/)
29. 最小的k个数
题目大意
经典的题目,找到数组中最小的k个数
思路
- 使用最大堆
- 使用快排思想的partition(牛客网的IDE很坑爹,k存在=-1的测试样例)
1 |
|
1 |
|
30. 连续子数组的最大和
给定一个有正有负的整数数组,要求连续子数组的最大和是多少。
1 |
|
31. 从1到n整数中1出现的次数
题目大意
统计从1到n整数中1出现的次数。
思路
1 |
|
32. 将数组的数拼接成最小的数
题目大意
将数组中的数拼接组成一个数,要求返回数字形式最大的。
1 |
|
33. 计算从小到大顺序的第n个丑数
题目大意
丑数定义:只包含质因子 2、3、5的数。默认认为1是第一个丑数
思路
新的丑数 肯定是某个丑数 乘以2 或 乘以3 或 乘以5的结果,所以记录乘2、乘3、乘5的在丑数序列中的位置,每次取得最小的作为新的丑数即可。
1 |
|
34. 第一次只出现一次的字符
题目大意
找到字符串中第一次只出现一次的字符
思路
先统计字符串中每个字符出现的次数,再重新遍历字符串,第一个字符出现次数为1的字符即为所求字符。
1 |
|
35. 数组中的逆序对
题目大意
给定一个数组,统计数组中的逆序对数量,一个逆序对定义为:后面的数比前面的数小则成为一对逆序对。
思路
普通思路是遍历整个数组,每次都会去和前面的每个数比较,在比较的过程中记录逆序对的个数。该方法的时间复杂度是 $O(n^2)$
使用归并排序的方法,在归并的过程中,统计逆序对个数。因为归并排序时,前后两个数组是有序的,所以当前一个数组的某个数比后一个数组某个数大时,那么后一个数组该数以及该数之前的数都可以构成逆序对,可以快速进行计算。时间复杂度为 $O(nlogn)$
1 |
|
36. 两个链表的第一个公共节点
题目大意
给定两个链表,找到链表的第一个公共节点
思路
将两个链表各自的首尾相连,各自在各自的链表上前进,当两个指针相遇时,就是第一个公共节点。
需要判断是否有公共部分:
- 提前在外面判断,即判断两个链表最后一个节点是否相同即可
- 在while内部判断,需要进行两次判断,一次判断是否是第一个公共节点,一个判断是否存在公共节点,我们假设在两个链表后面插入一个空节点,空节点相当于两条链表的公共节点,如果相遇在空节点上,则没有公共节点。
1 |
|
37. 数字在排序数组中出现的次数
题目大意
排序数组,很容易就想到用二分法。根据二分法的判断条件不同,使得最后high指针分别停留在数字区间的两端,这样将两次的high指针指向的位置相减就是区间数字的个数。
1 |
|
38. 二叉树的最大深度
题目大意
二叉树的深度指的就是最大深度;可能也会求二叉树的最小深度。
1 |
|
39. 判断一棵树是否为平衡二叉树
题目大意
判断一棵树是否为平衡二叉树。
思路
平衡二叉树的定义:
- 左子树和右子树的高度差不超过1
- 左子树和右子树也是平衡二叉树
根据概念定义,有两个需要递归判断的部分,一个是左子树和右子树的高度差,一个是左子树和右子树是不是均为平衡二叉树。
在判断左子树和右子树高度时,可以考虑使用缓存,因为有大量的重复计算。
1 |
|
40. 数组中只出现一次的数字
题目大意
给定一个数组,只有两个数字出现过一次,其他数字都出现过两次,找出只出现一次的两个数。
思路
将数组分为两个部分,一部分包含其中一个只出现过一次的数字,这样可以通过异或的方式得到只出现一次的数字。
找到两个只出现过一次的数字的不同的位,根据该不同的位去划分两个数组即可。
1 |
|
41. 和为S的连续正序列
题目大意
给定一个整数,返回所有连续和为该整数的正整数序列。
思路
连续整数序列因为是对称的,所以序列和可以简化为 N * 平均值。
序列长度为偶数时,平均值的小数位是0.5((sum % n) * 2 == n),当序列长度为奇数时,平均值为整数(sum % n == 0)。
根据序列长度可以找到序列的第一个整数是多少。
遍历可能的序列长度,[2, sum / 2 + 1](3是特殊情况,所以是闭区间)
根据求和公式 $s = (1+n) * \frac{n}{2}$ 得到 $n = \sqrt{2n}$,所以区间为[2, $\sqrt{2n}$]
代码
1 |
|
1 |
|
42. 和为S的两个数
题目大意
给定一个排序数组和一个整数sum,要求乘积最小的和为sum的两个数
思路
和相同的两个数,两个数字差值越大乘积越小,所以直接low、high指针从头尾开始找,找到的第一对满足条件的两个数则为最终结果
1 |
|
43. 左旋转字符串
题目大意
给定一个字符串,向左旋转n位。如给定xyzabc,左旋转3位得到abcxyz
思路
和反转词序的题思路相同,可以将xyz部分和abc部分做了一次翻转。
1 |
|
44. 翻转单词顺序
思路
先翻转单词内部的字母顺序,再翻转整个字符串的顺序
1 |
|
45. 扑克牌顺子
题目大意
给定一些牌,0可以表示任意一张牌,问这些牌是否能凑成顺子。
思路
考虑0的个数,以及需要填充的gap的个数,判断0是否能填充这些gap
如果有两种非零牌相同,那么必然无法凑成顺子了。
1 |
|
46. 约瑟夫环
题目大意
0-n-1个人围成一个圈,从0喊到m-1,m-1的人被淘汰。然后从下一个人开始继续,请问最后活下来的是哪个人
思路
推导
f(n,m)表示n个小朋友,第m个人出局剩下的最后一个人;g(n-1, m)表示淘汰了f(n,m)中第m个人后按照规则剩下的那个人;
f(n,m) = g(n-1,m) = (f(n-1, m) + m) % n 下标差了m
递推公式f(n,m) = (f(n-1, m) + m)% n
1 |
|
1 |
|
47. 1+2+…+n
题目大意
求1+2…+n,要求不能使用循环、乘除和判断语句
思路
可以考虑使用递归,用短路原则结束递归。
1 |
|
48. 不用加减乘除做加法
题目大意
在不用+、-、*、\的基础上实现加法操作
思路
两个数num1和num2相加
- n1 = num1&num2的结果表示两个数该位上都是1,则需要进位,可以用左移操作替代
- n2 = num1^num2的结果表示两个数位上只有一个位置是1
- n1 & n2 如果为零,说明不需要再进位了,那么n1 | n2就是最终求和的结果;否则一直继续1和2步骤
1 |
|
49. atoi
题目大意
atoi,不是合法数值的返回0
1 |
|
50. 数组中的重复数字
题目大意
在一个长度为n的数组中,数字的大小都在0到n-1之间。其中有若干个数字是重复的,重复的次数并不知道。
思路
注:只有一个重复数字的数组,找到重复数字的方法总结:
- 如果可以改变原数组
- 排序
- 做标记
- 如果可以使用额外的空间
- hash
- 既要求不能改变原数组,又要求空间复杂度O(1),且时间复杂度小于O(n^2)
- 判断循环链表的入口
1 |
|
51. 构建乘积数组
题目大意
给定一个数组A,返回数组B,B[i] = A[1] A[2]…A[i-1] A[i+1]…
不可以使用除法。
思路
如果暴力的话就是$n^2$复杂度了。可以考虑两个数组,一个数组存储左边所有数的乘积,一个数组存储右边所有数的乘积,这样两个数组对应位置相乘就是最后的B数组了。
1 |
|
52. 正则表达式匹配
题目大意
匹配符号包括’.’和’*’,分别匹配任意字符和他之前的字符匹配任意次(包括0次)
思路
暴力匹配???
1 |
|
53. 表示数值的字符串
题目大意
判断一个字符串是否能表示数字,例如,字符串”+100”,”5e2”,”-123”,”3.1416”和”-1E-16”都表示数值。 但是”12e”,”1a3.14”,”1.2.3”,”+-5”和”12e+4.3”都不是。
思路
判断+- Ee .出现的位置是否合法,具体见code
1 |
|
54. 字符流中第一个不重复的字符
题目大意
两个接口,一个接口可以向字符流中插入字符,一个接口返回当前字符流中第一个不重复的字符。
思路
两个要求,我们得知道字符流来的顺序,第二我们得知道哪些字符已经不是出现两次了。
map存储字符出现的次数,queue存储字符流,队头是第一个不重复的字符。
每当字符流来的时候,我们需要将队头出现不止一次的字符pop掉直到维持队头是第一个不重复的字符。
1 |
|
55. 链表中环的入口节点
题目大意
给定一个链表,如果有环,返回环的入口节点,否则输出null。
思路
经典题,但是low和high必须同时从头节点开始,且判断是否有环时 while的条件不能是 low!=high
1 |
|
56. 删除排序链表中的重复节点
题目大意
给定一个排序链表,删除链表中的重复节点。比如1->2->3->3->4->5的结果是1->2->5.
思路
新建头节点
- 如果cur和cur->next相等,则一直删除重复节点直到不等为止,然后last指向第一个不等节点,继续循环
- 如果不等,则last指向当前节点,并将last和cur指针向后移动一位。
1 |
|
57. 二叉树的下一个节点
题目大意
给定一个二叉树的某个节点,返回中序遍历的后一个节点。每个节点不仅有左子树、右子树,还有指向父节点的指针。
思路
是否有右子树
- 有,则后一个节点是右子树的最深处的左节点
- 没有
- 如果是父节点的左子树,那么父节点就是后一个节点
- 如果没有父节点,那么该节点是中序遍历的最后一个节点
- 如果是父节点的右子树,找到父节点的父节点的右子树,如果没有右子树就继续向上找,直到找到父节点的右子树为止,否则该节点是中序遍历的最后一个节点
1 |
|
1 |
|
58. 判断二叉树是否对称
1 |
|
59. 之字形打印字符串
思路
- reverse 耗时
- 直接下标索引
1 |
|
1 |
|
60. 层次遍历二叉树
1 |
|
61. 序列化二叉树
题目大意
序列化、反序列化一棵二叉树
思路
- 层次遍历序列化
序列化
- 层次遍历,相邻节点用
,
隔开,空节点用#
表示。
- 层次遍历,相邻节点用
反序列化
- 新建一个节点塞进队列中,依次读取序列化结果,读取出的两个就是队列头部节点的左子树和右子树
- 递归的前序遍历
序列化
- 序列化当前节点,左节点、右节点
反序列化
- 反序列化当前节点,左子树和右子树
1 | // 层次遍历 |
1 |
|
62. 二叉搜索树的第k个节点
题目大意
给定一棵二叉搜索树,找到从小到大的第k个节点。
思路
中序遍历
1 |
|
63. 数据流中的中位数
题目大意
一个数据流中,求当前数据流的中位数。
思路
二分法
- 插入时用二分法查找,维持已有数据流的顺序
- 获取中位数时,根据已有数据流的长度,索引中位数
两个优先级队列
- 思路比较巧妙,使用一个大顶堆,使用一个小顶堆,而大顶堆存中位数前一半的数,小顶堆存储中位数后一半的数;
- 这样,如果是偶数个数,那么大顶堆和小顶堆堆顶的平均数就是中位数;如果是奇数个数,那么较多的那个堆的堆顶元素就是中位数
- 每次插入时,均需要调整堆的高度,使得两个堆的高度差最多只差一个
1 |
|
64. 滑动窗口的最大值
题目大意
给定一个数组和一个窗口大小,依次在数组滑动,返回每个窗口的最大值。
思路
- 暴力法,暴力滑过各个窗口取得最大值,时间复杂度是O(nk),每个窗口计算时重复比较了很多数
- 堆
- 用一个堆记录当前值的排序顺序,每滑动一个窗口,往堆中插入一个数,堆顶元素即使当前窗口的最大值
- 问题在于,如何弹出已经超过该窗口的元素。堆中元素包含下标,如果堆顶元素的小标超过了当前窗口的最小小标值,那么就pop掉。
- 时间复杂度O(nlogk)
- 双端队列
- 队列中存储的是降序排列的元素,自然队列头部是当前窗口的最大值,如果当前要插入的元素大于尾部数据,则弹出尾部数据,直到找到比该元素大的数插入;
- 问题在于,如何判断某个元素是否已经超过了该窗口,此时,可以考虑队列中存储下标,如果下标超过了当前窗口的最小下标,那么就需要pop掉;
1 |
|
1 |
|
65. 矩阵中路径
题目大意
给定一个字符矩阵,给定一个字符串,判断是否存在某个路径能够组成该字符串,要求路径不能覆盖重复的位置。
思路
dfs遍历,以矩阵每个节点为开始节点,dfs遍历是否存在路径。用一个visited矩阵记录是否已经走过该节点。
1 |
|
66. 机器人的运动范围
题目大意
机器人从(0,0)开始,每次可以往上下左右四个方向走动。每次到的位置不能是横坐标和纵坐标的数位之和超过k的位置。求机器人能达到多少格子。
思路
dfs,同时设置sum的条件。
1 |
|