zoukankan      html  css  js  c++  java
  • 算法路漫漫(二) 递归与归并

    master公式

    T(N) = a*T ( N/b ) + O (N^d) 

    当log(b,a) > d  => 复杂度为O ( N^log(b,a) )
    当log(b,a) = d  => 复杂度为O ( N^d * logN )
    当log(b,a) < d  => 复杂度为O ( N^d ) 

    关于master公式详情可以参考: 算法的复杂度与 Master 定理

    1. 求一个无序数据的最大值(递归)

    递归排序算法的基本思想:将数组分为左右两半部分,分别在左/右半部分别求局部最大值,然后对这两部分的局部最大值求一个全局最大值。递归实际就是系统不断调用栈记录现场相信和还原现场信息的过程,所以所有的递归算法都可以使用非递归实现。时间复杂度O(N)

    /**
     * 
     * 递归排序
     * 复杂度T(N) = 2* T(N/2) + O(1) 
     *  a=2 b=2 d=0, log(2,2) > 0
     *  => N
     */
    @Slf4j
    public class Alg005_SearchMax {
    
    
        @Test
        public void test(){
            for(int i=0;i<5000;i++){
                int[] arr  = generateArr();
                log.info("arr:{}",arr);
                int max = getMax(arr, 0, arr.length-1);
                int matchVal = Arrays.stream(arr).max().getAsInt();
                if(max != matchVal){
                    arr =null;
                    log.info("test failed!");
                    return;
                }
                log.info("max:{}",max);
                arr =null;
            }
            log.info("test success!");
        }
    
    
    
        public int getMax(int[] arr, int left, int right){
            if(left == right){
                return arr[left];
            }
            int mid = left+ ((right-left) >> 1);
            int lMax = getMax(arr,left,mid);
            int rMax = getMax(arr, mid+1,right);
            return Math.max(lMax, rMax);
        }
        
    
        private int[] generateArr(){
            return new Random().ints(-100, 100).distinct().limit(20).toArray();
     
        }
    }

     时间复杂度分析:时间复杂度 T(N) = 2T(N/2) + O(1) .O(1)表示Math.max()方法复杂度。根据master公式可知,a = b = 2,d = 0. log(2,2) = 1< 0,所以时间复杂度为O(N).

    2. 将一个无序的数组排序 (归并排序)

    归并排序基本思路:先左边排好序,然后右边排好序,然后利用外排的方式进行排序 

    时间复杂度O(N*logN),额外空间复杂度O(N)

    /**
     * 归并排序
     * 复杂度T(N) = 2* T(N/2) + O(N) 
     * a=2 b=2 d=1, log(2,2)=1
     * => NlogN
     */
    @Slf4j
    public class Alg006_MergeSort {
    
    
        @Test
        public void test(){
            for(int i=0;i<5000;i++){
                int[] arr  = generateArr();
                mergeSort(arr, 0, arr.length-1);    
                log.info("arr:{}",arr);
            }
        }
    
    
        private void mergeSort(int[] arr, int L, int R){
            if(L == R){
                return;
            }
            int mid = L + ((R-L)>>1);
            // left part merge sort
            mergeSort(arr, L, mid);
            // right part merge sort
            mergeSort(arr,mid+1,R);
    
            merge(arr, L, mid, R);
    
        }
    
    
        private void merge(int[] arr, int L, int M, int R){
            // additional array
            int[] help = new int[R-L+1];
            int i = 0;
            int p1 = L;
            int p2 = M+1;
            // 
            while(p1<=M && p2<=R){
                help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
            }
            // if the point moved to end in the right part
            while(p1<=M){
                help[i++] = arr[p1++];
            }
            // if the point moved to end in the left part
            while(p2<=R){
                help[i++] = arr[p2++];
            }
            // copy the array to arr
            for(i = 0;i<help.length;i++){
                arr[L+i] = help[i];
            }
        }
    
    
        private int[] generateArr(){
            return new Random().ints(-100, 100).distinct().limit(20).toArray();
     
        }
        
    }

    外排:首先将数组的左右两边排好,准备一个辅助数组arr。然后a指针指向左边第一个,b指针指向右边第一个。如果a比b大,那么a指针不变,移动b指针指向下一个数;如果a比b小,那么b指针不动,移动a指针指向下一个数。谁小谁往辅助数组填,直到a或者b指针移动到最后一个数,然后将剩下的拷贝到辅助数组的后面即可。比如现在a指向3,b指向0,b小,辅助数组第一个填0,然后b移动到下一个,指向1,a不动还是指向3,b比a小,将1填进辅助数组,然后b移动到下一个,指向2,a不动还是指向3,b比a小,将2填进辅助数组,b到尽头,将a剩下的拷贝到辅助数组后面,最后将辅助数组拷贝回原数组,排序完成。

    外排的复杂度分析:左右两边T(N) = 2T(N/2),a,b指针移动,拷贝到辅助函数O(N),所以T(N) = 2T(N/2)+O(N),利用master公式可得归并排序复杂度为O(N*logN)。

    3. 在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和

    基本思路是在归并排序上的拓展:先左边排好序求小和,然后右边排好序求小和,然后合并左右求小和

    /**
     * 归并排序拓展 - 求小和 
     */
    @Slf4j
    public class Alg007_SmallSort {
    
    
        @Test
        public void test(){
            for(int i=0;i<5000;i++){
                // int[] arr = new int[]{1,2,3,4,5};
                // 4*1+3*2+2*3+4*1
                int[] arr  = generateArr();
                int[] arr2 = Arrays.copyOf(arr, arr.length);
                int val = mergeSort(arr, 0, arr.length-1);    
                int compareVal = compareVal(arr2);
                log.info("avl:{}, compareVal:{}", val, compareVal);
            }
        }
    
    
        private int mergeSort(int[] arr, int L, int R){
            if(L == R){
                return 0 ;
            }
            int mid = L + ((R-L)>>1);
            // lfet part sort and get small sum
            // right part sort and get small sum
            // merge left and right part and get small sum
           return mergeSort(arr, L, mid) + mergeSort(arr,mid+1,R) + merge(arr, L, mid, R);
        }
    
    
        private int merge(int[] arr, int L, int M, int R){
            // additional array
            int[] help = new int[R-L+1];
            int i = 0;
            int p1 = L;
            int p2 = M+1;
            int val = 0;
            // 
            while(p1<=M && p2<=R){
                // if the p1< p2, add the value (r-p2+1)* arr[p1], because alrady sorted both the left and right part 
                // if p1 >= p2, add 0
                val += arr[p1] < arr[p2] ? (R-p2+1)* arr[p1]: 0;
                help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
            }
            // if the point moved to end in the right part
            while(p1<=M){
                help[i++] = arr[p1++];
            }
            // if the point moved to end in the left part
            while(p2<=R){
                help[i++] = arr[p2++];
            }
            // copy the array to arr
            for(i = 0;i<help.length;i++){
                arr[L+i] = help[i];
            }
            return val;
        }
    
    
        private int[] generateArr(){
            return new Random().ints(-100, 100).distinct().limit(20).toArray();
     
        }
    
        private int compareVal(int[] arr){
            int val = 0;
            for(int i = arr.length-1;i>0;i--){
                for(int j = i-1;j>=0 && i>0;j--){
                    val += arr[j] < arr[i] ? arr[j] : 0;
                }
            }
            return val;
        }
        
    }
  • 相关阅读:
    Python MySQL
    selenium-java,暂停功能
    selenium-java,selenium安装配置
    selenium-java,selenium版本和火狐浏览器版本对应关系
    Jmeter-配置元件
    java-读写文件
    Jmeter-线程组执行顺序控制
    Jmeter-Interleave Controller(交替控制器)
    Jmeter-Transaction Controller(事务控制器)
    Jmeter-Logic Controllers(逻辑控制器)
  • 原文地址:https://www.cnblogs.com/hlkawa/p/14910877.html
Copyright © 2011-2022 走看看