zoukankan      html  css  js  c++  java
  • 常用算法

    一、时间复杂度和空间复杂度

    算法是指用来操作数据。解决程序问题的一组方法。对于同一个问题,使用不同的算法,也许最终得到的结果是一样的,但是过程汇总消耗的资源和时间却会由很大的区别。

    主要从算法所占用的【时间】和【空间】两个纬度去考量算法的优劣

    时间纬度:是指执行当前算法所消耗的时间,通常用【时间复杂度】来描述

    空间纬度:是指执行当前算法需要占用多少内存空间,通常用【空间复杂度】来描述

    时间复杂度

    通用的方法为【大O符号表示法】,即T(n)=O(f(n)),其中f(n)表示每行代码执行次数之和,而O 表示正比例关系,这个公式全称为:算法的渐进时间复杂度。

    常见的时间复杂度量级有:

    • 常数阶O(1)
    • 对数阶O(logN)
    • 线性阶O(n)
    • 线性对数阶O(nlogN)
    • 平方阶O(n²)
    • 立方阶O(n³)
    • K次方阶O(n^k)
    • 指数阶(2^n)

    空间复杂度

    既然时间复杂度不是用来计算程序具体耗时的,那么,空间复杂度也不是用来计算实际占用的空间的。

    空间复杂度是对一个算法在运行过程中临时占用存储空间大小的一个量度,同样反应的是一个趋势,用S(n)来定义

    空间复杂度比较常用的有:O(1)、O(n)、O(n²)

    二、二分查找法

    根据传值查询在数组中的索引

    思路:

    1、二分查找(又称折半查找),即不断将有序数组进行对半分割

    2、每次拿中间位置的数和要查找的数进行比较

    3、如果要查找的数小于中间数,则辨明要查找的数在数组的前半段

    4、如果要查找的数大于中间数,则表明要查找的数在后半段

    5、如果要查找的数等于中间数,则返回中间数

    条件:

    二分查找要求必须采用顺序存储结构,必须按关键字大小有序排列

    实现:

    public class binarySearch{
        
        public static int binarySearch(long value,long[]arr){
            int size =arr.length;
            int left=0;
            int right=size-1;
            while(left<=right){
                int middle=left+((right-left)>>1);
                if(arr[middle]>value){
                    right=middle-1;
                }else if(arr[middle]<value){
                    left=middle+1;
                }else{
                    return middle;
                }
            }
            return -1;
        }
        
        public static void main(String[] args) {
            long []arr={1,4,6,9,12,15};
            int result=binarySearch(6, arr);
            System.err.println(result);   //2
        }
    }

    三、冒泡排序(交换排序)

    思路:

    1、在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数一次进行比较和调整

    2、较大的数往下沉,较小的往上冒

    即:每当两相邻的数比较后发现他们的排序与拍讯要求相反时,就将他们互换

    public class bubbleSort {
    
        /**
         * 时间复杂度:平均情况和最差情况都是O(n^2)
         * 空间复杂度:o(1)
         */
        public static void bubbleSort(long[]arr){
            long temp;
            for(int i=0;i<arr.length-1;i++){
                for(int j=arr.length-1;j>i;j--){
                    if(arr[j]<arr[j-1]){
                        temp=arr[j];
                        arr[j]=arr[j-1];
                        arr[j-1]=temp;
                    }
                }
            }
        }
        
        public static void main(String[] args) {
            long[]arr={12,34,2,3,63,78,3,6,79,23};
            bubbleSort(arr);
            System.err.println(Arrays.toString(arr));  //[2, 3, 3, 6, 12, 23, 34, 63, 78, 79]
        }
    }

    四、快速排序(交换排序)

    思路:

    1、选取主元(以下选取数组开头为主元)

    2、小于等于主元的放左边,大于主元的放右边

    3、分别对左右递归,即重复1、2。

    图解:

    1、以数组[49,38,65,97,76,13,27,49]为例,选择第一个元素49位基准

    2、初始化关键字:[49,38,65,97,76,13,27,49]

    public class quickSort {
    
        public static void quickSort(long arr[], int left, int right) {
            int i = left;
            int j = right;
    
            long base = arr[left];
    
            while (i <= j) {
                while (arr[i] < base) {
                    i++;
                }
                while (arr[j] > base) {
                    j--;
                }
                if (i <= j) {
                    long temp;
                    temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                    i++;
                    j--;
                }
    
            }
            if (left < j) {
                quickSort(arr, left, j);
    
            }
            if (i < right) {
                quickSort(arr, i, right);
            }
        }
    
        public static void main(String[] args) {
            long[] arr = { 49, 38, 65, 97, 76, 13, 27, 49 };
            quickSort(arr, 0, arr.length - 1);
            System.err.println(Arrays.toString(arr));  //[13, 27, 38, 49, 49, 65, 76, 97]
        }
    
    }

     五、直接插入排序

    思路:

    1、在要排序的一组数组中,假设前面(n-1)[n>=2]个数已经是排好序的(局部排序)

    2、现在要把第n个数插到前面的有序数中,是的这n个数也是正好排好顺序的。

    3、如此反复循环,知道全部排好顺序

     在插入排序中,一组数据仅仅是局部有序的,而冒泡排序和选择排序,一组数据项在某个时刻是完全有序的

      

    public class insertSort {
    
        public static void insertSort(long[]arr,int left,int right){
            long temp;
            int j;
            for(int i=left+1;i<right;i++){
                
                temp=arr[i];
                j=i-1;
                while(j>=left&& arr[j]>temp){
                    arr[j+1]=arr[j--];
                }
                arr[j+1]=temp;
            }
        }
        
        public static void main(String[] args) {
            long[]arr={4,3,6,2,9,1};
            insertSort(arr, 0, arr.length-1);
            System.err.println(Arrays.toString(arr));
        }
        
    }

    六、shell

    shell排序时基于插入排序,并且添加了一些新特性,从而大大提高了插入排序的执行效率

    插入排序的缺陷:

    多次移动

    假如一个很小的数据在靠右端位置上,那么要将该数据排序到正确的位置上,则所有的中间数据都要向有移动一位

     

    shell排序的优点:

    shell排序通过加大插入排序中元素与元素之间的间隔,并且对这些间隔的元素进行插入排序,从而使得数据可以大幅度的移动。当完成该间隔的排序后,shell排序会减少数据间的间隔再进行排序,依次进行下去。shell排序也称为最小增量排序。

    思想:

    1、先将要排序的一组数按某个增量d(n/2,n为要排序的个数)根伟若干个组

    2、每个组中记录的下标相差d,对每组中全部元素进行直接插入排序

    3、然后再用一个较小的增量(d/2)对它进行分组,在每组中进行直接插入排序

    4、当增量减到1是,进行直接插入排序后,排序完成

    5、间隔的计算:间隔h的初始值为1.通过h=3*h+1来循环计算,知道间隔大于数组的大小事停止

    6、间隔的减少:通过公式h=(h-1)/3来计算

    图解:

    以长度10的数组{26,53,67,48,57,13,48,32,60,50}为例,步长序列为{5,2,1}

    public class ShellSort {
    
        public static void shellSort(int []arr){
            int len=arr.length;
            
            int h=1;
            while(h<len/3){
                h=h*3+1; //4
            }
            while(h>0){
                for(int i=h;i<len;i+=h){
                    if(arr[i]<arr[i-h]){
                        int temp=arr[i];
                        int j=i-h;
                        while(j>=0&& arr[j]>temp){
                            arr[j+h]=arr[j];
                            j-=h;
                        }
                        arr[j+h]=temp;
                    }
                }
                h=(h-1)/3;
            }
        }
        
        
        public static void shellSort2(int [] arr){
            int n=arr.length;
            for(int gap=n>>1;gap>0;gap>>=1){
                for(int i=gap;i<n;i++){
                    int temp=arr[i];
                    int j=i-gap;
                    while(j>=0&&arr[j]>temp){
                        arr[j+gap]=arr[j];
                        j-=gap;
                    }
                    arr[j+gap]=temp;
                }
            }
        }
        
        
        public static void main(String[] args) {
            int []arr={ 4, 3, 6, 2, 1, 9, 5, 8, 7};
            //shellSort(arr);
            shellSort2(arr);
            System.err.println(Arrays.toString(arr));
        }
    }

    七、直接选择排序

    思想:

    1、要在排序的一组数中,选出最小的一个数以第一个位置的数交换

    2、然后再剩下的数当中中在找到最小的与第二个位置的数交换

    3、如此循环到倒数第二个数和最后一个数比较为止

    与冒泡排序相比,选择排序将必要的交换次数从O(n*n)减少到O(N),但是比较次数仍保持为O(N*N)。

    图解:

    public class SelectSort {
        
        public static void selectSort(long[]arr){
            long temp;
            for(int i=0;i<arr.length;i++){
                int k=i;
                for(int j=i+1;j<arr.length;j++){
                    if(arr[j]<arr[k]){
                        k=j;
                    }
                }
                temp=arr[i];
                arr[i]=arr[k];
                arr[k]=temp;
            }
        }
        
        public static void main(String[] args) {
            long[]arr={4,3,6,37,1,58,6,89,23};
            selectSort(arr);
            System.err.println(Arrays.toString(arr));
        }
        
    }

     八、归并排序

    归并排序算法是目前所有排序算法中,平均时间复杂度较好O(nlgn),算法稳定性较好的一种排序算法。它的核心算法思路将大的问题分解成多个小的问题,并将结果进行合并

    思想:

    归并排序是将一个序列划分为同样大小的两个子序列,然后对两个序列分别进行排序,最后进行合并操作,将两个子序列合成有序的序列。在合成的过程中,一般的实现都需要开辟一块玉原序列大小形同的空间,已进行合并操作,所以我们需要最大O(N)的辅助空间用于存储合并序列。

    1、整个算法的拆分阶段,是将未排序的数字集合,从一个较大的集合递归拆分成若干较小的集合,这些较小的集合要么包含最多两个元素,要么就认为不够小需要继续进行拆分。

     

     

    2、那么对于一个集合中元素的排序问题就变成两个问题:较小集合最多两个元素的大小排序;如何将两个有序集合合并成一个新的有序集合。

    3、第二个问题只需要将两个集合同时进行一次遍历即可完成。比较当前集合中最小的元素,将最小元素放入新的集合,它的时间复杂度为O(N)。

     

     实现:

    1、申请空间,时期大小为两个已经排序序列之和,该空间用来存放合并后的序列

    2、设置两个指针,最初位置分别为两个已经排序序列的其实位置

    3、比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一个位置

    4、重复步骤3知道某一指针到达序列尾

    5、将另一个序列剩下的所有元素直接复制到合并序列尾

    public class MergeSort {
        
        public static void mergeSort(long []arr,int low,int hight){
            int mid=(low+hight)/2;
            if(low<hight){
                //左边
                mergeSort(arr, low, mid);
                //右边
                mergeSort(arr, mid+1, hight);
                //左右归并
                merge(arr, low, mid, hight);
            }
        }
        
        public static void merge(long[]arr,int low,int mid,int hight){
            long[]temp=new long[hight-low+1];
            int i=low;//左指针
            int j=mid+1;//右指针
            int k=0;
            //把较小的数先移到新数组
            while(i<=mid&&j<=hight){
                if(arr[i]<arr[j]){
                    temp[k++]=arr[i++];
                }else{
                    temp[k++]=arr[j++];
                }
            }
            
            //把左边剩余的数移入数组
            while(i<=mid){
                temp[k++]=arr[i++];
            }
            
            //把右边剩余的数组移入数组
            while(j<=hight){
                temp[k++]=arr[j++];
            }
            
            //把temp数组中的数覆盖arr数组
            for(int k2=0;k2<temp.length;k2++){
                arr[k2+low]=temp[k2];
            }
        }
        
        
        public static void main(String[] args) {
            long[]arr={4,3,6,2,9,1};
            mergeSort(arr, 0, arr.length-1);
            System.err.println(Arrays.toString(arr));
        }
    }

     

  • 相关阅读:
    加密CMD使电脑溢出也拿不到CMD权限
    全面提升Linux服务器的安全
    ping 源码,详细解释
    伤心一百回
    聊聊我对黑客技术的思考
    一个网管员的真实成长经历
    通过命令限制上网用户的权限
    防范黑客的简单办法
    “黑客”人生
    黑客现身讲话
  • 原文地址:https://www.cnblogs.com/houqx/p/13542035.html
Copyright © 2011-2022 走看看