题目一 二维数组中的查找
题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
思路
首先我们知道了最小数是第一行第一个,最大数是最后一行的最后一个,每一行最大的一个数是每一行的最后一个,每一行最小一个数是每一行的第一个。
所以先从第一行的最后一个数设为J
开始比较,如果目标大于J
,则与下一行的最后一个数比较,如此循环,直到目标比J
小
当目标比J
小时,我们就能确定是哪一行然后往该行的前面递减。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目二 替换空格
题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”
。例如,当字符串为We Are Happy.
则经过替换之后的字符串为We%20Are%20Happy
。
思路
第一个想法肯定就是从头开始找到一个空格就替换,但是这样的时间复杂度是O(n^2)
的,因为你要写两重循环,我们可以降低时间复杂度,先预处理出来空格的个数,然后从后往前碰到空格进行替换,最后得到结果。当然这题用Java
就巨简单了,Java
提供替代函数。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目三 从尾到头打印链表
题目描述
输入一个链表,按链表值从尾到头的顺序返回一个ArrayList
。
思路
因为链表是从头到尾,但是要从尾到头,所以很自然的就想到用Stack先存储对应的值,然后取出来放到ArrayList
中。
AC代码
c++
版:
1 | /** |
Java
版:
1 | /** |
题目四 重建二叉树
题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}
和中序遍历序列{4,7,2,1,5,3,8,6}
,则重建二叉树并返回。
思路
之前写过这样的思路,就是根据前序和中序排列的特性找到root
,不断进行递归就可以了。c++
版是直接拿了别人的,个人不建议这么写,可以看Java
版的会容易理解一点
AC代码
c++
版:
1 | /** |
Java
版:
1 | /** |
题目五 用两个栈实现队列
题目描述
用两个栈来实现一个队列,完成队列的Push
和Pop
操作。 队列中的元素为int
类型。
思路
一个栈存储放进来的值,一个栈用来pop
,看下代码就知道了。
AC代码
c++
版:
1 | class Solution |
Java
版:
1 | import java.util.Stack; |
题目六 旋转数组的最小数字
题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}
为{1,2,3,4,5}
的一个旋转,该数组的最小值为1。 NOTE
:给出的所有元素都大于0,若数组大小为0,请返回0。
思路
就是找到这个数组的最小值。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | import java.util.ArrayList; |
题目七 斐波那契数列
题目描述
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
n<=39
思路
经典斐波那契数列求值。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目八 跳台阶
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
思路
每层台阶可由下一层或者下两层跳上来,就是斐波那契数列递归求解即可。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目九 变态跳台阶
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
思路
和斐波那契数列推导一样,这个推一下吧。
用Fib(n)表示青蛙跳上n阶台阶的跳法数,青蛙一次性跳上n阶台阶的跳法数1(n阶跳),设定Fib(0) = 1;
当n = 1 时,只有一种跳法,即1阶跳:Fib(1) = 1;
当n = 2 时,有两种跳的方式,一阶跳和二阶跳:Fib(2) = Fib(1) + Fib(0) = 2;
当n = 3 时,有三种跳的方式,第一次跳出一阶后,后面还有Fib(3-1)中跳法;第一次跳出二阶后,后面还有Fib(3-2)中跳法;第一次跳出三阶后,后面还有Fib(3-3)中跳法
当n = n 时,共有n种跳的方式,第一次跳出一阶后,后面还有Fib(n-1)中跳法;第一次跳出二阶后,后面还有Fib(n-2)中跳法…第一次跳出n阶后,后面还有 Fib(n-n)中跳法.
Fib(n) = Fib(n-1)+Fib(n-2)+Fib(n-3)+……….+Fib(n-n)=Fib(0)+Fib(1)+Fib(2)+…….+Fib(n-1)
又因为Fib(n-1)=Fib(0)+Fib(1)+Fib(2)+…….+Fib(n-2)
两式相减得:Fib(n) = 2*Fib(n-1) n >= 2
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目十 矩形覆盖
题目描述
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
思路
n=1
:只有横放一个矩形一种解决办法n=2
:有横放一个矩形,竖放两个矩形两种解决办法n=3
:n=2
的基础上加1个横向,n=1的基础上加2个竖向n=4
:n=3
的基础上加1个横向,n=2的基础上加2个竖向
...
n=n
:n = f(n-1) + f(n-2)
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目十一 二进制中1的个数
题目描述
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
思路
把一个整数减去1,再和原整数做与运算,会把该整数最右边的一个1变成0.那么一个整数的二进制表示中有多少个1,就可以进行多少次运算
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目十二 数值的整数次方
题目描述
给定一个double
类型的浮点数base
和int
类型的整数exponent
。求base
的exponent
次方。
思路
就是简单地整数次方,但是要考虑正负数和0。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目十三 调整数组顺序使奇数位于偶数前面
题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
思路
两种做法:
c++
用空间换时间,直接把所有的值存到两个数组里面然后重新按顺序填一下就好了很简单。
Java
就是用两个变量标注奇数和偶数的位置i
标示奇数,j
表示偶数,当出现i
和j
不同的时候就把所有的奇数往前移保证奇数都在前面然后把这个偶数放在这个位置,然后继续往前找。可以画图理解一下。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目十四 链表中倒数第k个结点
题目描述
输入一个链表,输出该链表中倒数第k
个结点。
思路
解题思路就和给你一个有环的链表让你找入口,我们选择用两个指针,两个一个走快一点一个走慢一点然后它必会重合就是那个入口,这个也一样,这里使用两个指针实现一次遍历,第一个指针先走k-1
步,第二个指针一直不动;然后两个指针同时移动,知道第一个指针遍历完成。因为两个指针间隔为k-1
,所以第二个指针指向的节点即为倒数第k
个节点。
同样的题还有求链表的中间节点,我们也可以定义两个指针,同时从链表的头节点出发,一个指针一次走一步,另一个指针一次走两步。当走得快的指针走到链表的末尾时,走得慢的指针正好在链表的中间。
AC代码
c++
版:
1 | /* |
Java
版:
1 | /* |
题目十五 反转链表
题目描述
输入一个链表,反转链表后,输出新链表的表头。
思路
两种思路一个是递归,一个是遍历。分别用c++
和Java
写的。
递归:
我们使其先走到链表的末尾,确保每次回溯时都返回最后一个节点的指针。同时从倒数第二个结点开始反序。head.next.next = head;
是指使当前节点的下一个节点指向自己head.next = null
断开与下一个节点的联系,完成真正的反序操作。
遍历:
一遍遍历,保存三个指针,pre
,now
,aft
,用aft
保存下一个节点,防止链表断开后,无法继续后移然后每次now
的next
指向pre
实现反序,然后now
和pre
同时后移一步即可,直到now
指向空为止,说明链表已完成反序操作。
AC代码
c++
版:
1 | /* |
Java
版:
1 | /* |
题目十六 合并两个排序的链表
题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
思路
同上两种思路一个是递归,一个是遍历。分别用Java
和c++
写的。
递归:
新建个链表,每次两个链表当中选一个小值复制过去,那么现在是不是就是合并两个链表(其中有一个已经赋值过去了,就是之前的.next
,然后递归就可以了。
遍历:
新建一个链表,我们先把第一个值复制过去,然后当两个链表都存在的时候,谁值小就把谁链接到新链表后面然后后移一个,直到两个当中某一个为空了,最后把两个链表剩余的直接链接上去就可以了。
AC代码
c++
版:
1 | /* |
Java
版:
1 | /* |
题目十七 树的子结构
题目描述
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
思路
如果找到了对应Tree2的根节点的点,
以这个根节点为为起点判断是否包含Tree
如果找不到,那么就再去root的左孩子当作起点,去判断时候包含Tree2
如果还找不到,那么就再去root的右孩子当作起点,去判断时候包含Tree2
AC代码
c++
版:
1 |
Java
版:
1 | /** |
题目十八 二叉树的镜像
题目描述
操作给定的二叉树,将其变换为源二叉树的镜像
思路
递归。
先把特殊情况去了,然后对于每个节点,把左右节点调换一下,然后子节点递归调用即可。
递归:
新建个链表,每次两个链表当中选一个小值复制过去,那么现在是不是就是合并两个链表(其中有一个已经赋值过去了,就是之前的.next
,然后递归就可以了。
遍历:
新建一个链表,我们先把第一个值复制过去,然后当两个链表都存在的时候,谁值小就把谁链接到新链表后面然后后移一个,直到两个当中某一个为空了,最后把两个链表剩余的直接链接上去就可以了。
AC代码
c++
版:
1 | /* |
Java
版:
1 | /** |
题目十九 顺时针打印矩阵
题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
思路
画下图就知道,因为每次起点都是在(a,a)
点,所以保证每次n > flag\*2 && m > flag\*2
,然后就是四次循环跑就可以了。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | import java.util.ArrayList; |
题目二十 包含min函数的栈
题目描述
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min
函数(时间复杂度应为O(1)
)。
思路
两个栈维护,一个维护正常的栈元素,一个维护min
值栈,
那什么时候放到min
值栈呢?
第一个是min
值栈为空必须要放
第二个就是我现在放进来的值比我所有的元素值也就是min
值栈的首元素。
当然了在pop
的时候如果把最小值pop
了,min
值栈也要进行对应的pop
操作
先把特殊情况去了,然后对于每个节点,把左右节点调换一下,然后子节点递归调用即可。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | import java.util.Stack; |
题目二十一 栈的压入、弹出序列
题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5
是某栈的压入顺序,序列4,5,3,2,1
是该压栈序列对应的一个弹出序列,但4,3,5,1,2
就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
思路
这个比较简单,就是模拟一次就行,按放进的顺序往里放元素,然后碰到出栈元素就执行pop
,注意这里一个while
循环,因为可能pop
了下一个还是出栈元素 ,到最后看这个栈是不是空的就行了。
那什么时候放到min
值栈呢?
第一个是min
值栈为空必须要放
第二个就是我现在放进来的值比我所有的元素值也就是min
值栈的首元素。
当然了在pop
的时候如果把最小值pop
了,min
值栈也要进行对应的pop
操作
先把特殊情况去了,然后对于每个节点,把左右节点调换一下,然后子节点递归调用即可。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | import java.util.Stack; |
题目二十二 从上往下打印二叉树
题目描述
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
思路
二叉树的层次遍历
AC代码
c++
版:
1 | /* |
Java
版:
1 | import java.util.ArrayList; |
题目二十三 二叉搜索树的后序遍历序列
题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
思路
如果一个数组是一个二叉搜索树的后序遍历结果,那么这个数组有以下特征:数组的最后一个元素是(子)树的根节点,前面的部分可以分为两个部分,前一部分的元素都小于最后一个元素值,后一部分都大于数组最后一个元素值,我们首先需要找到这个临界值,在判断前一部分的元素是否都小于数组末尾的值,接着分别对数组前两部分处理,(递归定义)。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目二十四 二叉树中和为某一值的路径
题目描述
输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
思路
首先我们知道这题肯定要用dfs
,当一次遍历完成后,如果输入整数值恰好等于节点值之和,则输出这条路径并且回退一个节点;如果不等于则直接回退一个节点,即回退到当前节点的父节点,如果该父节点有右孩子,则继续遍历,否则继续回退。考虑回退到根节点,此时如果它有右孩子,则继续遍历,否则整个DFS结束。需要注意的是不论路径的值是否等于输入整数值,都要回退,也就是使用remove函数移除路径上的最后一个节点。
AC代码
c++
版:
1 | /* |
Java
版:
1 | import java.util.ArrayList; |
题目二十五 复杂链表的复制
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
思路
- 遍历该链表,复制每一个节点,插入到当前节点的后面.形成如下链表.
1->1’->2->2’…. - 将每个拷贝节点的随机指针域,指向原节点(即拷贝节点的上一个节点)的随即指针域指向(注意随机指针域可能为空)的下一个节点.即1的随机指针域指向3,则1’的随机指针域指向3的下一个指针3’.
- 拆分链表,返回1’->2’->3’…
AC代码
c++
版:
1 |
Java
版:
1 | /* |
题目二十六 二叉搜索树与双向链表
题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
思路
由于要求转换之后的链表是排好序的,我们可疑中序遍历树中的每一个结点,这是因为中序遍历算法的特点是按照从小到达的顺序遍历二叉树的每一个结点。当遍历到根节点的时候,我们把树堪称三部分:值为10的结点,根节点为6的左子树、根节点为14的右子树。根据排序链表的定义,值为10的结点将和它的左子树的最大的一个结点(即值为8的结点)链接起来,同时它还将和右子树最小的结点(即值为12的结点)链接起来,
按照中序遍历的顺序,当我们遍历转换到根节点(值为10的结点)时,它的左子树已经转换成一个排序的链表了,并且处在链表中的最后一个结点是当前值的最大的结点。我们把值为8的结点根节点链接起来,此时链表中的最后一个结点是10了。接着我们去遍历转换右子树,并把根节点和右子树最小的结点链接起来。至于怎么去转换它的左子树和右子树,由于遍历和转换过程是一样的,我们自然的想到了递归。
AC代码
c++
版:
1 |
Java
版:
1 | import utils.BinaryTreeNode; |
题目二十七 字符串的排列
题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
思路
我们以三个字符abc为例来分析一下求字符串排列的过程。首先我们固定第一个字符a,求后面两个字符bc的排列。当两个字符bc的排列求好之后,我们把第一个字符a和后面的b交换,得到bac,接着我们固定第一个字符b,求后面两个字符ac的排列。现在是把c放到第一位置的时候了。记住前面我们已经把原先的第一个字符a和后面的b做了交换,为了保证这次c仍然是和原先处在第一位置的a交换,我们在拿c和第一个字符交换之前,先要把b和a交换回来。在交换b和a之后,再拿c和处在第一位置的a进行交换,得到cba。我们再次固定第一个字符c,求后面两个字符b、a的排列。
AC代码
c++
版:
1 |
Java
版:
1 | import java.util.ArrayList; |
题目二十八 数组中出现次数超过一半的数字
题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
思路
有两种想法,一次遍历找到出现次数最多的那个数字,再遍历一下找到这个数出现的次数看是否满足。
或者一次遍历,用map存储对应的数字出现的次数,然后判断这个值是否满足题意。
AC代码
c++
版:
1 | /* |
Java
版:
1 | import java.util.HashMap; |
题目二十九 最小的K个数
题目描述
输入n
个整数,找出其中最小的K
个数。例如输入4,5,1,6,2,7,3,8
这8个数字,则最小的4个数字是1,2,3,4,
。
思路
有两种想法,直接sort
一遍,取前k个数即可。
或者呢可以用冒泡排序的思想,我么知道冒泡排序是一个O(n^2)
的算法,它是每次把一个数排序到最前面保证前面数有序,然后一直到整个数组有序,那么我们只需要循环k
次就行了在这里面,保证前k个数最小的就满足题意了。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | import java.util.ArrayList; |
题目三十 连续子数组的最大和
题目描述
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
思路
可以用递归去做,可以看我的LeetCode字节跳动专题有一样的。
这个是O(n)
的解法,思路是对于一个数组中的一个数x,若是x的左边的数加起来非负,那么加上x能使得值变大,这样我们认为x之前的数的和对整体和是有贡献的。如果前几项加起来是负数,则认为有害于总和。
我们用cur记录当前值, 用max记录最大值,如果cur<0,则舍弃之前的数,让cur等于当前的数字,否则,cur = cur+当前的数字。若cur和大于max更新max。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目三十一 整数中1出现的次数(从1到n整数中1出现的次数)
题目描述
求出113的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
思路
暴力循环一下,对于每个数都判断一下,应该有更好的解法,暂时没想到。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目三十二 把数组排成最小的数
题目描述
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
思路
我们需要定义一种新的比较大小规则,数组根据这个规则可以排成一个最小的数字。
排序规则:两个数字 m 和 n,我们比较 mn 和 nm 的大小,来确定在新的比较规则下 n 和 m 的大小关系,来确定哪个应该排在前面
AC代码
c++
版:
1 |
Java
版:
1 | import java.util.ArrayList; |
题目三十三 丑数
题目描述
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
思路
某个丑数肯定是前面丑数的2,3,5
倍数。只需要从前往后生成即可。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目三十四 第一个只出现一次的字符
题目描述
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).
思路
设立一个hashMap,将每个字母出现的次数进行统计。若要找出第一个,就要对string从头开始再次遍历一遍找到其在hashMap中的value值为1,则返回其下标
AC代码
c++
版:
1 |
Java
版:
1 | import java.util.HashMap; |
题目三十五 数组中的逆序对
题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
思路
利用归并排序或者树状数组,暂时写了个Java的。
AC代码
c++
版:
1 |
Java
版:
1 | public class Solution { |
题目三十六 两个链表的第一个公共结点
题目描述
输入两个链表,找出它们的第一个公共结点。
思路
一个while
循环,当不是共同结点的时候,两个链表都往后面移动
AC代码
c++
版:
1 | /* |
Java
版:
1 | /* |
题目三十七 数字在排序数组中出现的次数
题目描述
统计一个数字在排序数组中出现的次数。
思路
一遍循环,因为是有序的数组,所以一直找到第一个大于k
的数值就跳出循环,==
的时候记录数量
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目三十八 二叉树的深度
题目描述
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
思路
如果一棵树只有一个结点,它的深度为1,如果根节点只有左子树而没有右子树,那么树的深度应该是其左子树的深度+1.同样如果根节点只有右子树而没有左子树,那么树的深度应该是其右子树+1.如果既有左子树又有右子树,那概述的深度就是左、右子树的深度的较大值加1.。
所以我们可以用递归来实现代码
AC代码
c++
版:
1 | /* |
Java
版:
1 | // 非递归版本 |
题目三十九 平衡二叉树
题目描述
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
思路
首先如果某二叉树中任意结点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
而我们已经做了二叉树的深度,遍历树的每个结点的时候,调用函数TreeDepth得到它的左右子树的深度。如果每个结点的左右子树的深度相差不超过1,按照定义它就是一棵平衡的二叉树
或者呢可以用冒泡排序的思想,我么知道冒泡排序是一个O(n^2)
的算法,它是每次把一个数排序到最前面保证前面数有序,然后一直到整个数组有序,那么我们只需要循环k
次就行了在这里面,保证前k个数最小的就满足题意了。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目四十 数组中只出现一次的数字
题目描述
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
思路
首先是异或运算的一个性质:任何一个数字异或它自己都等于 0。也就是说, 如果我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字,因为那些成对出现两次的数字全部在异或中抵消了,同样应用这个思路,分为三步:
- 从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。因为其他数字都出现了两次,在异或中全部抵消了。由于这两个数字肯定不一样,那么异或的结果肯定不为 0,也就是说在这个结果数字的二进制表示中至少就有一位为 1 。
- 我们在结果数字中找到第一个为 1 的位的位置,记为第 n 位。现在我们以第 n 位是不是 1 为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第 n 位都是 1,而第二个子数组中每个数字的第 n 位都是 0。由于我们分组的标准是数字中的某一位是 1 还是 0 , 那么出现了两次的数字肯定被分配到同一个子数组。因为两个相同的数字的任意一位都是相同的,我们不可能把两个相同的数字分配到两个子数组中去,于是我们已经把原数组分成了两个子数组。
- 每个子数组都包含一个只出现一次的数字,而其他数字都出现了两次。我们已经知道如何在数组中找出唯一一个只出现一次数字
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | //num1,num2分别为长度为1的数组。传出参数 |
题目四十一 和为S的连续正数序列
题目描述
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16
的和,他马上就写出了正确答案是100
。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100
(至少包括两个数)。没多久,他就得到另一组连续正数和为100
的序列:18,19,20,21,22
。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
思路
窗口的思想,窗口里面的值加起来与我们的目标值进行对比,如果小了就让窗口右边界右移使得值变大,如果比我们的目标值大就使得窗口左边界右移使得值变小,如果刚好相等的话就把这个序列放到我们的答案序列里面。
AC代码
c++
版:
1 |
Java
版:
1 | import java.util.ArrayList; |
题目四十二 和为S的两个数字
题目描述
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
思路
暴力的想法:先选定第一个数字,然后将后面的数字依次遍历求和,并与需要的数字比较,需要n-1次,如果第一个数字不行,选择第二个,依次遍历求和。。。。需要n^2次,时间复杂度比较高
同样的窗口的思想:第一个指向第一个元素,第二个指向最后一个元素;
先拿第一个元素和最后一个元素相加,与要求的数字进行比较;如果等于,恭喜找到了;如果大于,则将第二个指针向后移一位(索引值-1),再求和进行比较;如果小于,则将第一个指针向前移一位(索引值+1),在进行求和比较;直至找到结果。
AC代码
c++
版:
1 |
Java
版:
1 | import java.util.ArrayList; |
题目四十三 左旋转字符串
题目描述
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
思路
就是一个取余的想法,还有一种就是先把它加上一段然后截取我们需要的那节。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目四十四 翻转单词顺序列
题目描述
牛客最近来了一个新员工Fish
,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat
对Fish
写的内容颇感兴趣,有一天他向Fish
借来翻看,但却读不懂它的意思。例如,“student. a am I”
。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”
。Cat
对一一的翻转这些单词顺序可不在行,你能帮助他么?
思路
利用StringBuilder
,然后倒序遍历。
也可以利用stack
的性质,我们先遍历然后放到stack
里面,然后再取出来。
AC代码
c++
版:
1 |
Java
版:
1 | public class Solution { |
题目四十五 扑克牌顺子
题目描述
LL
今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2
个大王,2
个小王(一副牌原本是54
张^_^
)他随机从中抽出了5
张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”
不是顺子.....LL
不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12
,K为13
。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”
。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true
,否则就输出false
。为了方便起见,你可以认为大小王是0
。
思路
我们根据0
的个数,我们先给序列排个序,然后除了0
,我们会得到一个序列,我们从头遍历,我们计算相邻两个数的差值这个区间缺的数我们就用0
来代替,直到最后如果区间缺的值小于0
的个数就可以成为一个So Lucky!
AC代码
c++
版:
1 |
Java
版:
1 | import java.util.Arrays; |
题目四十六 孩子们的游戏(圆圈中最后剩下的数)
题目描述
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数….这样下去….直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
思路
约瑟夫环:约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。通常解决这类问题时我们把编号从0~n-1,最后 [1] 结果+1即为原问题的解。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目四十七 求1+2+3+…+n
题目描述
求1+2+3+...+n
,要求不能使用乘除法、for、while、if、else、switch、case
等关键字及条件判断语句(A?B:C
)。
思路
首先是一个递归的想法,不能用那些条件的时候递归就是一个很好地思路。
其次我们知道这个我们可以算出来一个数学公式,就可以利用内置的函数去解决这个问题
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目四十八 不用加减乘除做加法
题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/
四则运算符号。
思路
不能进行正常的加法的话,第二个思路就是利用移位的思想来进行我们需要的运算,具体的思想我也是百度的。
AC代码
c++
版:
1 |
Java
版:
1 | public class Solution { |
题目四十九 把字符串转换成整数
题目描述
将一个字符串转换成一个整数(实现Integer.valueOf(string)
的功能,但是string
不符合数字要求时返回0
),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
。
思路
这个就是简单的转换了,注意+
,-
这个符号只有在开始才会出现,然后就是简单的字符串转换数字
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
题目五十 数组中重复的数字
题目描述
在一个长度为n
的数组里的所有数字都在0
到n-1
的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7
的数组{2,3,1,0,2,5,3}
,那么对应的输出是第一个重复的数字2
。
思路
记录一个cnt
数组,如果出现了重复就赋值到duplication
中就可以了,还是比较简单的。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 | public class Solution { |
public class Solution {
public int movingCount(int threshold, int rows, int cols)
{
boolean[][] flag = new boolean[rows][cols];
return helper(0, 0, rows, cols, threshold, flag);
}
public int helper(int i, int j, int rows,int cols, int threshold, boolean[][] flag){
if(i < 0 || j < 0 || i >= rows || j >= cols || flag[i][j] || (getSum(i) + getSum(j) > threshold)){
return 0;
}
flag[i][j] = true;
return helper(i- 1, j, rows, cols, threshold, flag)
+ helper(i, j - 1, rows, cols, threshold, flag)
+ helper(i + 1, j, rows, cols, threshold, flag)
+ helper(i , j + 1, rows, cols, threshold, flag) + 1;
}
int getSum(int x){
int sum = 0;
while( x != 0){
sum += (x + 10) % 10;
x /= 10;
}
return sum;
}
题目五十一 构建乘积数组
题目描述
给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…A[i-1]A[i+1]…A[n-1]。不能使用除法。
思路
AC代码
c++
版:
1 |
Java
版:
1 | class Solution { |
题目五十二 正则表达式匹配
题目描述
请实现一个函数用来匹配包括'.'
和'\*'
的正则表达式。模式中的字符’.’表示任意一个字符,而'\*'
表示它前面的字符可以出现任意次(包含0
次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"
与模式"a.a"
和"ab*ac*a"
匹配,但是与"aa.a"
和"ab\*a"
均不匹配
思路
递归思想可以看代码的注释
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 |
题目五十三 表示数值的字符串
题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"
和"-1E-16"
都表示数值。 但是"12e","1a3.14","1.2.3","+-5"
和"12e+4.3"
都不是。
思路
e/E
后面必须跟数字,且只能有一个e/E;不能最后一个第一次出现的符号(+/-)要么在开头,要么紧接在e/E后面,第二次出现的符号(+/-)只能在e/E后面;
小数点只能出现一次,并且只能出现在e/E前面;
不能出现除了0~9,+/-,.,e/E以外的字符
把握这四个规则,对应每个规则进行处理就可以了。
AC代码
c++
版:
1 |
Java
版:
1 | public class Solution { |
题目五十四 字符流中第一个不重复的字符
题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"
时,第一个只出现一次的字符是"g"
。当从该字符流中读出前六个字符“google"
时,第一个只出现一次的字符是"l"
。
思路
第一种就是利用string
这个内置的函数,利用find
判断,第二种就是开一个cnt
数组,然后遍历一遍
AC代码
c++
版:
1 | class Solution |
Java
版:
1 | public class Solution { |
题目五十五 链表中环的入口结点
题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null
。
思路
利用快慢指针,一个每次走两步,一个每次走一步,直到两个指针重复,找到重复的时候,再走一圈就能知道这个环的入口结点了。
AC代码
c++
版:
1 | /* |
Java
版:
1 | public class Solution { |
题目五十六 删除链表中重复的结点
题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5
处理后为 1->2->5
思路
主要看代码的注释,注释说的比较清楚。
AC代码
c++
版:
1 | /* |
Java
版:
1 | /* |
题目五十七 二叉树的下一个结点
题目描述
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
思路
如果节点存在右子树:那么它的下一个节点就是右子树中的最左节点;
如果节点没有右子树:
节点为其父节点的左子节点,那么其父节点就是它的下一个节点;
节点为其父节点的右子节点,那么需要沿其父指针一直向上遍历,一直找到某个节点是其父节点的左子节点为止,那么这个节点的父节点即是需要寻找的下一个节点。
AC代码
c++
版:
1 |
Java
版:
1 | /* |
题目五十八 对称的二叉树
题目描述
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
思路
注意镜像对称的意义,左子树的左孩子和右字树的右孩子相等,左子树的右孩子和右字树的左孩子相等,递归的思想搞定。
AC代码
c++
版:
1 |
Java
版:
1 | /* |
题目五十九 按之字形顺序打印二叉树
题目描述
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
思路
就是一个思路,奇数次从左到右入栈然后偶数次从右到左入栈,我们就开两个栈来实现就可以了,其他的就跟平时的打印二叉树一样。
不用辅助空间就递归完成即可。
AC代码
c++
版:
1 | /* |
Java
版:
1 |
|
题目六十 把二叉树打印成多行
题目描述
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
思路
二叉树的打印,利用队列实现,一个bfs
的感觉,输出就可以了。
AC代码
c++
版:
1 | /* |
Java
版:
1 |
题目六十一 序列化二叉树
题目描述
请实现两个函数,分别用来序列化和反序列化二叉树
思路
我们以前做过利用前序和中序序列来重建二叉树,这个也是差不多,利用中序的遍历实现序列化,然后进行反序列化。
AC代码
c++
版:
1 |
Java
版:
1 | /* |
题目六十二 二叉搜索树的第k个结点
题目描述
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
思路
首先我们要知道二叉搜索树是什么,根据输的性质我们知道按从小到大的顺序排列正好就是中序遍历;所以按照中序遍历查找,第k个节点就是结果。
AC代码
c++
版:
1 |
Java
版:
1 | /* |
题目六十三 数据流中的中位数
题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()
方法读取数据流,使用GetMedian()
方法获取当前读取数据的中位数。
思路
我们可以将数据排序后分为两部分,左边部分的数据总是比右边的数据小。那么,我们就可以用最大堆和最小堆来装载这些数据:
最大堆装左边的数据,取出堆顶(最大的数)的时间复杂度是O(1)
最小堆装右边的数据,同样,取出堆顶(最小的数)的时间复杂度是O(1)
从数据流中拿到一个数后,先按顺序插入堆中:如果左边的最大堆是否为空或者该数小于等于最大堆顶的数,则把它插入最大堆,否则插入最小堆。然后,我们要保证左边的最大堆的size等于右边的最小堆的size或者最大堆的size比最小堆的size大1。
要获取中位数的话,直接判断最大堆和最小堆的size,如果相等,则分别取出两个堆的堆顶除以2得到中位数,不然,就是最大堆的size要比最小堆的size大,这时直接取出最大堆的堆顶就是我们要的中位数。
AC代码
c++
版:
1 |
Java
版:
1 | import java.util.PriorityQueue; |
题目六十四 滑动窗口的最大值
题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
思路
就根据题意来做,我们可以根据窗口的大小来决定窗口的个数,然后从遍历起始点,得到窗口右边界,然后遍历这个窗口得到这个窗口的最值,然后放到vector
。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 |
题目六十五 矩阵中的路径
题目描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e
这样的3 X 4
矩阵中包含一条字符串"bcced"
的路径,但是矩阵中不包含"abcb"
路径,因为字符串的第一个字符b
占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
思路
这个其实就是bfs
和dfs
的最好的应用,遍历路径是否存在,其次还要记住有个回溯的过程。
AC代码
c++
版:
1 |
Java
版:
1 |
|
题目六十六 机器人的运动范围
题目描述
地上有一个m
行和n
列的方格。一个机器人从坐标0,0
的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k
为18
时,机器人能够进入方格(35,37)
,因为3+5+3+7 = 18
。但是,它不能进入方格(35,38)
,因为+5+3+8 = 19
。请问该机器人能够达到多少个格子?
思路
同上,就是遍历所有的点,看满足题意的个数。
AC代码
c++
版:
1 |
Java
版:
1 | public class Solution { |
题目六十七 剪绳子
题目描述
给定一根长度为n的绳子,请把绳子剪成m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],…,k[m]。请问k[0]* k[1] * … *k[m]可能的最大乘积是多少?
思路
定义函数f(n)表示为把长度为n的绳子剪成若干段后各段长度乘积的最大值。
对于第一刀,我们有n-1种可能的选择,可推导出f(n)=max{f(i)*f(n-i)};
很明显这是一个从上至下的递归,但是这个递归存在很多重复的计算,所以使用至下而上的动态规划,将子问题的最优解保存。
注意绳子剪成ix(n-i)和(n-i)xi是相同的;
注意不符合切割条件的输入n,以及输入为2、3长度时的结果,因为题中规定m>1。
AC代码
c++
版:
1 | class Solution { |
Java
版:
1 |