暴躁算法(剑指系列)-每日练习4.18

2022.4.18

分治法实现二分查找

这两个经常使用,属于基础算法

就放在练习的前面

分治法实现快速排序

建立基本的代码库,用于以后便于使用。

快速排序-分解

q为基准元素将a:p:r划分成3段a:p:q-1qq+1:r,使得a:q-1中任何元素小于qq+1:r中任何元素大于q

下标q在划分过程中确定。

递归求解

递归调用快速排序算法对ap:q-1aq+1:r进行排序;

二分查找:

public int BinarySearch(int[] A,int target){
            int start = 0;
            int end = A.length;
            while(start<=end){
                //初始化中间指针
                int mid = (start+end)/2;
                //根据mid与target的大小变化start或end
                if(A[mid]==target){
                    return mid;
                }
                if(A[mid]>target){
                    end = mid-1;
                }else {
                    start= mid+1;
                }
            }
            //返回非法下标表示查找失败
            return -1;
        }

快速排序

 public static int  Partition(int[] A,int start,int end){
         int temp = A[start];
         while(start<end) {
             while (A[end] >= temp && end >start) {
                 end--;
             }
             A[start] = A[end];
             while (A[start] <= temp && start<end) {
                 start++;
             }
             A[end] = A[start];
         }
         A[start] = temp;
         return start;
     }
     public static void QuickSort(int[] A,int s,int t){
         int i;
         if(s<t){
             i = Partition(A,s,t);
             QuickSort(A,s,i-1);
             QuickSort(A,i+1,t);
         }
     }
剑指 Offer 10- II. 青蛙跳台阶问题

难度简单273

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

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

简单的动态规划问题。

动态规划5部曲

数组的定义:是d[i]代表走到第i个台阶的方法种数。

初始化数组:第0阶是1,第一阶1,第二节为2

明确递推公式:任何一阶的种数取决于他前两阶。比如第三个台阶,只能是由第一阶走一步(两阶)或者第二阶作为起点走一阶。

所以d[i] = d[i-1]+d[i-2]。(在决定好起点与终点后,走法只有一种)

咳咳,这个递推公式很明显嘛:斐波那契(这个就有一点取巧了。。)

所以直接反手把之前写的斐波那契数列的最优写法cv上去(在4.16好那篇里面)

 public int numWays(int n) {
        if(n==0){
            return 1;
        }
        if(n==1){
            return 1 ;
        }
        if(n==2){
            return 2 ;
        }
        long pre = 1L;
        long cur = 1L;
        long next = 2L;
        long temp;
        while(n>=3){
            temp =(cur+next)%1000000007;
            pre = cur;
            cur = next;
            next=temp;
            n--;
        }
        return (int)next;
    }

运算结果:

执行结果:

通过

执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户

内存消耗:38 MB, 在所有 Java 提交中击败了62.13%的用户

勉勉强强嘛.

阿巴阿巴。。

这个就是正儿八经动态规划写法

public int fib(int n) {
        if(n==0){
            return 1;
        }
        if(n==1){
            return 1 ;
        }
    //比起斐波那契,这里的初始值要稍微改一下01123——》1123
        long[] fbs = new long[n+1];
        fbs[0] = 1L;
        fbs[1]=1L;
        fbs[2]=2L;
        for(int i =3;i<fbs.length;i++){
            fbs[i]=(fbs[i-1]+fbs[i-2])%1000000007;
        }
        return (int)fbs[n];
    }

执行结果:

通过

执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户

内存消耗:38 MB, 在所有 Java 提交中击败了71.75%的用户

通过测试用例:51 / 51

这里的效果也是不错的

剑指 Offer 11. 旋转数组的最小数字

难度简单602

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。

给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2][1,2,3,4,5] 的一次旋转,该数组的最小值为 1。

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]

public int minArray(int[] numbers) {
            int cur=0;
    //特殊情况处理
            if(numbers.length==1){
                return numbers[0];
            }
    //判断当前序列是不是升序。
            while(numbers[cur]<=numbers[cur+1] && cur<numbers.length-2){
                cur++;
            }
    //排除全部是升序,最小数在下标为0的地方这种情况
            if(numbers[cur]>numbers[cur+1]){
                return numbers[cur+1];
            }
            return numbers[0];   
    }

执行结果:

通过

执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户

内存消耗:41.4 MB, 在所有 Java 提交中击败了11.76%的用户

~

优化:二分查找

最小值左边的值任何一个都会比右边所有值大。

public int minArray(int[] numbers) {
        //初始化
        int low = 0;
        int high = numbers.length - 1;
        //这里出现三种情况
        while (low < high) {
            int pivot = low + (high - low) / 2;
            //舍弃右边的值
            if (numbers[pivot] < numbers[high]) {
                high = pivot;
            } 
            //舍弃左边的值
            else if (numbers[pivot] > numbers[high]) {
                low = pivot + 1;
            }
            //如果相等,移动坐标再一次比较
            else {
                high -= 1;
            }
        }
    //结束的时候low就是最小值
        return numbers[low];
    }

int pivot = low + (high - low) / 2;

这一个很妙,一般写的是a+b)/2,这样写,用减法可以避免在相加的时候溢出。

执行结果:

通过

执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户

内存消耗:40.8 MB, 在所有 Java 提交中击败了77.48%的用户

其他写法

  • 1、分治:「二分的减治思想本来就是分治思想的特殊情况」
  • 2、递归
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值