数据结构学习笔记三(排序)

版权声明:转载请注明出处 https://blog.csdn.net/ty13572053785/article/details/85200864

一、冒泡排序

冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求。如果不满足就让它俩互换。一次冒泡会让至少一个元素移动到它应该在的位置,重复n次,就完成了n个数据的排序工作。

//冒泡排序,n表示数组中元素个数
public void bubbleSort(int[] a,int n){
        if(n<=1)
            return ;
        for(int i=0;i<n;++i){//有n个元素一共要进行n趟
            //通过交换相邻的两个元素,使其处于正确的排序位置
            for(int j=0;j<n-i-1;++j){    //剩下的没有排序的元素个数
                if(a[j]>a[j+1]){
                    int temp=a[j];
                    a[j]=a[j+1];
                    a[j+1]=temp;
                }
            }
        }
    }

二、插入排序

将需要排序的数组分为两个区间,已排序区间和未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素。插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束。
插入排序包含两种操作,一种是元素的比较,一种是元素的移动。当我们需要将一个数据a插入到已排序区间时,需要拿a与已排序区间的元素依次比较大小,找到合适的插入位置。找到插入点之后,还需要将插入点之后的元素顺序往后移动一位,这样才能腾出位置给元素a插入。

/*
    插入排序
    把要排序的数组分成两个区间,已排序区间和未排序区间
    从未排序区间中取出元素插入到已i排序区间的合理位置
     */
public void insertionSort(int[] a,int n){
        if(n<=1)
            return;
        for(int i=1;i<n;i++){
            int value=a[i];    //未排序元素区间
            int j=i-1;         //已排序元素区间
            //查找要插入的位置并移动数据
            for(;j>=0;--j){
                if(a[j]>value){
                    a[j+1]=a[j];
                }
                else{
                    break;
                }
            }
            a[j+1]=value;    //--j,边界条件j为-1时才停
        }
    } 

三、为什么插入排序比冒泡排序受欢迎

冒泡排序和插入排序的时间复杂度都是O(n²),都是原地排序算法。它们之间的区别在于赋值操作。

            //冒泡排序
               if(a[j]>a[j+1]){
                    int temp=a[j];
                    a[j]=a[j+1];
                    a[j+1]=temp;
                }
             //插入排序
              if(a[j]>value){
                    a[j+1]=a[j];
                }

从实现代码上看,冒泡排序的数据交换要比插入排序的数据移动要复杂,冒泡排序需要三个赋值操作,而插入排序只需要一个。

四、选择排序

选择排序算法的实现类似于插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其防到已排序区间的末尾。

//选择排序
//把数组分为已排序和未排序区间,每次从未排序区间中取出最小值,放到已排序区间末尾
public void selectionSort(int[] a,int n){
        if(n<=1)
            return;
        for(int i=0;i<n-1;++i){
            int minIndex=i;
            for(int j=i+1;j<n-1;++j){
                if(a[j]<a[minIndex])
                    minIndex=j;
            }
            //交换
            int temp=a[i];
            a[i]=a[minIndex];
            a[minIndex]=temp;
        }
    }

五、三种排序的比较

是原地排序? 是否稳定? 最好 最坏 平均
冒泡排序 O(n) O(n²) O(n²)
插入排序 O(n) O(n²) O(n²)
选择排序排序 × O(n²) O(n²) O(n²)

上述三种算法适合对于小规模数据的排序,用起来非常高效。不适合大规模数据的排序,时间复杂度太高。

六、归并排序和快速排序

归并排序和快速排序用的都是分治的思想,代码都是通过递归来实现,过程非常相似。归并排序在任何情况下时间复杂度都比较稳定的排序算法,但由于归并排序不是原地排序算法,空间复杂度比较高,是O(n)。正因为此,它没有快排应用广泛。

七、桶排序

桶排序的核心思想是将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序。桶内排完序之后,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的。桶排序的时间复杂度是O(n)。桶排序比较适合用在外部排序中。
桶排序应用:
比如我们有10GB的订单数据,我们希望按订单金额进行排序,但是内存有限,只有几百MB,没办法一次性把10GB的数据都加载到内存中,这时候就可以使用桶排序。我们先扫描一遍文件,看订单金额所处的数据范围。假设经过扫描我们了解到,订单金额的最小值为1元,最大值为10万元。我们将所有订单根据金额划分到100个桶里,第一个桶存储金额在1元到1000的订单,第二个桶存储金额在1001到2000元的订单,一次类推。每一个桶对应一个文件,并且按照金额的大小顺序编号命名。理想情况下,如果订单金额在1到10万之间顺序分布,那订单会被均匀划分到100个桶文件中,每个小文件中存储大约100MB的订单数据,我们可以将这100个小文件依次防到内存中,用快排来排序。等所有文件都排好序后,我们只需要按照文件编号,从小到大依次读取每个小文件中的订单数据,并将其写入到一个文件中,这样就完成了排序。如果划分后内存仍旧放不下,可以进一步进行划分。

八、基数排序

我们有10万个手机号码,希望将这10万个手机号码从小到大排序,可以使用基数排序。先按照最后一位来排序手机号码,然后再按倒数第二位重新排序,以此类推,最后按照第一位进行排序,经过11此排序之后,手机号码就都有序了。

猜你喜欢

转载自blog.csdn.net/ty13572053785/article/details/85200864
今日推荐