剑指offer复习6-10题_day2

6.题目:旋转数组的最小数字

题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

思路:

这道题一共有三种思路

思路一:

顺序查找. 分界点前后都是非递减数组,分界点后面的数组比前面的都要小,对其进行顺序查找,当出现后一个数比前一个小时,这个数就是最小值.

package com.matajie;
import java.util.ArrayList;

/**
 * 6.旋转数组的最小数字
 * 题目描述
 * 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
 * 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。
 * 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
 * NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
 *
 * 我的程序才不会有bug!
 * author:年仅18岁的天才少年程序员丶mata杰
 **/
//方法一.顺序查找
public class MinNumberInRotateArray {
    public int minNumberInRotateArray(int [] array) {
      if(array == null||array.length == 0){
          return 0;
      }
      for(int i = 0; i< array.length;i++){
          if(array[i+1]<array[i]){
              return array[i+1];
          }
      }
      return array[0];//全部相等,返回第一个数即可.
    }
}

思路二:

mid = low+(high-low)/2,
考虑三种情况:

  1. array[ mid ] > array[ high ].
    例如[ 3, 4, 5, 6, 0, 1, 2 ],此时最小数字一定在mid的右边,令low = mid+1.

  2. array[ mid ] == array[ high ],
    例如[1, 0 , 1, 1, 1 ] 或[ 1, 1, 1, 0, 1 ],此时最小数字不好判断在mid左边还是右边,只好一步一步缩小范围. high = high - 1,

  3. array[ mid ] < array[ high ],
    例如[ 2, 2 , 3, 4, 5, 6, 6 ] ,此时最小数字就是array[ mid ]或者在mid 的左边.令 high = mid.(注意:这里不能令high = mid+1,因为待查询的范围只剩两个数,那么mid一定会指向下表靠前的数字.例如[ 4, 6 ],array [ low ] = 4,array [ mid ] = 4,array [ high ] = 6,如果high = mid - 1,就会产生错误,但是1中low = mid+1不会产生错误)

package com.matajie;
import java.util.ArrayList;

/**
 * 6.旋转数组的最小数字
 * 题目描述
 * 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
 * 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。
 * 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
 * NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
 *
 * 我的程序才不会有bug!
 * author:年仅18岁的天才少年程序员丶mata杰
 **/
//方法二.基于二分查找
public class MinNumberInRotateArray {
    public int minNumberInRotateArray(int [] array) {
     int low = 0;int high = array.length-1;
     while (low<high){
         int mid = low + (high-low)/2;
         if(array[mid]>array[high]){
             low = mid+1;
         }else if(array[mid] == array[high]){
             high = high-1;
         }else {
             high = mid;
         }
     }
     return array[low];
    }
}

思路三:

旋转之后的数组实际上可以划分成两个有序的子数组,前面子数组的大小都大于后面子数组中的元素,而最小元素就是两个子数组的分界线.
1.我们用两个指针left,right分别指向数组的第一个元素和最后一个元素,由题可得第一个元素大于最有一个元素(在没有重复元素情况下). 如果不是旋转,第一个元素肯定小于最后一个元素
2.找到数组的中间元素
中间元素大于第一个元素,则中间元素位于前面的递增子数组,此时最小元素位于中间元素的后面.我们可以让第一个指针left指向中间元素.
移动之后第一个指针仍位于前面的递增数组中.

中间元素小于第一个元素.则中间元素位于后面的递增子数组,此时最小元素位于中间元素的前面.我们可以让第二个指针right指向中间元素.
移动之后第二个指针仍位于后面的递增数组中.

以此来缩小查找范围.
3.以上,第一个指针总是指向前面递增数组的元素,第二个指针right总是指向后面递增的数组元素,最终第一个指针将指向前面数组的最后一个元素,第二个指针指向后面数组的第一个元素,也就是说它们指向两个相邻的元素,而第二个指针指向的刚好就是最小的元素(这就是循环条件)

以上是没有重复数字的情况,有重复数字我们无法移动指针来缩小查找范围.(代码略)

本题我认为掌握第一种思路即可,第二种思路面试最好别用.

7.题目:斐波那契数列

题目描述

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

使用普通的递归显然太消耗时间了,

思路:动态规划

package com.matajie;

/**
 * 7.斐波那契数列
 * 题目描述
 *
 * 大家都知道斐波那契数列,现在要求输入一个整数n,
 * 请你输出斐波那契数列的第n项(从0开始,第0项为0)。   n<=39
 *
 * 我的程序才不会有bug!
 * author:年仅18岁的天才少年程序员丶mata杰
 **/
//动态规划
public class Fibonacci {
    public int Fibonacci(int n) {
      int f = 0;int g = 1;
      while (n-->0){
          g += f;
          f = g - f;
      }
      return f;
    }
}

8.跳台阶

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

思路:

如果只有一个台阶,则有一种跳法,如果只有两个台阶,则可以1次跳两步,也可以跳两次一步,2种跳法.
我们再来看看多层台阶的情况:
假设有6层台阶,我们可以从5跳到6,则跳到5有多少跳法,到6就有多少种.
我们也可以从4跳到6,则跳到4有多少跳法,到6就有多少种.

package com.matajie;

/**
 * 8.跳台阶
 * 题目描述
 * 一只青蛙一次可以跳上1级台阶,也可以跳上2级。
 * 求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
 *
 * 我的程序才不会有bug!
 * author:年仅18岁的天才少年程序员丶mata杰
 **/
public class JumpFloor {
    public int JumpFloor(int target) {
      if(target<=0)return -1;
      else if(target == 1)return 1;
      else if(target == 2)return 2;
      else return JumpFloor(target-1)+JumpFloor(target-2);
    }

}

很显然这就是一个斐波那契数列,这样递归显然消耗时间太长了,我们可以利用斐波那契数列从下往上算,避免重复计算,提高效率

package com.matajie;

/**
 * 8.跳台阶
 * 题目描述
 * 一只青蛙一次可以跳上1级台阶,也可以跳上2级。
 * 求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
 *
 * 我的程序才不会有bug!
 * author:年仅18岁的天才少年程序员丶mata杰
 **/
public class JumpFloor {
    public int JumpFloor(int target) {
        if(target<2)return target;
        int first = 1;
        int second = 0;
        int third = 0;
        for(int i = 0;i<target;i++){
            third = first + second;
            second = first;
            first = third;
        }
        return third;
    }

}


9.变态跳台阶

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

思路:

对一个n级台阶来说,第一步有n种跳法(如 5 级,第一步可以为1, 2, 3, 4, 5 这5种)
跳一级,剩下F(n-1)步的跳法为JumpFloor(n-1);
跳两级,剩下F(n-2)步的跳法为JumpFloor(n-2);
所以JumpFloorII(n) = JumpFloorII(n-1)+JumpFloorII(n-2)+…+JumpFloorII(1);
又因为JumpFloorII(n-1) = JumpFloorII(n-2)+…+JumpFloorII(1);
所以JumpFloorII(n) = 2 * JumpFloorII(n-1);
n = 1,JumpFloorII(1) = 1;
n = 2,JumpFloorII(2) = 2 * JumpFloorII(1) = 2;
n = 3,JumpFloorII(3) = 2 * JumpFloorII(2) = 4;

n = n,JumpFloorII(n) = 2 ^(n-1).

如果不理解,我们可以这样想:每个台阶可以看坐一块木板,让青蛙跳上去,n个台阶就有n块木板,最后一块木板是青蛙到达的位子,必须存在. 其它 n - 1 块木板可以任意选择是否存在,则每个木板有存在和不存在两种选择,n - 1 块木板就有2^(n-1)种选择.

package com.matajie;

/**
 * 我的程序才不会有bug!
 * author:年仅18岁的天才少年程序员丶mata杰
 **/
public class JumpFloorII {
    public int JumpFloorII(int target) {
      return 1 << (--target);
    }
}

矩形覆盖

题目描述

我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

思路和代码都同题8,不再解释

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值