剑指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经典题