[内排序]八大经典排序合集

news2025/1/12 19:45:24

文章目录

  • 1 排序的基本概念
    • 1.1 什么是排序
    • 1.2 排序的稳定性
    • 1.3 内排序和外排序
  • 2 插入排序
    • 2.1 直接插入排序
      • 1. 排序思路
      • 2. 直接插入排序实例
      • 3. 排序算法
      • 4. 算法分析
      • 5. 折半插入排序 / 二分插入排序
        • 5.1 排序思路
        • 5.2 排序算法
      • 5.3 算法分析
    • 2.2 希尔排序
      • 1. 排序思路
      • 2. 希尔排序实例
      • 3. 排序算法
      • 4. 算法分析
  • 3 交换排序
    • 3.1 冒泡排序
      • 1. 排序思路
      • 2. 冒泡排序实例
      • img
      • 3. 排序算法
      • 4. 算法分析
      • 5. 改进的冒泡排序算法
    • 3.2 快速排序
      • 1. 排序思路
      • 2. 快速排序实例
      • 3. 排序算法
      • 4. 算法分析
  • 4 选择排序
    • 4.1 直接选择排序
      • 1. 排序思路
      • 2. 直接选择排序实例
      • 3. 排序算法
      • 4.算法分析
    • 4.2 堆排序
      • 1. 排序思路
      • 2. 直接选择排序实例
      • 3. 排序算法
      • 4.算法分析
  • 5 归并排序
      • 1. 排序思路
      • 2. 归并排序实例
      • 3. 排序算法
      • 4. 算法分析
  • 6 基数排序
      • 1. 排序思路[#](#619425620)
      • 2. 基数排序实例
      • 3. 排序算法
      • 4. 算法分析
  • 7 各种内排序方法的比较和选择
      • 1. 各种排序方法的性能比较
      • 2. 选择排序方法应综合考虑下列因素:
      • 3. 总结

1 排序的基本概念

假定排序的数据是由一组元素组成的表,而元素由若干个数据项组成,其中有一项可用来标识该元素,称为关键字项,其值称为关键字。关键字可用作排序运算的依据。

1.1 什么是排序

所谓排序,就是整理表中的元素,使之按关键字递增或递减的顺序排列。

1.2 排序的稳定性

当待排序元素的关键字各不相同时,排序的结果是唯一的,否则排序的结果不一定唯一。

如果待排序的表中,存在多关键字相同的元素,经过排序后这些具有相同关键字的元素之间的相对次序保持不变,则称这种排序方法是稳定的;反之,若具有相同关键字的元素之间的相对次序发生变化,则称这种排序方法是不稳定的

注意,排序算法的稳定性是针对所有输入实例而言的。也就是说,在所有可能的输入实例中,只要有一个实例使得算法不满足稳定性要求,则该排序算法就是不稳定的。

1.3 内排序和外排序

在排序过程中,若整个表都是放在内存中处理,排序时不涉及内、外存数据的交换,则称之为内排序;反之,若排序过程中要进行内。外存数据的交换,则称之为外排序

内排序适用于元素个数不是很多的小表,外排序则适用于元素个数很多,不能一次将全部元素放入内存的大表。

按所用的策略不同,排序方法还可以分为需要关键字比较和不需要关键字比较两类。需要关键字比较的排序方法有插入排序。选择排序。交换排序和归并排序;不需要关键字比较的排序方法有基数排序。

img

图1.1 排序的分类

2 插入排序

插入排序基本思想:每次将一个待排序的元素,按其关键字大小插入到已经排好序的子表中的适当位置,直到全部元素插入完成为止。

2.1 直接插入排序

1. 排序思路

假设待排序的元素存放在数组 Data[0…n-1] 中,排序过程中的某一时刻,Data 被划分成两个子区间 Data[0…i-1] 和 Data[i…n-1](刚开始时 i=1,有序区只有 Data[0] 一个元素),其中,前一个子区间是已排好序的有序区,后一个子区间则是当前未排序的部分,称其为无序区。直接插入排序的一趟操作是将当前无序区的开头元素 Data[i] (1 <= i <= n-1)插入到有序区 Data[0…i-1] 中适当的位置上,使 Data[0…i] 变为新的有序区,如图2.1所示。这种方法通常称为增量法,因为它每趟操作使有序区增加一个元素。

img

说明:直接插入排序每趟产生的有序区并一定是全局有序区,也就是说有序区中的元素并不一定放在其最终的位置上。当一个元素在整个排序结束前就已经放在其最终的位置上,称为归位。

2. 直接插入排序实例

设待排序的表有 10 个元素,其关键字为 {9,8,7,6,5,4,3,2,1,0}。以下为直接插入排序的排序过程:

img

图中用方括号表示每趟操作后的有序区。每趟向有序区中插入一个元素(用方框表示),并保持有序区中的元素仍有序。

3. 排序算法

public class InsertSort {
    public static void insertSort(int[] Data) {
        int temp , j;
        for(int i = 0; i < Data.length; i++) {
            temp = Data[i];
            j = i-1;                        //从右向左在有序区Data[0..i-1]中找Data[i]的插入位置
            while(j >= 0 && temp < Data[j]) {
                Data[j+1] = Data[j];        //将大于Data[i]的元素后移
                j--;
            }
            Data[j+1] = temp;                //在j+1处插入Data[i]
        }
    }

    public static void main(String[] args) {
        int[] Data = {9,8,7,6,5,4,3,2,1,0};

        insertSort(Data);

        System.out.println("直接插入排序的结果:");
        for(int i = 0; i < Data.length; i++) {
            System.out.print(Data[i]+" ");
        }
    }
}

4. 算法分析

时间复杂度:O(n2);

空间复杂度:O(1);

稳定性:稳定;

复杂性:简单。

5. 折半插入排序 / 二分插入排序

5.1 排序思路

直接插入排序将无序区中开头元素 Data[i](1 <= i <= n-1)插入到有序区 Data[0…i-1]中,此时可以采用折半查找方法先在 Data[0…i-1] 中找到插入的位置,再通过移动元素进行插入。

在Data[low…high](初始时 low=0,high=i-1)中采用折半查找插入 Data[i] 的位置为 Data[high+1],再将 Data[high+1…i-1] 中的元素后移一个位置,并置 Data[high+1] = Data[i]。

说明:和直接插入排序一样,折半插入排序每趟产生的有序区并不一定是全局有序区。

5.2 排序算法

public class HalfInsertSort {
    public static void halfInsertSort(int[] Data) {
        int low , high , mid;
        int temp;
        for(int i = 1; i < Data.length; i++) {
            temp = Data[i];                //将Data[i]保存在temp中
            low = 0;
            high = i-1;
            while( low <= high) {        //在Data[low..high]中折半查找有序插入的位置
                mid = (low+high) / 2;    //取中间位置
                if(temp < Data[mid]) {
                    high = high-1;        //插入点在左半区
                }else {
                    low = mid + 1;        //插入点在右半区
                }
            }
            for(int j = i-1; j >= high+1; j--) {
                Data[j+1] = Data[j];    //元素后移
            }
            Data[high+1] = temp;        //插入在Data[high+1]位置
        }
    }

    public static void main(String[] args) {
        int[] Data = {9,8,7,6,5,4,3,2,1,0};

        halfInsertSort(Data);

        System.out.println("折半插入排序的结果:");
        for(int i = 0; i < Data.length; i++) {
            System.out.print(Data[i]+" ");
        }
    }
}

5.3 算法分析

从上述算法中看到,当初始数据序列为正序时,关键字的比较次数并不能减少;当初始数据序列为反序时,关键字的比较次数也不会增加。

折半插入排序的元素移动次数与直接插入排序相同,不同的仅是变分散移动为集合移动。

就平均性能而言,折半查找优于顺序查找,所以折半插入排序优于直接插入排序。

时间复杂度为:O(n2);

空间复杂度为:O(1);

稳定性:稳定;

复杂性:简单。

2.2 希尔排序

1. 排序思路

先取定一个小于数组长度 Data.length 的整数 gap(gap = Data.length / 2)作为第一个增量,把表的全部元素分成 gap 个组,所有相互之间距离为 gap 的倍数的元素放在同一个组中,在各组内进行直接插入排序;然后,减小增量 gap(gap = gap /2),重复上述的分组和排序过程,直至增量减小为 0,即所有元素放在同一组中进行直接插入排序。

说明:希尔排序每趟并不产生有序区,在最后一趟排序结束前,所有元素并不一定归位。但是每趟排序完成后,数据越来越接近有序。

2. 希尔排序实例

设待排序的表有 10 个元素,其关键字为 {9,8,7,6,5,4,3,2,1,0}。以下为直接插入排序的排序过程:

img

第一趟排序时,d = 5 ,整个表被分成 5 组:(9,4),(8,3),(7,2),(6,1),(5,0),各组采用直接插入排序方法变成有序的,即结果分别为(4,9),(3,8),(2,7),(1,6)(0,5)。

第二趟排序时,d = 2,整个表分为两组:(4,2,0,8,6)和(3,1,9,7,5),各组采用直接插入排序方法变成有序的,即结果分别为(0,2,4,6,8)和(1,3,5,7,9)。

第三趟排序时,d = 1,整个表为一组,采用直接插入排序方法使整个数列有序,最终结果为(0,1,2,3,4,5,6,7,8,9)。

3. 排序算法

public class ShellSort {
    public static void shellSort(int[] Data) {
        int gap = Data.length / 2;                //增量置初值
        int temp = 0 , j = 0;
        while(gap > 0) {
            for(int i = gap; i < Data.length; i++) {    //对所有相隔gap位置的元素组采用直接插入排序
                temp = Data[i];
                j = i - gap;
                while(j >= 0 && temp < Data[j]) {        //对相隔gap位置的元素组进行排序
                    Data[j+gap] = Data[j];
                    j -= gap;
                }
                Data[j+gap] = temp;
            }
            gap /= 2;                            //减小增量
        }
    }

    public static void main(String[] args) {
        int[] Data = {9,8,7,6,5,4,3,2,1,0};

        shellSort(Data);
        System.out.println("希尔排序的结果:");
        for(int i = 0; i < Data.length; i++) {
            System.out.print(Data[i]+" ");
        }
    }
}

4. 算法分析

时间复杂度:O(n1.3);

空间复杂度:O(1);

稳定性:不稳定;

复杂性:较复杂。

3 交换排序

3.1 冒泡排序

1. 排序思路

通过无序区中相邻元素间关键字的比较和位置的交换,使关键字最小的元素如气泡一般逐渐往上“漂浮”直至“水面”。整个算法是从最下面的元素开始,对每两个相邻的关键字进行比较,且使关键字较小的元素换至关键字较大的元素之上,使得经过一趟冒泡排序后,关键字最小的元素到达最上端。接着,再在剩下的元素中找关键字次小的元素,并把它交换到第二个位置上。依次类推,一直到所有元素都有序为止。

img
  说明:冒泡排序每趟产生的有序区一定是全局有序区,也就是说每趟产生的有序区中所有元素都归位了。

2. 冒泡排序实例

设待排序的表有 10 个元素,其关键字为 {9,8,7,6,5,4,3,2,1,0}。以下为冒泡排序的排序过程:

每次从无序区中冒出一个关键字最小的元素(用方框表示)并将其定位。

img

3. 排序算法

public class BubbleSort {
    public static void bubbleSort(int[] Data) {
        int temp = 0;
        for(int i = 0; i < Data.length-1; i++) {
            for(int j = Data.length-1 ; j > i; j--) {    //比较,找出本趟关键字最小的元素
                if(Data[j] < Data[j-1]) {
                    temp = Data[j];                //Data[j-1]与Data[j]进行交换,将关键字最小的元素前移
                    Data[j] = Data[j-1];
                    Data[j-1] = temp;
                }
            }
        }
    }

    public static void main(String[] args) {
        int[] Data = {9,8,7,6,5,4,3,2,1,0};

        bubbleSort(Data);
        System.out.println("冒泡排序的结果:");
        for(int i = 0; i < Data.length; i++) {
            System.out.print(Data[i]+" ");
        }
    }

}

4. 算法分析

时间复杂度为:O(n2);

空间复杂度为:O(1);

稳定性:稳定;

复杂性:简单。

5. 改进的冒泡排序算法

在有些情况下,在第 i (i < n-1)趟时就已经排好序了,但算法仍执行后面几趟的比较。实际上,一旦算法中某一趟比较时没有出现任何元素交换,说明已排好序了,就可以结束本算法。为此,改进冒泡排序算法如下:

public class ImproveBubbleSort {
    public static void improveBubbleSort(int[] Data) {
        boolean exchange;
        int temp = 0;
        for(int i = 0; i < Data.length-1; i++) {
            exchange = false;
            for(int j = Data.length-1; j > i; j--) {    //比较,找出本趟关键字最小的元素
                if(Data[j] < Data[j-1]) {
                    temp = Data[j];                //Data[j-1]与Data[j]进行交换,将关键字最小的元素前移
                    Data[j] = Data[j-1];
                    Data[j-1] = temp;
                    exchange = true;
                }
            }
            if(!exchange)                        //本趟没有发生交换,中途结束算法
                return;
        }
    }

    public static void main(String[] args) {
        int[] Data = {9,8,7,6,5,4,3,2,1,0};

        improveBubbleSort(Data);
        System.out.println("改进冒泡排序的结果:");
        for(int i = 0; i < Data.length; i++) {
            System.out.print(Data[i]+" ");
        }
    }
}

3.2 快速排序

1. 排序思路

在待排序的 n 个元素中任取一个元素(通常取第一个元素)作为基准,把该元素放入适当的位置后,数据序列被此元素分成两部分,所有关键字比该元素关键字小的元素放置在前一部分,所有关键字比该元素关键字大的元素放置在后一部分,并把该元素排在这两部分的中间(称该元素归位),这个过程称做一趟快速排序,之后对所有划分出来的两部分分别重复上述过程,直至每部分内只有一个元素或为空为止。

img

说明:快速排序每趟仅将一个元素归位。

2. 快速排序实例

设待排序的表有 10 个元素,其关键字为 {6,8,7,9,0,1,3,2,4,5}。以下为快速排序的排序过程:

其排序过程如图 3.4 所示(最后结果图(g)称为快速排序递归树)。第 1 趟是以 6 为基准将整个区间分为(5,4,2,3,0,1)和(9,7,8)两个子区间,并将 6 归位;对于每个子区间,又进行同样的排序,直到该子区间只有一个元素或不存在元素为止。

img

3. 排序算法

public class QuickSort {
    public static void quickSort(int[] Data , int s , int t) {
        int i = s , j = t;                        //对Data[s]至Data[t]的元素进行快速排序
        int temp = 0;
        if(s < t) {                                //区间内至少存在两个元素的情况
            temp = Data[s];                        //用区间的第1个元素作为基准
            while(i != j) {                        //从区间两端交替向中间扫描,直至i = j为止
                while(j > i && Data[j] >= temp)
                    j--;                        //从右向左扫描,找第1个小于temp的元素Data[j]
                Data[i] = Data[j];                //找到这样的Data[j],Data[i]与Data[j]交换
                while(i < j && Data[i] <= temp)
                    i++;                        //从左向右扫描,找第1个大于temp的元素Data[j]
                Data[j] = Data[i];                //找到这样的Data[i],Data[i]与Data[j]交换
            }
            Data[i] = temp;
            quickSort(Data, s, i-1);            //对左区间递归排序
            quickSort(Data, i+1, t);            //对右区间递归排序
        }
    }

    public static void main(String[] args) {
        int[] Data = {6,8,7,9,0,1,3,2,4,5};

        quickSort(Data, 0, Data.length-1);
        System.out.println("快速排序的结果:");
        for(int i = 0; i < Data.length; i++) {
            System.out.print(Data[i]+" ");
        }
    }
}

4. 算法分析

时间复杂度:O(nlog2n);

空间复杂度:O(log2n);

稳定性:不稳定;

复杂性:较复杂。

4 选择排序

4.1 直接选择排序

1. 排序思路

第 i 趟排序开始时,当前有序区和无序区分别为 Data[0…i-1] 和 Data[i…n-1](0 <= i < n-1),该趟排序是从当前无序区中选出关键字最小的元素 Data[k],将它与无序区的第 1 个元素 Data[i] 交换,使 Data[0…i] 和 Data[i+1…n-1] 分别变为新的有序区和新的无序区,如图4.1所示。因为每趟排序均使有序区中增加一个元素,且有序区中的元素关键字均不大于无序区中元素的关键字,即第 i 趟排序之后,Data[0…i] 中的关键字小于 Data[i+1…n-1] 中的所有关键字,所以进行 n-1 趟排序之后有 Data[0…n-2] 中的所有关键字小于等于 Data[n-1],也就是说,经过 n-1 趟排序之后,整个表 Data[0…n-1] 递增有序。

img 说明:直接选择排序每趟产生的有序区一定是全局有序区,也就是说每趟产生的有序区中所有元素都归位了。

2. 直接选择排序实例

设待排序的表有 10 个元素,其关键字为 {6,8,7,9,0,1,3,2,4,5}。以下为直接选择排序的排序过程:

每趟选择出一个元素(带方框者)。

img

3. 排序算法

public class SelectSort {
    public static void selectSort(int[] Data) {
        int k = 0;
        int temp = 0;
        for(int i = 0; i < Data.length-1; i++) {     //做第i趟排序
            k = i;
            for(int j = i+1; j < Data.length; j++) { //在当前无序区Data[i..n-1]中选最小的Data[k]
                if(Data[j] < Data[k])
                    k = j;                             //k记下目前找到的最小关键字所在的位置
            }
            if(k != i) {                             //交换Data[i]和Data[k]
                temp = Data[i];
                Data[i] = Data[k];
                Data[k] = temp;
            }
        }
    }

    public static void main(String[] args) {
        int[] Data = {6,8,7,9,0,1,3,2,4,5};

        selectSort(Data);
        System.out.println("直接选择排序的结果:");
        for(int i = 0; i < Data.length; i++) {
            System.out.print(Data[i]+" ");
        }
    }

}

4.算法分析

时间复杂度:O(n2);

空间复杂度:O(1);

稳定性:不稳定;

复杂性:简单。

4.2 堆排序

1. 排序思路

堆排序是一种树形选择排序方法,它的特点是,在排序过程中,将 Data[0…n-1] 看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲节点和孩子节点之间的内在关系,在当前无序区中选择关键字最大(或最小)的元素。

堆排序的排序过程中每次挑选最大元素归位,如图4.3所示。挑选最大元素的方法是将数组中存储的数据看成是一棵完全二叉树,利用完全二叉树中双亲节点和孩子节点之间的内在关系来选择关键字最大的元素。具体的做法是:把待排序的表的关键字存放在数组Data[0…n-1] 之中,将 Data 看做一棵二叉树,每个节点表示一个元素,源表的第一个元素Data[0] 作为二叉树的根,以下各元素 Data[1…n-1] 依次逐层从左到右顺序排列,构成一棵完全二叉树,节点 Data[i] 的左孩子是 Data[2i+1],右孩子是 Data[2i+2],双亲是 Data[(i+1)/2-1]。

img
说明:堆排序每趟产生的有序区一定是全局有序区,也就是说每趟产生的有序区中所有元素都归位了。

2. 直接选择排序实例

设待排序的表有 10 个元素,其关键字为 {6,8,7,9,0,1,3,2,4,5}。以下为堆排序的排序过程:

其初始状态如图4.4(a)所示,通过第一个 for 循环调用 sift() 产生的初始堆如图4.4(b)所示,这时 Data 中关键字序列为{9,8,7,6,5,1,3,2,4,0}。

img
堆排序的排序过程如图4.5所示,每输出一个元素,就对堆进行一次筛选调整。

img

3. 排序算法

public class HeapSort {
    public static void heapSort(int[] Data , int n) {
        int temp = 0;
        for(int i = (n+1)/2-1; i >= 0; i--)    //循环建立初始堆,第一个双亲节点的位置序号为(n+1)/2-1
            sift(Data , i , n-1);
        for(int i = n-1; i >= 1; i--) {        //进行n-1趟堆排序,每一趟堆排序的元素个数减1
            temp= Data[0];                    //将最后一个元素同当前区间内Data[0]对换
            Data[0] = Data[i];
            Data[i] = temp;
            sift(Data , 0 , i-1);            //筛选Data[0]节点,得到i个节点的堆
        }
    }

    // 调整堆
    private static void sift(int[] Data, int low, int high) {
        int place = 2 * low + 1;            //该节点的左孩子在数组中的位置序号
        int temp = Data[low];                //保存当前节点
        while(place <= high) {
            if(place+1 <= high && Data[place] < Data[place+1])    //若右孩子较大,则把place指向右孩子
                place++;
            if(temp < Data[place]) {
                Data[low] = Data[place];    //将Data[place]调整到双亲节点位置上
                low = place;                //修改low和place值,以便继续向下筛选
                place = 2 * low+1;
            } else
                break;                        //筛选结束
        }
        Data[low] = temp;                    //被筛选节点的值放入最终位置
    }

    public static void main(String[] args) {
        int[] Data = {6,8,7,9,0,1,3,2,4,5};

        heapSort(Data,Data.length);
        System.out.println("堆排序的结果:");
        for(int i = 0; i < Data.length; i++) {
            System.out.print(Data[i]+" ");
        }
    }

}

4.算法分析

时间复杂度:O(nlog2n);

空间复杂度:O(1);

稳定性:不稳定;

复杂性:较复杂。

5 归并排序

1. 排序思路

归并排序是多次将两个或两个以上的有序表合并成一个新的有序表。最简单的归并排是直接将两个有序的子表合并成一个有序的表即二路归并。

将 Data[0…n-1] 看成是 n 个长度为 1 的有序序列,然后进行亮亮归并,得到 [n/2] 个长度为 2(最后一个有序序列的长度可能为 1)的有序序列,再进行两两归并,得到 [n/4] 个长度为 4(最后一个有序序列的长度可能小于 4)的有序序列,…… ,直到得到一个长度为 n 的有序序列。

说明:归并排序每趟产生的有序区只是局部有序的,也就是说在最后一趟排序结束前,所有元素并不一定归位了。

2. 归并排序实例

设待排序的表有 10 个元素,其关键字为 {18,2,20,34,12,32,6,16}。以下为归并排序的排序过程:

采用二路归并排序时,需要进行 3 趟归并排序,其过程如图5.1所示。第 1 趟将每两个各含有一个元素的子表归并成一个新表,如将 {18} 和 {2} 排好序变为 {2,18}。第 2 趟将每两个各含有两个元素的子表归并成一个新表,如将 {2,18} 和 {20,34} 归并为{2,18,20,34}。第 3 趟将每两个各含有 4 个元素的子表归并成一个新表,产生最终有序表。

img

3. 排序算法

public class MergeSort {
    /**
     * 自底向上的二路归并算法
     * @param Data
     */
    public static void mergeSort(int[] Data) {
        for(int i = 1; i < Data.length; i = 2*i) {    //进行log2n趟归并
            mergePass(Data , i , Data.length);
        }
    }
    /**
     * 对整个表进行一趟归并
     * @param Data
     * @param len    子表的长度
     * @param n        整个表的长度
     */
    private static void mergePass(int[] Data, int len, int n) {
        int  i = 0;
        for(i = 0; i + 2*len - 1 < n; i += 2*len) {        //归并len长的两个相邻子表
            merge(Data , i , i+len-1 , i+2*len-1);
        }
        if(i+len-1 < n) {                                //余下两个子表,后者长度小于len
            merge(Data , i , i+len-1 , n-1);            //归并这两个子表
        }
    }
    /**
     * 实现两个子表在同一个表中的一次归并
     * @param Data
     * @param low    第一个子表的开始
     * @param mid    第一个子表的结尾
     * @param high    第二个子表的结尾,第二个子表的开始为mid+1
     */
    private static void merge(int[] Data, int low, int mid, int high) {
        int[] num = new int[Data.length];    //暂存数组
        int i = low;    //第一个子表的开始下标
        int j = mid+1;    //第二个子表的开始下标
        int k = 0;        //暂存表的下标
        while( i <= mid && j <= high) {    //在第一个子表和第二个子表均为扫描完时循环
            if(Data[i] <= Data[j]) {    //将第一个子表中的元素放入暂存表num
                num[k] = Data[i];
                i++;
                k++;
            } else {                    //将第二个子表中的元素放入暂存表num
                num[k] = Data[j];
                j++;
                k++;
            }
        }
        while(i <= mid) {                //将第一个子表剩余部分复制到暂存表num
            num[k] = Data[i];
            i++;
            k++;
        }
        while(j <= high) {                //将第二个子表剩余部分复制到暂存表num
            num[k] = Data[j];
            j++;
            k++;
        }
        for(k = 0 , i = low; i <= high; k++ , i++) {    //将暂存表复制到源表中
            Data[i] = num[k];
        }
    }

    public static void main(String[] args) {
        int[] Data = {18,2,20,34,12,32,6,16};

        mergeSort(Data);
        System.out.println("归并排序的结果:");
        for(int i = 0; i < Data.length; i++) {
            System.out.print(Data[i]+" ");
        }
    }
}

4. 算法分析

时间复杂度:O(nlog2n);

空间复杂度:O(n);

稳定性:稳定;

复杂性:较复杂。

6 基数排序

1. 排序思路#

基数排序有两种:最低位优先(LSD)和最高位优先(MSD)。最低位优先的过程是:先按最低位的值对元素进行排序,在此基础上,再按次低位进行排序,以此类推,由低位向高位,每趟都是根据关键字的一位并在前一趟的基础上对所有元素进行排序,直至最高位。

说明:基数排序每趟并不产生有序区,也就是说在最后一趟排序结束前,所有的元素并不一定归位了。

2. 基数排序实例

设待排序的表有 10 个元素,其关键字为 {18,2,20,34,12,32,6,16}。以下为堆排序的排序过程:

先按个位数进行排序,再按十位数进行排序。

img]

3. 排序算法

public class RadixSort {
    public static void radixSort(int[] Data){
        int[][] gather = new int[10][Data.length];    //存放某位上数相同值的那一些数,如15 ,25
        int[] sameNumSize = new int[10];               //存放某位相同值的那一些数的个数
        int some=0;        //总的收集次数
        int sub = 0;      //数组Data的下标
        int n=1;          //取低位上的值时的除数,如取个数时n=1,取十位时n=10

        while(some < maxSize(Data)) {
            for(int i = 0; i < Data.length; i++) {    //从个位开始分配
                int remain = (Data[i] / n) % 10;    //取某位上的数
                gather[remain][sameNumSize[remain]] = Data[i];    //将某位上数相同值的那一些数存放在二维数组gather中
                sameNumSize[remain]++;                //相同值的那一些数的个数加1
            }

            for(int i = 0; i < 10; i++) {    //将分配结束的数收集到源数组中
                if(sameNumSize[i] != 0) {
                    for(int j = 0; j < sameNumSize[i]; j++) {
                        Data[sub] = gather[i][j];
                        sub++;
                    }
                    sameNumSize[i] = 0;        //置空,以便下次分配时使用
                }
            }
            sub = 0;    //下标置0
            n *= 10;      //取次低位
            some++;      //收集次数加1
        }

    }
    /**
     * 求数组中最大数的长度
     * @param arr
     * @return 最大长度
     */
    public static int maxSize(int[] arr) {
        int temp = arr[0];
        for(int i = 1;i < arr.length; i++) {
            if(temp < arr[i]) {
                temp = arr[i];
            }
        }
        return String.valueOf(temp).length();
    }

    public static void main(String[] args) {
        int[] Data = {75,23,98,44,57,12,29,64,38,82};

        radixSort(Data);
        System.out.println("基数排序的结果:");
        for(int i = 0; i < Data.length; i++) {
            System.out.print(Data[i]+" ");
        }
    }
}

4. 算法分析

时间复杂度:O(d(n+r));

空间复杂度:O®;

稳定性:稳定;

复杂性:较复杂。

7 各种内排序方法的比较和选择

1. 各种排序方法的性能比较

img]

图7.1 各种排序方法的性能比较

2. 选择排序方法应综合考虑下列因素:

(1)待排序的元素数目 n(问题规模);

(2)元素的大小(每个元素的规模);

(3)关键字的结构及其初始状态;

(4)对稳定性的要求;

(5)语言工具的条件;

(6)存储结构;

(7)时间和空间复杂度。

3. 总结

(1)若 n 较小(如 n <= 50),可采用直接插入和直接选择排序;

(2)若文件初始状态基本有序(指正序),则应选用直接插入、冒泡或随机的快速排序;

(3)若 n 较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序;

(4)若要将两个有序表组合成一个新的有序表,最好的方法是归并排序方法;

(5)若 n 很大,元素的关键字位数较少且可以分解时,采用基数排序较好。

原文地址:https://www.cnblogs.com/cao-lei/p/6742815.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/24671.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

免费查题接口

免费查题接口 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点击跳转&a…

NodeJs实战-待办列表(7)-connect组件简化代码

NodeJs实战-待办列表7-connect组件简化代码什么是connectconnect demo 程序conenct 应用到服务端验证添加完成什么是connect connect demo 程序 安装 conncet、connect-query 组件 npm install connect npm install connect-query编写 demo 程序&#xff0c;保存到 test_conn…

阿里高工内产的 SpringBoot 实战派手册仅发布一天霸榜Github

近年来&#xff0c;Spring Boot 是整个Java社区中最有影响力的项目之一&#xff0c;常常被人看作是Java EE( Java Platform Enterprise Edition )开发的颠覆者&#xff0c;它将逐渐替代传统SSM ( Java EE互联网轻量级框架整合开发——Spring MvCSpringMyBatis&#xff09;架构。…

低代码平台的核心价值与优势

数字化时代的到来&#xff0c;迫使企业跳出舒适圈&#xff0c;坚定地踏上数字化转型的征程。不断飙升的用户需求&#xff0c;加上专业开发人员的显著缺口&#xff0c;让我们不得不承认&#xff0c;过去几十年的应用开发方式已经无法满足需求。低代码革命已经悄然开始&#xff0…

智慧防汛解决方案-最新全套文件

智慧防汛解决方案-最新全套文件一、建设背景行业痛点&#xff1a;1、家底不清&#xff0c;责权不分2、状态不明难以监管3、内外业脱节4、主观防涝二、建设思路面临的挑战&#xff1a;三、建设方案四、获取 - 智慧防汛全套最新解决方案合集一、建设背景 随着城市的快速发展&…

数据库的一级、二级、三级封锁协议

0、内容补充 X锁&#xff08;排他锁、写锁&#xff09; S锁&#xff08;共享锁、读锁&#xff09; 一、一级封锁协议 一级封锁协议是指&#xff0c;事务T在修改数据R之前必须先对其加X锁&#xff0c;直到事务结束才释放。事务结束包括正常结束&#xff08;COMMIT&#xff09;…

Kotlin Flow啊,你将流向何方?

前言 前边一系列的协程文章铺垫了很久&#xff0c;终于要分析Flow了。如果说协程是Kotlin的精华&#xff0c;那么Flow就是协程的精髓。 通过本篇文章&#xff0c;你将了解到&#xff1a; 什么是流&#xff1f;为什么引进Flow?Fow常见的操作为什么说Flow是冷流? 1. 什么是流 …

你必须要了解的国产数据库——OceanBase

文章目录前言1、什么是OceanBase&#xff1f;2、OceanBase名字的由来3、OceanBase 发展历程4、OceanBase优势5、OceanBase的核心特性6、应用场景7、未来展望前言 一直以来&#xff0c;外国企业在数据库领域保持高市占率&#xff0c;主流的数据库系统大多数都是使用外国的产品。…

读文章笔记-OSSFS-DD

读文章笔记-OSSFS-DD文章代码文章主要贡献内容公式(5)(6)的详细推倒对比其他7种算法算法思考文章 Online Scalable Streaming Feature Selection via Dynamic Decision 代码 https://github.com/doodzhou/OSFS 文章主要贡献 将特征分成选择&#xff0c;丢弃和延迟。提出基…

《第一行代码》核心知识点:Android的脸面之UI控件

《第一行代码》核心知识点&#xff1a;Android的脸面之UI控件前言三、Android的脸面之UI控件3.1 常用控件介绍3.1.1 TextView3.1.2 Button3.1.3 EditText3.1.4 ImageView3.1.5 ProgressBar3.1.6 AlertDialog3.1.7 ProgressDialog3.2 四种基本布局介绍3.2.1 线性布局3.2.2 相对布…

更强悍 更智能!飞凌嵌入式FET3588-C核心板震撼发布!

为提升AIoT领域终端设备的处理能力以及智能化程度&#xff0c;飞凌嵌入式与瑞芯微强强联手&#xff0c;正式推出FET3588-C核心板&#xff01; 1、国芯之光 品质之选 FET3588-C核心板基于Rockchip新一代旗舰RK3588处理器开发设计&#xff0c;采用先进的8nm制程工艺&#xff0c; …

【软考软件评测师】第三十一章 操作系统综合知识

【软考软件评测师】第三十一章 操作系统综合知识 第三十一章 操作系统综合知识【软考软件评测师】第三十一章 操作系统综合知识第一部分 知识点集锦1.嵌入式操作系统1&#xff09;可裁剪性2&#xff09;实时性3&#xff09;统一的接口4&#xff09;弱交互性5&#xff09;更好的…

EasyExcel使用详解与源码解读

EasyExcel使用详解1、EasyExcel简单介绍64M内存20秒读取75M(46W行25列)的Excel&#xff08;3.0.2版本&#xff09;2、EasyExcel和POI数据处理能力对比3、使用EasyExcel读写Excel、web上传/下载3.1、写EXCEL3.2、读EXCEL3.3、web上传、下载4、EasyExcel源码解读5、总结1、EasyEx…

AdsPower VS Dolphin,哪款指纹浏览器性价比更高?

现在指纹浏览器已经成为了跨境人必备的账号安全工具&#xff0c;市面上的指纹浏览器也非常多&#xff0c;但其实真正专业的寥寥无几。现在市场上比较出名的浏览器有AdsPower、Dolphin、multilogin。为了比较出AdsPower和Dolphin功能上的差异&#xff0c;龙哥特地去下载使用了这…

三、nginx设置浏览器缓存[expires]

一、浏览器缓存 解释&#xff1a;浏览器缓存极大缓解了带宽压力&#xff0c;提高了用户体验。 访问页面-查看是否有缓存&#xff0c;如果没有过期&#xff0c;直接击中缓存&#xff0c;不向服务器发送请求访问页面-查看是否有缓存&#xff0c;如果过期&#xff0c;向服务器发送…

如何给async await批量添加try…catch?

如何给async await批量添加try…catch&#xff1f;async和await是什么&#xff1f;如何给async await批量添加try…catchbabel插件的实现思路插件的基本格式示例寻找await节点向上查找 async 函数添加用户选项babel插件的安装使用其他参考资料async和await是什么&#xff1f; …

深紫色粉末BHQ-1 NHS,916753-61-2,NHS修饰是合成后与一个伯氨基的共轭

英文名称&#xff1a;BHQ-1 NHS CAS&#xff1a;916753-61-2 外观&#xff1a;深紫色粉末 分子式&#xff1a;C30H31N7O7 分子量&#xff1a;601.62 储存条件&#xff1a;-20C&#xff0c;避光避湿 结构式&#xff1a; 凯新生物产品简介&#xff1a; NHS修饰是合成后与一…

程序员必知必会网络传输之TCP/IP协议族,共864页的详解文档让你原地起飞!

我们现实网络无处不在&#xff0c;我们被庞大的虚拟网络包围&#xff0c;但我们却对它是怎样把我们的信息传递并实现通信的&#xff0c;我们并没有了解过&#xff0c;那么当我们在浏览器中出入一段地址&#xff0c;按下回车这背后都会发生什么&#xff1f; 比如说一般场景下&a…

ubuntu18.04安装QT5

ubuntu18.04安装QT5 1、说明 QT界面本不应该做为一个很高的知识点&#xff0c;问题是&#xff0c;越来越多的程序实验&#xff0c;需要界面支持&#xff0c;或用界面显得更加方便&#xff0c;因而专门启动该栏目专门介绍QT方法。因为体系比较庞大&#xff0c;因此&#xff0c;需…

在腾讯云 TKE 上部署 EMQX MQTT 服务器集群

云进入以「应用为中心」的云原生阶段&#xff0c;Operator 模式的出现&#xff0c;为 Kubernetes 中的自动化任务创建配置与管理提供了一套行之有效的标准规范。针对大规模分布式物联网 MQTT 消息服务器 EMQX 全生命期管理的自动化管理工具 EMQX Kubernetes Operator&#xff0…