几种常见的排序算法分析学习

目录(?)[-]

  1. 冒泡排序
  2. 选择排序
  3. 1 直接插入排序
  4. 1 二分查找插入排序
  5. 希尔入排序
  6. 快速排序
  7. 归并排序
  8. 总结

本篇博客知识点 
分别描述了 冒泡,选择,直接插入,二分插入,希尔,快速以及归并排序。同时还有Java实现代码,算法分析和示意图

冒泡排序

算法描述

  • 设待排序记录序列中的记录个数为n
  • 一般地,第i趟起泡排序从1到n-i+1
  • 依次比较相邻两个记录的关键字,如果发生逆序,则交换之。
  • 其结果是这n-i+1个记录中,关键字最大的记录被交换到第n-i+1的位置上,最多作n-1趟。

算法实例 
经过五趟可以将 21 25 49 25 16 08 排为由小到大的升序 
 
其中,里面的每一趟的排序示意如下,以第一天49如何配到最后为例 

每一趟的结果都是把未排序的最大的那个数字排到最后。

算法代码实现—Java代码实现 
工具方法,交换数组中的两个位置

private static void swap(int[] num, int i, int j) {
        int temp = num[i];
        num[i] = num[j];
        num[j] = temp;

    }
//优化版---冒泡排序
    public void sort2(int [] num){
        for(int i=0;i<num.length;i++){
            boolean isOK = true;
            for(int j=0;j<num.length-i-1;j++){
                if(num[j]>num[j+1]){
                    //交换位子
                    swap(num, j, j+1);
                    isOK = false;
                }
            }
            // 当某次没有交换时,说明已经是排好序了。后面就可以不用再比较了
            if(isOK){
                System.out.println("isOk:"+isOK);
                break;
            }
        }
    }
    //冒泡排序
    public void sort1(int [] num){
        for(int i=0;i<num.length-1;i++){
            for(int j=0;j<num.length-1-i;j++){
                if(num[j]>num[j+1]){
                    //交换位子
                    swap(num,j,j+1);
                }
            }
        }
    }

算法评价

  • 时间复杂度:T(n)=O(n2)
  • 空间复杂度:S(n)=O(1) 

选择排序

算法描述

  • 首先通过n-1次比较,从n个数中找出最小的, 将它与第一个数交换——第一趟选择排序,结果最小的数被安置在第一个元素位置上。
  • 再通过n-2次比较,从剩余的n-1个数中找出关键字次小的记录,将它与第二个数交换——第二趟选择排序。
  • 重复上述过程,共经过n-1趟排序后,排序结束。

排序实例 
找到最小的数,把他放到前面去 

算法代码实现—Java代码实现

//选择排序:每一趟选出最小的数前面的数交换
    public void sort2(int a[]){
        int k = 0;// k用来比赛当前最小的数的坐标
        for(int i=0;i<a.length-1;i++){
            for(int j=i+1;j<a.length;j++){
                if(a[j]<a[k]){
                     k = j;
                }
            }
            //一轮比较完后,最小的数的位子为k  应该放的位子最前面的位子i 交换~
            swap(a, i, k);
        }
    }

算法评价

  • 时间复杂度:T(n)=O(n2)
  • 空间复杂度:S(n)=O(1) 

3.1 直接插入排序

算法分析

  • 关键字比较次数和记录移动次数与记录关键字的初始排列有关。
  • 最好情况下, 排序前记录已按关键字从小到大有序, 每趟只需与前面有序记录序列的最后一个记录比较1次, 移动2次记录, 总的关键字比较次数为 n-1, 记录移动次数为 2(n-1)。在平均情况下的关键字比较次数和记录移动次数约为n2 /4。
  • 直接插入排序是一种稳定的排序方法
  • 直接插入排序最大的优点是简单,在记录数较少时,是比较好的办法。

算法描述:

记录存放在数组R[0….n-1]中,排序过程的某一中间时刻,R被划分成两个子区间R[0…i-1]和R[i….n-1],其中:前一个子区间是已排好序的有序区;后一个子区间则是当前未排序的部分。

基本操作:

将当前无序区的第1个记录R[i]插入到有序区R[0….i-1]中适当的位置,使R[0…i]变为新的有序区。

操作细节:

  当插入第i(i≥1)个对象时, 前面的r[0], r[1], …, r[i-1]已经排好序。
  用r[i]的关键字与r[i-1], r[i-2], …的关键字顺序进行比较(和顺序查找类似),如果小于,则将r[x]向后移动(插入位置后的记录向后顺移);找到插入位置即将r[i]插入。
  • 1
  • 2

算法实例: 21, 25, 49, 25*, 16, 08 

实现代码

//3.1直接插入排序 ---原序列越有序排得越快 (逆序排得最慢)
    public void sort3_2(int a[]){
        //依次把每个元素拿来插入到 之前已经有序的子序列当中
        for(int i=0; i<a.length-1; i++){//趟数:n-1  ---除第1个元素,后面的每个元素都拿来插入一次
            //前面i个数已经排好序,现在是准备插入第i+1个数

            //待插入的数
            int temp = a[i+1];

            //找到j ----temp最终是坐在j+1的位置    j的情况:或者是-1,或者是从后往前找到的第一个没有比temp大的数
            int j=i;//从第i个位置开始从后往前依次边查找边移位置
            while(a[j]>temp){
                a[j+1] = a[j];
                j--;
                if(j<0){
                    break;
                }
            }
            //让temp坐在j+1的位置
            a[j+1]=temp;
        }
    }

3.1 二分查找插入排序

算法描述:

  • 在直接插入排序的基础上,利用二分(折半)查找算法决策出当前元素所要插入的位置。
  • 在直接插入排序的基础上,利用二分(折半)查找算法决策出当前元素所要插入的位置。
  • 找到当前元素的插入位置i之后,把i和high之间的元素从后往前依次后移一个位置,然后再把当前元素放入位置i。

算法实现:


    //3.2 加入二分查找的插入排序
        private static void binaryInsertSort(int[] a) {
            //依次把每个元素拿来插入到 之前已经有序的子序列当中
            for(int i=0; i<a.length-1; i++){//趟数:n-1  ---除第1个元素,后面的每个元素都拿来插入一次
                //前面i个数已经排好序,现在是准备插入第i+1个数

                //待插入的数
                int temp = a[i+1];

                //※※利用二分算法查找j ---j的定义同3.1
                int low=0;
                int high=i;
                int mid;
                while(low<=high){
                    //System.out.println(low+","+high);
                    mid =(low+high)/2;
                    if(a[mid]>temp){//左半区(所有右半区的数都会比temp大)
                        high=mid-1;
                    }else{//右半区
                        low = mid+1;
                    }
                }
                int j=high;//出循环后,high的位置即是我们想要找的j

                //把[j,i]部分的元素全部往后移一个位置
                for(int k=i;k>j;k--){
                    a[k+1]=a[k];
                }

                //让temp坐在j+1的位置
                a[j+1]=temp;
            }

        }

希尔入排序

1.希尔排序又称缩小增量排序,是1959年由D.L.Shell提出来的。

算法描述

1.先取定一个小于n的整数gap1作为第一个增量,把整个序列分成gap1组。所有距离为gap1的倍数的元素放在同一组中,在各组内分别进行排序(分组内采用直接插入排序或其它基本方式的排序)。
2.然后取第二个增量gap2<gap1,重复上述的分组和排序。
3.依此类推,直至增量gap=1,即所有元素放在同一组中进行排序为止。

算法分析

  • 开始时 gap 的值较大, 子序列中的元素较少, 排序速度较快。
  • 随着排序进展, gap 值逐渐变小, 子序列中元素个数逐渐变多,由于前面大多数元素已基本有序, 所以排序速度仍然很快。
  • 分组后n值减小,n2更小,而T(n)=O(n2),所以T(n)从总体上看是减小了。
  • Gap的取法有多种。 shell 提出取 gap = n/2 ,gap = gap/2 ,…,直到gap = 1。gap若是奇,则gap=gap+1

运用实例 

实现代码

//希尔排序,最小增量排序
    public void sort4_1(int a[]){
        // gab---每次减半
        for( int gab=(a.length+1)/2;gab>0;gab=(gab+1)/2){
            //组内排序---简单冒泡排序
            for(int i=0;i<a.length-gab;i++){
                for(int j=i;j<a.length-gab;j+=gab){
                    if(a[j]>a[j+gab]){
                        swap(a, j, j+gab);
                    }
                }
            }
            if(gab==1){
                break;
            }
        }
    }

快速排序

算法描述

  • 任取待排序记录序列中的某个记录(例如取第一个记录)作为基准(枢),按照该记录的关键字大小,将整个记录序列划分为左右两个子序列
  • 左侧子序列中所有记录的关键字都小于或等于基准记录的关键字
  • 右侧子序列中所有记录的关键字都大于基准记录的关键字
  • 基准记录则排在这两个子序列中间(这也是该记录最终应安放的位置)。
  • 然后分别对这两个子序列重复施行上述方法,直到所有的记录都排在相应位置上为止。
基准记录也称为枢轴(或支点)记录。取序列第一个记录为枢轴记录,其关键字为Pivotkey。指针low指向序列第一个记录位置,指针high指向序列最后一个记录位置。
  • 1

算法特点:

  • 以某个记录为界(该记录称为支点或枢轴),将待排序列分成两部分:
  • 一部分: 所有记录的关键字大于等于支点记录的关键字
  • 另一部分: 所有记录的关键字小于支点记录的关键字

算法实例 
 
 

算法分析

  • 快速排序是一个递归过程,快速排序的趟数取决于递归树的高度。
  • 如果每次划分对一个记录定位后, 该记录的左侧子序列与右侧子序列的长度相同, 则下一步将是对两个长度减半的子序列进行排序, 这是最理想的情况 
    实现代码
//快速排序
        private static void sort5_1(int[] a, int p, int r) {
            if(p<r){
                int q = partition(a,p,r);//划分之后,a[q]的位置已经排好,a[p~q-1]中的元素全部比a[q]小,a[q+1~r]中的元素全部比a[q]大
                sort5_1(a,p,q-1);//左子序列
                sort5_1(a,q+1,r);//右子序列
            }
        }
        private static int partition(int[] a, int p, int r) {
            //优化,随机取一个数和第一个数交换
            int rand = (int)(Math.random()*(r-p));
            swap(a,p,p+rand);  //把随机选中的元素换到首元素(枢轴)
            //以下代码都是以第一个数为枢轴
            int x=a[p];
            int i=p+1;//把第一个元素定为枢轴
            int j=r+1;
            while(true){
                /*
                //i--在左区找比枢轴大的数(位置)
                while(a[i]<x && i<r){
                    i++;
                }
                //j--在右区找比枢轴小的数(位置)
                while(a[j]>x){
                    j--;
                }
                */

                //i--在左区找比枢轴大的数(位置)
                while(a[i]<x && i<r){
                    i++;
                }
                //j--在右区找比枢轴小的数(位置)
                while(a[--j]>x);

                if(i>=j){
                    break;
                }
                swap(a,i,j);
            }
            //把枢轴换中间位置
            swap(a,p,j);

            return j;
        }

算法评价

  • 时间复杂度:

最好情况(每次总是选到中间值作枢轴)T(n)=O(nlogn) 
最坏情况(每次总是选到最小或最大元素作枢轴)T(n)=O(n2)

  • 空间复杂度:需栈空间以实现递归

最坏情况:S(n)=O(n) 
一般情况:S(n)=O(logn)

快速排序 优化—前面实现版本

   1.可以证明,快速排序算法在平均情况下的时间复杂性和最好情况下一样,也是O(nlogn),这在基于比较的算法类中算是快速的,快速排序也因此而得名。
   2.快速排序算法的性能取决于划分的对称性。因此通过修改partition( )方法,可以设计出采用随机选择策略的快速排序算法,从而使期望划分更对称,更低概率出现最坏情况。

归并排序

算法描述:

1.设初始序列含有n个记录,则可看成n个有序的子序列,每个子序列长度为1。
2.两两合并,得到 n/2 个长度为2或1的有序子序列。
3.再两两合并,……如此重复,直至得到一个长度为n的有序序列为止。

算法实例 

实现代码

//6归并排序
    //6.1 归并方法(要求掌握):把数组a[]当中 [left,mid] 和  [mid+1,right] 两个子序列归并,结果放在b[]
    private static void merge(int[]a, int[]b, int left,int mid, int right ){
        int p=left; //子序列a[left,mid]的遍历游标
        int r=mid+1; //子序列a[mid+1,right]的遍历游标
        int k=left; //归并结果序列b[left,right]的遍历游标

        //该过程持续到其中一个子序列归并完为止
        while( (p<=mid) && (r<=right) ){
            if( a[p]<a[r]){
                b[k++]=a[p++];
            }else{
                b[k++]=a[r++];
            }
        }
        //把另一个序列中剩下的元素直接对拷到 b 中
        if(p>mid){//左子序列归并完。把右子序列中剩下的元素对拷到b
            for(int i=r;i<=right;i++){
                b[k++]=a[i];
            }
        }else{//右子序列归并完。把左子序列中剩下的元素对拷到b
            for(int i=p;i<=mid;i++){
                b[k++]=a[i];
            }
        }
    }
    //6.2 对一个乱序序列进行用归并排序(了解即可)
    private static void mergeSort(int[]a, int left, int right){
        if (left<right) {//至少有2个元素才进行排序
            //先分解
            int mid = (left + right) / 2;
            mergeSort(a,left,mid);
            mergeSort(a,mid+1,right);

            //对上面的两个子序列进行归并排序
            int b[] = new int[a.length];//新开一个临时数组b,用于存放归并结果
            merge(a,b,left,mid,right);//把当前分解层次的两个子序列归并到数组b中
            copyArray(a,b,left,right);//把临时数组b中的数据复制到a中,由后续动作对a继续进行归并
        }
    }
    private static void copyArray(int[] a, int[] b, int left, int right) {
        for(int i=left;i<=right;i++){
            a[i] = b[i];
        }
    }

算法分析:

 1. 合并排序法主要是将两笔已排序的资料合并和进行排序。
 2.如果所读入的资料尚未排序,可以先利用其它的排序方式来处理这两笔资料,然后再将排序好的这两笔资料合并。 

算法评价

  • 时间复杂度:T(n)=O(nlogn)
  • 空间复杂度:S(n)=O(n)

总结

时间: 2024-08-03 08:59:56

几种常见的排序算法分析学习的相关文章

java几种常见的排序算法总结

[java] view plain copy /*************几种常见的排序算法总结***************************/ package paixu; public class PaiXu { final int MAX=20; int num[]=new int[MAX]; { System.out.print("生成的随机数组是:"); for(int i=0;i<20;i++){ num[i]=(int)(Math.random()*100)

几种常见的排序算法

1.插入类排序 在一个已经有序的序列中,插入一个新的记录.有直接插入排序.折半插入排序.希尔排序. 插入类排序 直接插入排序 1 void InsertSort(int R[], int n) 2 { 3 int i, j; 4 int temp; 5 for (i = 1; i < n; ++i) 6 { 7 temp = R[i]; 8 j = i - 1; 9 while (j >= 0 && temp < R[j]) 10 { 11 R[j+1] = R[j];

Python全栈开发之5、几种常见的排序算法以及collections模块提供的数据结构

在面试中,经常会遇到一些考排序算法的题,在这里,我就简单了列举了几种最常见的排序算法供大家学习,说不定以后哪天面试正好用上,文章后半段则介绍一下collections模块,因为这个模块相对于python提供的基本数据结构(list,tuple,dict)不被人们所熟悉,但是如果你对他们了解的话,用起来也是非常方便高效的. 排序算法 一.冒泡排序(BubbleSort) 步骤: 比较相邻的元素,如果第一个比第二个大,就交换他们两个. 循环一遍后,最大的数就“浮”到了列表最后的位置. 将剩下的数再次

七种常见经典排序算法总结(C++)

最近想复习下C++,很久没怎么用了,毕业时的一些经典排序算法也忘差不多了,所以刚好一起再学习一遍. 除了冒泡.插入.选择这几个复杂度O(n^2)的基本排序算法,希尔.归并.快速.堆排序,多多少少还有些晦涩难懂,幸好又博客园大神dreamcatcher-cx都总结成了图解,一步步很详细,十分感谢. 而且就时间复杂度来说,这几种算法到底有什么区别呢,刚好做了下测试. 代码参考: http://yansu.org/2015/09/07/sort-algorithms.html //: basic_so

java基础之几种常见的排序算法

一,冒泡排序 1.原理: 从数组的第一个位置开始两两比较array[index]和array[index+1],如果array[index]大于array[index+1]则交换array[index]和array[index+1]的位置,止到数组结 束: 从数组的第一个位置开始,重复上面的动作,止到数组长度减一个位置结束: 从数组的第一个位置开始,重复上面的动作,止到数组长度减二个位置结束: ....... 2. 时间复杂度:O(N2),进行了(n-1)*(n-2)....=n*(n-1)/2

几种常见的排序算法Java实现总结

public class MySort { final int MAX=20; int num[]=new int[MAX]; { System.out.print("生成的随机数组是:"); for(int i=0;i<20;i++){ num[i]=(int)(Math.random()*100); System.out.print(num[i]+" "); } System.out.println(); } int num2[]=new int[MAX]

几种常见的排序算法总结

*****选择排序***** 方法描述:首先找到第一个最小的数,和第一个数交换:然后从剩下的找出最小的与第二个交换,以此类推.效率: 长度为N的数组,大约N2/2比较,N次交换特点: 1.运行时间和输入无关,有序数组,全部相等的数组,随机数组所用时间一样,没有充分利用输入的初始状态. 2.数据移动最少,与输入成线性关系.代码: sort(int[] a){ int n = a.length; for(int i = 0; i < n; i++){ int min = i; for(int j =

用Java实现几种常见的排序算法

用Java语言实现的各种排序,包括插入排序.冒泡排序.选择排序.Shell排序.快速排序.归并排序.堆排序.SortUtil等. 插入排序: package org.rut.util.algorithm.support; import org.rut.util.algorithm.SortUtil; /** * @author treeroot * @since 2006-2-2 * @version 1.0 */ public class InsertSort implements SortU

几种常见简单排序算法

- 简单选择排序 循环遍历整个数组,每次遍历都是从当前待填充位置开始,找出未排序集合中的最小值,每次找到更小值都记录其值和位置信息,遍历完成后,将最小值与当前待填充点 进行交换.继续下一个位置,直到外循环到达数组尾. #include <iostream> using namespace std; int main() { int a[10]={23,11,6,33,14,99,23,2,14,20}; int smallest; int smallpos; for(int i=0;i<