剑指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;
    }
}
 

原文地址:https://www.cnblogs.com/lyeeer/p/12215817.html

时间: 2024-11-02 21:15:34

剑指offer2.4-算法和数据操作的相关文章

《剑指offer》算法题第十一天

今日题目: 滑动窗口的最大值 扑克牌中的顺子 圆圈中最后剩下的数字 求1+2+3+...+n 不用加减乘除做加法 构建乘积数组 今天的题目比较有意思,可以学到很多知识,包括第1题中的数据结构--双向队列,第3题约瑟夫环问题等. 1.滑动窗口的最大值 题目描述: 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值.例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}: 针对数组{2,3,4,2,6,

《剑指offer》算法题第三天

今日题目: 斐波那契数列 青蛙跳台阶问题(及其变种:变态跳台阶) 矩形覆盖 旋转数组的最小数字 矩阵中的路径 机器人的运动范围 细心的同学会发现,第1,2,3题其实对应的是<剑指>书上的同一道题目,即第10题斐波那契数列,这类问题属于递归问题,虽然思路比较简单,但却是属于那种不看答案想不出来,看了答案恍然大悟的题目,因此在平时同学们和博主都应该多练练这一类型的题目,培养这种递归的思维.有趣的是,博主在做题的时候发现这三道题目是可以用动态规划的思路来解决的,而且往往动态规划的所用的时间是要低于递

剑指offer-2

1. 题目 1.1 题目简介 请实现一个函数,将一个字符串中的每个空格替换成“%20”.例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy. 1.2 题目分析 该题目确实是相对而言较为简单的一个题目,在没看题目前,我以为给定的是一个String类型的字符串.初步的解题步骤是想先调用字符串的toArray方法,将字符串改变为数字,然后遍历数组,找到空格的地方并且替换它. 但是实际上题目给出的是给出的是StringBuffer str,之前没太过于关注

剑指Offer-2.替换空格(C++/Java)

题目: 请实现一个函数,将一个字符串中的每个空格替换成“%20”.例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy. 分析: 题意明确,就是将一个字符串中的每个空格替换成“%20”,可以新建一个空字符串,遍历原字符串,遇到空格就添加“%20”,否则就将字符添加进新串中. 不过这种方法需要额外的空间,我们可以在原字符串上进行操作. 如果从头开始遍历字符串,遇到空格将其替换成为“%20”,需要将后面的字符进行后移操作,时间复杂度很高. 所以我们可以先

《剑指Offer》算法题——二维数组查找

题目:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. class Solution { public: bool Find(vector<vector<int> > array, int target) { int col = array.size(); int i = 0; while (i < col) { int j = array[i].size()

《剑指Offer》算法题——“旋转数组”的最小数字

题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个非递减序列的一个旋转,输出旋转数组的最小元素.例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1. //这里说的非递减并不是乱序的,也是递增的,只不过递增的过程中可以有相同数字而已 #include <iostream> #include <vector> using namespace std; class Solution { public: int minNu

《剑指offer》算法题第八天

今日题目(对应书上第39~42题): 数组中出现次数超过一半的数字 最小的k个数(top k,重点!) 数据流中的中位数 连续子数组的最大和 今天的题目都比较经典,特别是第2题. 1. 数组中出现次数超过一半的数字 题目描述: 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2.如果不存在则输出0. 思路:有两种方法,一,利用类似于快排的思想,寻找数组中的中位

《剑指offer》算法题第九天

今日题目: 整数中1出现的次数 把数组排成最小的数 丑数 第一个只出现一次的字符位置 今天的题目相对比较难,特别是第1题和第3题很考验数学功底,下面我们一题一题来看看. 1.整数中1出现的次数 题目描述: 输入一个整数n,求1~n这n个整数的十进制表示中1出现的次数.例如,输入12,1~12这些整数中包含1的数字有1,10,11,和12,1一共出现了5次. 思路:这道题分别有递归和迭代两种解法,第一次看见这个题目比较难想到,可参考:递归:http://blog.csdn.net/gatieme/

剑指offer-2.单例模式

0 问题 实现一个单例模式 1 分析 实现单例模式的关键在于: 将构造函数设置为private,或是protected 创建一个静态函数,调用构造函数. 使用一个静态成员变量保存单例对象 因为只能在堆上分配内存,因此需要一个函数显式的调用析构函数 2 实现 class Singleton { public: static Singleton* get() { if(obj==nullptr) { MuteLock lock(mutex); if(obj==nullptr) { obj=new S