剑指offer2.4-算法和数据操作

Posted lyeeer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指offer2.4-算法和数据操作相关的知识,希望对你有一定的参考价值。

题目8

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

思路

题目给出的数组在一定程度上是排序的,因此可以使用二分查找法的思路来寻找这个最小的元素。

二分查找一般都设置两个指针P1,P2分别指向数组的第一个元素和最后一个元素,然后通过找到中间元素,比较大小不断缩小指针的指向范围。

我们需要找到的就是递增数组和递减数组的临界点。

1.第一次指向,如果中间元素大于第一个元素,那么说明中间元素位于前半部分递增数组中。可以移动P1指向中间元素的位置

2.如果中间元素小于第一个元素,那么说明中间元素位于后半部分的递减数组中,则移动P2指向中间元素的位置。

3.不断比较P1和P2指针中间元素和P1元素的大小,更新指针指向,直到两个指针指向相邻位置。这样能说明两个指针已经处于数组的临界点了。那么P2指向的元素就是最小元素,P1指向的就是最大元素

4.需要注意考虑出现重复元素的情况,下面的解答没考虑。即当两个指针指向的数字及它们的中间数字三者相等时,就无法判断中间元素处于递增还是递减数组了,无法通过移动指针来缩小范围。就需要通过顺序查找来完成。就需要在方法里加入这一情况 if array[med]==array[p1]==array[p2],就在两个指针之间通过顺序查找比较大小,找到最小值。

解答

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        int p1=0;
        int p2=array.length-1;
        int med=0;
        if(array.length==0){
            return 0;
        }
        while(p2-p1!=1){
            med=(p1+p2)/2;
            if(array[med]>=array[p1]){
                p1=med;
            }
            else
                p2=med;
        }
        if(array[p1]<=array[p2]){
            return array[p1];
        }
        else
            return array[p2];
    }
}

 

题目9

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

思路

 Fibonacci数列是典型的递归函数场景,但是由于计算过程中有很多重复的节点,因此递归解法有很严重的效率问题。

解答

public class Solution {
    public int Fibonacci(int n) {
        if(n==0){
            return 0;
        }
        else if(n==1){
            return 1;
        }
        else{
            return Fibonacci(n-1)+Fibonacci(n-2);
        }
    }
}

题目9-1

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

思路

这就是斐波那切数列的一个应用场景。

1.如果只有一级台阶,那么显然只有一种跳法。

2.如果有两级台阶,则2=2/2=1+1,有两种跳法。

3.当n>2,则有两种选择,当第一次只跳一级,后面剩下的n-1就相当于跳n-1级台阶;当第一次跳2级,后面剩下的相当于跳n-2级台阶。因此f(n)=f(n-1)+f(n-2) 。

4.我们刚提到了上面的斐波那契数列解法会产生太多的重复计算,导致效率过低。那这里我们通过保存得到的数列中间项避免重复计算,或者从下往上推,即根据f(1)和f(0)得到f(2),根据f(1)和f(2)又得到f(3)(其中f(1)就是上一次计算中的一项,而f(2)又已经通过上次计算得到,两项相加即可),我们在过程中都可以对这些上一项进行保存。就可以简化计算,得到时间复杂度为O(n)的解法。

 解答 

public class Solution {
    public int JumpFloor(int target) {
        int[] result={1,2};
        if(target<=2){
            return result[target-1];
        }
        int floorOne=1;
        int floorTwo=2;
        int floorN=0;
        for(int i=3;i<=target;++i){
            floorN=floorOne+floorTwo;
            floorOne=floorTwo;
            floorTwo=floorN;
        }
        return floorN;
    }
}

 

题目9-2 

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

思路

 这道题可以通过两种思路来解:

1.通过数学计算推导,我们可以发现f(n)=1+2^0+2^1+2^2+···+2^(n-2),那么等比数列求和就可以计算出来f(n)=2^(n-1)

2.分析可以得到f(n)=f(1)+f(2)+···+f(n-1)+1,同样可以通过上一题的迭代过程中保存上一层的值,计算得到值。

解答

public class Solution {
    public int JumpFloorII(int target) {
        int sum=1;
        int first=1;
        while(target>1){
            sum+=first;
            first=sum;
            target--;
        }
        return sum;
    }
}

 

题目9-3

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

思路

通过画图,我们可以发现这也是斐波拉契数列的一种应用。f(n)=f(n-1)+f(n-2)

注意边界值0

解答

public class Solution {
    public int RectCover(int target) {
        int[] result={0,1,2};
        if(target<=2){
            return result[target];
        }
        int firstN=1;
        int twoN=2;
        int sumN=0;
        for(int i=3;i<=target;i++){
            sumN=firstN+twoN;
            firstN=twoN;
            twoN=sumN;
        }
        return sumN;
    }
}

题目10

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。 

思路

1.Integer.bitCount(int n);//这个函数就可以用于计算整型的二进制表示中1的个数。

2.整数右移和除以2在数学上等价,但是除法的效率比移位运算要低得多,因此在编程过程中尽可能使用移位运算代替乘除法。

3.我们可以不断把输入的整数进行右移,然后判断最后一位是否为1。不断右移,直到整个整数变为0为止。

4.那么这种思路怎么判断一个整数的最右边是否为1,可以采用整数与1进行与运算。由于1除了最右一位为1,别的位都为0,那么如果整数与1进行与运算结果是1,表示该整数最右一位是1,否则是0.

5.但是这种方法如果输入一个负数,就需要保证移位后也是负数,则移位后的最高位设为1,如果一直做右移运算,这个数将陷入死循环。为了避免陷入死循环,可以不右移输入的数字n,而通过将n与1做与运算,判断n的最低位是否为1。而且这种思路循环的次数就相当于整数二进制的位数。

6.简化这个操作,那么我们就考虑将一个数减去1的情况。

n:  100100

n-1:  100011

n&(n-1):100000

每次将 n 和 n-1 进行 & 运算,从右往左去掉二进制最右边的一个1。

解答 

public class Solution {
    public int NumberOf1one(int n) {
        int count=0;
        while(n!=0){ if(n&1)
          count++;
        n=n>>1; } return count;
    }


    public int NumberOf1two(int n) {
        int count=0;
     unsigned int flag=1;
        while(flag!=0){
            if(n&flag)
          count++;
        flag=flag<<1;
        }
        return count;
    } 
  
  public
int NumberOf1three(int n) { int count=0; while(n!=0){ ++count; n=(n-1)&n; } return count; } }

 

 

以上是关于剑指offer2.4-算法和数据操作的主要内容,如果未能解决你的问题,请参考以下文章

《剑指Offer:专项突破版》 - 栈部分 JavaScript 题解

一文通数据结构与算法之——数组+常见题型与解题策略+Leetcode经典题

算法系列——左旋字符串(剑指offer)

《剑指Offer:专项突破版》 - 数组部分 JavaScript 题解

剑指offer算法总结

有人可以解释啥是 SVN 平分算法吗?理论上和通过代码片段[重复]