7天刷完剑指offer(一)

题库来源:牛客网

《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超出数组边界。

7天刷完剑指offer(一)_第1张图片

替换空格

题目:请实现一个函数,将一个字符串中的每个空格替换成“%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,4], [3,2,4]

此时根节点是2,由中序数组可知,3在2的左结点,4在2的右结点

  • 回到刚才1的右子树,其前序和中序数组分别是[5,6,7][6,5,7]

此时根节点是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 中先入栈的元素放到栈的末尾,见下图

7天刷完剑指offer(一)_第2张图片

旋转数组的最小数

题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 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]。同理推出右边的情况。二分法解题攻略参见我之前的博客

7天刷完剑指offer(一)_第3张图片

斐波那契数列

题目:大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。

思路:斐波那契数列就是一个数等于前两个数只和,初始两个数分别是0、1。则第2个数是1,第三个数是2,以此类推。

  1. 递归。 func(n) = func(n - 1) + func(n - 2) 。时间复杂度:O(2^n),空间复杂度:递归栈的空间
  2. 记忆化模版。递归中计算func(6)的时候需要计算func(5),然后运行到func(5)时又要计算一遍,重复计算很多,因此通过一个数组记录已经计算了的func(i)。时间复杂度:O(n),空间复杂度:O(n)和递归栈的空间
  3. 动态规划。dp[n] = dp[n - 1] + dp[n - 2]。时间复杂度:O(n),空间复杂度:O(n)
  4. 三个变量。由于斐波那契数列之间的依赖关系,一个数取决于前两个数,那么我们只需要用三个变量分别存这个三个数。时间复杂度:O(n),空间复杂度:O(1)

跳台阶

题目:一只青蛙一次可以跳上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(n1)+f(n2)+...+f(1)+f(0)   (1)

青蛙跳n-1个台阶的跳法总共是 f ( n − 1 ) f(n-1) f(n1),因此得到

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(n1)=f(n2)+f(n3)+...+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(n1)。同理得到 f ( n − 1 ) = 2 f ( n − 2 ) f(n-1) = 2f(n-2) f(n1)=2f(n2),因此 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(n1)=22f(n2)=...=2n1f(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(n1)+f(n2)。用斐波那契数列的四种解法

7天刷完剑指offer(一)_第4张图片

二进制中1的个数

题目:输入一个整数n,输出该数32位二进制表示中1的个数。其中负数用补码表示。

思路

位运算。这道题最简单的方法就是通过n & (n - 1)把二进制数的1从右到左消除掉。比如假设用8位二进制,n = 10, 则binary(n) = 0000 1010,binary(9) = 0000 1001, 10 & 9 = 0000 1000。此时消除了n最右边的1。

你可能感兴趣的:(算法,剑指offer,数据结构,算法)