题库来源:牛客网
《7天刷完剑指offer》系列只提供解题思路,代码实现见牛客网
题目:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
思路
解法1:暴力枚举
遍历数组中每个元素,如果找到了target,就返回true,否则返回false
时间复杂度: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( 1 ) O(1) O(1)
解法2: 二分法
二分法核心三要素:start, end, mid。根据数组的特点,从左到右递增和从上到下递增,越往左上方,值越小,越往右下方,值越大。start表示行数,从第一行开始。end表示列数,从最后一列开始。mid是二者的交汇处。如下图。如果mid小于target,start下移,如果mid大于target,end左移。退出条件是当start 或者end超出数组边界。
题目:请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
public class Solution {
public String replaceSpace(StringBuffer str) {
return str.toString().replaceAll(" ", "%20");
}
}
题目:输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
思路
先按顺序把链表的值存在一个ArrayList,然后用Collections.reverse()一下。
题目:输入二叉树的前序和中序数组,要求重建二叉树
思路
题目中的例子前序数组和中序数组分别是[1,2,3,4,5,6,7],[3,2,4,1,6,5,7]
由前序数组可知,根节点的value是1,根据中序数组可知,1的左侧[3,2,4]是左子树上的结点,右侧[6,5,7]是右子树的结点
此时根节点是2,由中序数组可知,3在2的左结点,4在2的右结点
此时根节点是5, 中序数组可以推出6是5的左结点,7是5的右结点
//1. 定义一个函数返回根节点
reconstruct(pre, in){
//7. 递归的退出:当pre数组为空时,退出
//2. 根节点的值是pre数组的第一个数
//3. 找到根节点的值在in数组中的位置index
//4. root.left = reconstruct(左子树的pre数组,左子树的in数组)
//5. root.right = reconstruct(右子树的pre数组,右子树的in数组)
//6. return root;
}
注意在递归的时候,如果每次都创建一个pre数组和in数组,要耗费大量内存,因此,我们一般用首尾两个指针表示新的数组。注意
root.left = recursion(
pre, in, preStart + 1, preStart + inIndex - inStart, inStart, inIndex - 1
);
root.right = recursion(
pre, in, preStart + 1 + inIndex - inStart , preEnd, inIndex + 1, inEnd
);
题目:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
思路:队列的特点是先进先出,栈的特点是先进后出。队列和栈的区别在于输出的顺序不一样。push操作二者是一样的,所以push到其中一个栈,假设我们用stack1存放push的值。实现队列的pop操作的时候,把需要stack1 中先入栈的元素放到栈的末尾,见下图
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
思路
解法1:暴力枚举,遍历数组找到最小值
解法2:二分法。二分法三要素:start,end,mid。常用二分法在有序数组中找target,但是这里没有target怎么办?
一种思路是把右端点(array[start])当作target,如果array[start] ≤ array[mid]并且array[start] ≥ array[end],最小值在mid和end之间;否则,最小值在start和mid之间。加上array[start] ≥ array[end]是为了防止数组没有被旋转,如果确定数组一定被旋转了,那么可以不用加上这个条件。
一种思路从结果出发(如下图),最小值要么在mid左边,有么在mid右边。当最小值在mid左边,把数组元素的相对大小画在坐标轴上,得到两条增长的线段,此时array[start] > array[mid]。同理推出右边的情况。二分法解题攻略参见我之前的博客
题目:大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。
思路:斐波那契数列就是一个数等于前两个数只和,初始两个数分别是0、1。则第2个数是1,第三个数是2,以此类推。
题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
思路
用斐波那契数列的前三个方法做
题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
思路
假设青蛙跳n个台阶的跳法总共是 f ( n ) f(n) f(n), f ( n ) f(n) f(n)取决于上一次跳的级数,上一次可以跳了1级,2级,或者n级。因此可得
f ( n ) = f ( n − 1 ) + f ( n − 2 ) + . . . + f ( 1 ) + f ( 0 ) ( 1 ) f(n) = f(n-1) + f(n-2) + ... + f(1) + f(0)\space\space\space(1) f(n)=f(n−1)+f(n−2)+...+f(1)+f(0) (1)
青蛙跳n-1个台阶的跳法总共是 f ( n − 1 ) f(n-1) f(n−1),因此得到
f ( n − 1 ) = f ( n − 2 ) + f ( n − 3 ) + . . . + f ( 1 ) + f ( 0 ) ( 2 ) f(n - 1) = f(n-2) + f(n-3) + ... + f(1) + f(0)\space\space\space(2) f(n−1)=f(n−2)+f(n−3)+...+f(1)+f(0) (2)
把 ( 1 ) (1) (1)减去 ( 2 ) (2) (2),得到 f ( n ) = 2 f ( n − 1 ) f(n) = 2f(n-1) f(n)=2f(n−1)。同理得到 f ( n − 1 ) = 2 f ( n − 2 ) f(n-1) = 2f(n-2) f(n−1)=2f(n−2),因此 f ( n ) = 2 f ( n − 1 ) = 2 2 f ( n − 2 ) = . . . = 2 n − 1 f ( 1 ) f(n) = 2f(n-1) = 2^2f(n-2) = ... = 2^{n-1}f(1) f(n)=2f(n−1)=22f(n−2)=...=2n−1f(1)
题目:我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2n的大矩形,总共有多少种方法?比如n=3时,23的矩形块有3种覆盖方法
思路
小矩形有两种放法,水平或者垂直。如图。假设。 f ( n ) f(n) f(n)表示长为n的矩形被覆盖的方法,那么 f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n) = f(n - 1) + f(n - 2) f(n)=f(n−1)+f(n−2)。用斐波那契数列的四种解法
题目:输入一个整数n,输出该数32位二进制表示中1的个数。其中负数用补码表示。
思路
位运算。这道题最简单的方法就是通过n & (n - 1)把二进制数的1从右到左消除掉。比如假设用8位二进制,n = 10, 则binary(n) = 0000 1010,binary(9) = 0000 1001, 10 & 9 = 0000 1000。此时消除了n最右边的1。