常用排序算法二

206 阅读10分钟

五、直接选择排序

  1、基本介绍

  选择排序(select sorting)也是一种简单的排序方法。它的基本思想是:第一次从arr[0]~arr[n-1]中选取最小值,与arr[0]交换,第二次从arr[1]~arr[n-1]中选取最小值,与arr[1]交换,第三次从arr[2]~arr[n-1]中选取最小值,与arr[2]交换,…,第i次从arr[i-1]~arr[n-1]中选取最小值,与arr[i-1]交换,…, 第n-1次从arr[n-2]~arr[n-1]中选取最小值,与arr[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列。

  

  

  2、代码实现

  

 1  //O(n2)
 2     public static void selectSort(int[] arr){
 3         for (int i = 0; i < arr.length-1; i++) {
 4             //假定最小值为i
 5             int minIndex = i;
 6             //j从i+1开始
 7             for (int j = i+1; j <arr.length ; j++) {
 8                 //如果假定的最小值大于后面一个元素即arr[j],则吧minIndex指向arr[j]
 9                 //否则不进入if判断
10                 if(arr[minIndex]>arr[j]){
11                     minIndex = j;
12                 }
13             }
14             //如果minIndex没有变化,即,minIndex就是arr[i],不需交换
15             if(minIndex!=i) {
16                 int tmp = arr[i];
17                 arr[i] = arr[minIndex];
18                 arr[minIndex] = tmp;
19             }
20         }
21     }

 

 

六、堆排序

  1、堆排序基本介绍

  1)堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。
  2)堆是具有以下性质的完全二叉树每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆, 注意 : 没有要求结点的左孩子的值和右孩子的值的大小关系。
  3)每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆
  4)大顶堆举例

  

  我们对堆中的结点按层进行编号,映射到数组中就是下面这个样子:

  

  大顶堆的特点:

  arr[i] >= arr[2*i+1] && arr[i] >= arr[2*i+2]  // i 对应第几个节点,i从0开始编号

  5)小顶堆举例

  

  小顶堆的特点:

  arr[i] <= arr[2*i+1] && arr[i] <= arr[2*i+2] // i 对应第几个节点,i从0开始编号

  6)一般升序采用大顶堆,降序采用小顶堆

  2、堆排序的基本思路

  1)将待排序序列构造成一个大顶堆
  2)此时,整个序列的最大值就是堆顶的根节点。
  3)将其与末尾元素进行交换,此时末尾就为最大值。
  4)然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

  1、基本介绍 

  

  

  

  2、代码实现

   

 1 public class HeapSort {
 2 
 3     public static void  heapSort(int[] arr) {
 4         int temp= 0;
 5 //        int arr[] = {4, 6, 8, 5, 9};
 6         //将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆
 7         for (int i = arr.length/2-1; i >= 0; i--) {
 8             adjustHeap(arr,i,arr.length);
 9         }
10         //System.out.println(Arrays.toString(arr));
11         //2).将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
12         //3).重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
13         for (int j = arr.length-1; j >0 ; j--) {
14             temp = arr[j];
15             arr[j] = arr[0];
16             arr[0] = temp;
17             adjustHeap(arr,0,j);
18         }
19         //System.out.println(Arrays.toString(arr));
20     }
21 
22     //将一个数组(二叉树), 调整成一个大顶堆
23     /**
24      * 功能: 完成 将 以 i 对应的非叶子结点的树调整成大顶堆
25      * 举例  int arr[] = {4, 6, 8, 5, 9}; => i = 1 => adjustHeap => 得到 {4, 9, 8, 5, 6}
26      * 如果我们再次调用  adjustHeap 传入的是 i = 0 => 得到 {4, 9, 8, 5, 6} => {9,6,8,5, 4}
27      * @param arr 待调整的数组
28      * @param i 表示非叶子结点在数组中索引
29      * @param lenght 表示对多少个元素继续调整, length 是在逐渐的减少
30      */
31     private static void adjustHeap(int arr[], int i,int length){
32         //先取出当前元素的值,保存在临时变量
33         int temp = arr[i];
34         //开始调整
35         //说明
36         //1. k = i * 2 + 1 k 是 i结点的左子结点
37         for (int k = i*2+1; k <length; k=i*2+1) {
38             //说明左子结点的值小于右子结点的值
39             if(k+1<length && arr[k]<arr[k+1]){
40                 // k 指向右子结点
41                 k++;
42             }
43             //如果子结点大于父结点
44             if(arr[k]>temp){
45                 //把较大的值赋给当前结点
46                 arr[i] = arr[k];
47                 //!!! i 指向 k,继续循环比较
48                 i=k;
49             }else{
50                 break;
51             }
52         }
53         //当for 循环结束后,我们已经将以i 为父结点的树的最大值,放在了 最顶(局部)
54         arr[i] = temp;//将temp值放到调整后的位置
55     }
56 }

 

七、归并排序

  1、基本介绍 

  归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)

  策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
  

 

  

  

  2、代码实现

   

 1 public class MergeSort {
 2     public static void sort(int[] arr, int left, int right, int[] temp) {
 3         if (left < right) {
 4             int mid = (left + right) / 2;
 5             //向左递归进行分解
 6             sort(arr, left, mid, temp);
 7             //向右递归进行分解
 8             sort(arr, mid + 1, right, temp);
 9             //合并
10             merge(arr, left, mid, right, temp);
11         }
12     }
13 
14     /**
15      * @param arr   排序的原始数组
16      * @param left  左边有序序列的初始索引
17      * @param mid   中间索引
18      * @param right 右边索引
19      * @param temp  做中转的数组
20      */
21     private static void merge(int[] arr, int left, int mid, int right, int[] temp) {
22         int i = left; // 初始化i, 左边有序序列的初始索引
23         int j = mid + 1; //初始化j, 右边有序序列的初始索引
24         int t = 0; // 指向temp数组的当前索引
25         //(一)
26         //先把左右两边(有序)的数据按照规则填充到temp数组
27         //直到左右两边的有序序列,有一边处理完毕为止
28         while (i <= mid && j <= right) {
29             //如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素
30             //即将左边的当前元素,填充到 temp数组
31             //然后 t++, i++
32             if (arr[i] <= arr[j]) {
33                 temp[t++] = arr[i++];
34             } else {
35                 temp[t++] = arr[j++];
36             }
37         }
38 
39         //(二)
40         //把有剩余数据的一边的数据依次全部填充到temp
41         while (i <= mid) {//左边的有序序列还有剩余的元素,就全部填充到temp
42             temp[t++] = arr[i++];
43         }
44         while (j <= right) {//右边的有序序列还有剩余的元素,就全部填充到temp
45             temp[t++] = arr[j++];
46         }
47 
48         //(三)
49         //将temp数组的元素拷贝到arr
50         //注意,并不是每次都拷贝所有
51         t=0;
52         int tempLeft=left;
53         //第一次合并 tempLeft = 0 , right = 1 //  tempLeft = 2  right = 3
54         //最后一次 tempLeft = 0  right = 7
55         while (tempLeft<=right){
56             arr[tempLeft++]=temp[t++];
57         }
58     }
59 }

 

八、基数排序

  1、基数排序(桶排序)介绍

 

  1)基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用

  2)基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法

  3)基数排序(Radix Sort)是桶排序的扩展

  4)基数排序是1887年赫尔曼·何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较

 

    

    

  2、基数排序基本思想

  1)将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。

   然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。


  2)这样说明,比较难理解,下面我们看一个图文解释,理解基数排序的步骤
  3、基数排序图文说明

  将数组 {53, 3, 542, 748, 14, 214} 使用基数排序, 进行升序排序

  

  

  

  4、基数排序说明

  1)基数排序是对传统桶排序的扩展,速度很快.
  2)基数排序是经典的空间换时间的方式,占用内存很大, 当对海量数据排序时,容易造成 OutOfMemoryError 。(本人电脑1亿随机数排序会出现,1千万不会)
  3)基数排序时稳定的。

  [注:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的]
  4)有负数的数组,我们不用基数排序来进行排序, 如果要支持负数,参考: https://code.i-harness.com/zh-CN/q/e98fa9


  2、代码实现

  

 1 public class RadixSort {
 2     public static void sort(int[] arr){
 3         //1. 得到数组中最大的数的位数
 4         int max = arr[0]; //假设第一数就是最大数
 5         for (int i = 1; i <arr.length ; i++) {
 6             if(arr[i]>max){
 7                 max=arr[i];
 8             }
 9         }
10         //得到最大数是几位数
11         int maxlength =(max+"").length();
12         //定义一个二维数组,表示10个桶, 每个桶就是一个一维数组
13         //说明
14         //1. 二维数组包含10个一维数组
15         //2. 为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.length
16         //3. 名明确,基数排序是使用空间换时间的经典算法
17         int[][] bucket = new int[10][arr.length];
18         //为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数
19         //可以这里理解
20         //比如:bucketElementCounts[0] , 记录的就是  bucket[0] 桶的放入数据个数
21         int[] bucketElementCounts = new int[10];
22         for (int i = 0,n=1; i <maxlength ; i++,n*=10) {
23             //(针对每个元素的对应位进行排序处理), 第一次是个位,第二次是十位,第三次是百位..
24             for (int j = 0; j <arr.length ; j++) {
25                 int digitOfElement=arr[j]/n%10;
26                 bucket[digitOfElement][bucketElementCounts[digitOfElement]]=arr[j];
27                 bucketElementCounts[digitOfElement]++;
28             }
29             //按照这个桶的顺序(一维数组的下标依次取出数据,放入原来数组)
30             int index =0;
31             //遍历每一桶,并将桶中是数据,放入到原数组
32             for (int k = 0; k < bucketElementCounts.length; k++) {
33                 //如果桶中,有数据,我们才放入到原数组
34                 if(bucketElementCounts[k]!=0){
35                     //循环该桶即第k个桶(即第k个一维数组), 放入
36                     for (int l = 0; l < bucketElementCounts[k]; l++) {
37                         arr[index++]=bucket[k][l];
38                     }
39                 }
40                 //第i+1轮处理后,需要将每个 bucketElementCounts[k] = 0 !!!!
41                 bucketElementCounts[k]=0;
42             }
43            // System.out.println(Arrays.toString(arr));
44         }
45 
46     }
47 }

 

 

 

 

 

九、桶排序

  1、基本介绍 

  

  2、代码实现

十、计数排序

  1、基本介绍

  

  2、代码实现