8大排序

Posted wylwyl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了8大排序相关的知识,希望对你有一定的参考价值。

技术图片

技术图片

 

 

 

一、直接插入排序

往有序的数组中快速插入一个新的元素。

基本思想:将数组中的所有元素依次跟前面已经排好的元素相比较,如果选择的元素比已排序的元素小,则交换,直到全部元素都比较过为止。

 1 // 插入排序
 2 public static void insertSort(int[] arr) {
 3     if(arr == null || arr.length <= 0)  {
 4         return;
 5     }
 6 
 7     for(int i = 1; i < arr.length; i++) {
 8         int tmp = arr[i];
 9         
10         for(int j = i; j >= 0; j--) {
11             if(j > 0 && arr[j-1] > tmp) {
12                 arr[j] = arr[j-1];
13             } else {
14                 arr[j] = tmp;
15                 break;
16             }
17         }
18     }
19 }

技术图片

 

 二、希尔排序

希尔排序又称递减增量排序算法。希尔排序是先把待排序的记录序列分割成若干子序列,分别进行直接插入排序,待整个序列中基本有序时,再对全体记录进行直接插入排序。

①. 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;(一般初次取数组半长,之后每次再减半,直到增量为1
②. 按增量序列个数k,对序列进行k 趟排序;
③. 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

希尔排序实质上是一种分组插入方法。它的基本思想是:对于n个待排序的数列,取一个小于n的整数gap(gap被称为步长)将待排序元素分成若干个组子序列,所有距离为gap的倍数的记录放在同一个组中;然后,对各组内的元素进行直接插入排序。 这一趟排序完成之后,每一个组的元素都是有序的。然后减小gap的值,并重复执行上述的分组和排序。重复这样的操作,当gap=1时,整个数列就是有序的。

下面以数列{80,30,60,40,20,10,50,70}为例,演示它的希尔排序过程

第1趟:(gap=4)

技术图片

当gap=4时,意味着将数列分为4个组: {80,20},{30,10},{60,50},{40,70}。 对应数列: {80,30,60,40,20,10,50,70}
对这4个组分别进行排序,排序结果: {20,80},{10,30},{50,60},{40,70}。 对应数列: {20,10,50,40,80,30,60,70}

第2趟:(gap=2)

技术图片

当gap=2时,意味着将数列分为2个组:{20,50,80,60}, {10,40,30,70}。 对应数列: {20,10,50,40,80,30,60,70}
注意:{20,50,80,60}实际上有两个有序的数列{20,80}和{50,60}组成。
          {10,40,30,70}实际上有两个有序的数列{10,30}和{40,70}组成
对这2个组分别进行排序,排序结果:{20,50,60,80}, {10,30,40,70}。 对应数列: {20,10,50,30,60,40,80,70}

第3趟:(gap=1)

技术图片

当gap=1时,意味着将数列分为1个组:{20,10,50,30,60,40,80,70}
注意:{20,10,50,30,60,40,80,70}实际上有两个有序的数列{20,50,60,80}和{10,30,40,70}组成。
对这1个组分别进行排序,排序结果:{10,20,30,40,50,60,70,80}

 

 1 public static void shellSort(int[] arr){
 2     int gap = arr.length / 2;
 3 
 4     for (; gap > 0; gap /= 2) {      //不断缩小gap,直到1为止
 5 
 6         for (int j = 0; (j+gap) < arr.length; j++){     //使用当前gap进行组内插入排序
 7 
 8             for(int k = 0; (k+gap)< arr.length; k += gap){
 9                 if(arr[k] > arr[k+gap]) {
10                     
11                     int temp = arr[k+gap];      //交换操作
12                     arr[k+gap] = arr[k];
13                     arr[k] = temp;
14                 }
15             }
16         }
17     }
18 }

技术图片

 

选择排序

选择排序的基本思想:比较+交换

①、从待排序的序列中,找到关键字最小的元素;

②、如果最小的元素不是待排序序列的第一个元素,将其和第一个元素交换;

③、从余下的N-1个元素中,找出关键字最小的元素,重复1和2,直到排序结束

 1 // 选择排序
 2 public static void selectionSort(int[] arr) {
 3     if(arr == null || arr.length < 0) {
 4         return;
 5     }
 6 
 7     for(int i = 0; i < arr.length; i++) {
 8         int tmp = arr[i]; // 当前最小值
 9         int flag = i;
10 
11         for(int j = i + 1; j < arr.length; j++) {
12             if(arr[j] < tmp) {
13                 tmp = arr[j];
14                 flag = j;
15             }
16         }
17 
18         if(flag != i) {
19             arr[flag] = arr[i];
20             arr[i] = tmp;
21         }
22     }
23 }

技术图片

 

 

堆排序

堆排序的过程就是将待排序的序列构成一个堆,选出堆中的最大元素移走,再把剩余的元素调整成堆,找出最大的再移走,重复直到有序。

 

 

快速排序

基本思想:挖坑填数+分治法

①. 从数列中挑出一个元素,称为”基准”(pivot)。
②. 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
③. 递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。

 

 1 public static void quickSort(int[] arr, int low, int high) {
 2     if(arr == null || arr.length <= 0) {
 3         return;
 4     }
 5 
 6     int left = low;
 7     int right = high;
 8     int tmp = arr[left];
 9 
10     while(left < right) {
11         while(left < right && arr[right] >= tmp) {
12             right--;
13         }
14 
15         arr[left] = arr[right];
16 
17         while(left < right && arr[left] <= tmp) {
18             left++;
19         }
20         
21         arr[right] = arr[left];
22     }
23 
24     arr[left] = tmp;
25     quickSort(arr, low, left-1);
26     quickSort(arr, left+1, high);
27 }

技术图片

 

归并排序

基本思想:归并排序算法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

 1 public class Solution {
 2     public static void mergeSort(int[] nums) {
 3         int[] tmp = new int[nums.length];
 4 
 5         mergeSort(nums, tmp, 0, nums.length-1);
 6     }
 7 
 8     private static void mergeSort(int[] nums, int[] tmp, int left, int right) {
 9         if(left < right) {
10             int mid = (left + right) >> 1;
11             mergeSort(nums, tmp, left, mid);
12             mergeSort(nums, tmp, mid+1, right);
13             merge(nums, tmp, left, mid+1, right);
14         }
15     }
16 
17     private static void merge(int[] nums, int[] tmp, int leftPos, int rightPos, int rightEnd) {
18         int leftEnd = rightPos-1;
19         int tmpPos = leftPos;
20         int numElements = rightEnd-leftPos+1;
21 
22         while(leftPos <= leftEnd && rightPos <= rightEnd) {
23             if(nums[leftPos] <= nums[rightPos]) {
24                 tmp[tmpPos++] = nums[leftPos++];
25             } else {
26                 tmp[tmpPos++] = nums[rightPos++];
27             }
28         }
29 
30         while(leftPos <= leftEnd) {
31             tmp[tmpPos++] = nums[leftPos++];
32         }
33 
34         while(rightPos <= rightEnd) {
35             tmp[tmpPos++] = nums[rightPos++];
36         }
37 
38         for(int i = 0; i < numElements; i++, rightEnd--) {
39             nums[rightEnd] = tmp[rightEnd];
40         }
41     }
42 }

技术图片

 

基数排序

它是这样实现的:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

基数排序按照优先从高位或低位来排序有两种实现方案:

  • MSD(Most significant digital) 从最左侧高位开始进行排序。先按k1排序分组, 同一组中记录, 关键码k1相等, 再对各组按k2排序分成子组, 之后, 对后面的关键码继续这样的排序分组, 直到按最次位关键码kd对各子组排序后. 再将各组连接起来, 便得到一个有序序列。MSD方式适用于位数多的序列

  • LSD (Least significant digital)从最右侧低位开始进行排序。先从kd开始排序,再对kd-1进行排序,依次重复,直到对k1排序后便得到一个有序序列。LSD方式适用于位数少的序列

 1 public static void radixSort(int[] arr){
 2     if(arr.length <= 1) return;
 3 
 4     //取得数组中的最大数,并取得位数
 5     int max = 0;
 6     for(int i = 0; i < arr.length; i++){
 7         if(max < arr[i]){
 8             max = arr[i];
 9         }
10     }
11 
12     // 最大数的位数
13     int maxDigit = 1;
14     while(max / 10 > 0){
15         maxDigit++;
16         max = max / 10;
17     }
18 
19     //申请一个桶空间
20     int[][] buckets = new int[10][arr.length];
21 
22     int base = 10;
23 
24     //从低位到高位,对每一位遍历,将所有元素分配到桶中
25     for(int i = 0; i < maxDigit; i++){
26 
27         int[] bktLen = new int[10];//存储各个桶中存储元素的数量
28         
29         //分配:将所有元素分配到桶中
30         for(int j = 0; j < arr.length; j++){
31             int whichBucket = (arr[j] % base) / (base / 10);
32             buckets[whichBucket][bktLen[whichBucket]] = arr[j];
33             bktLen[whichBucket]++;
34         }
35 
36         //收集:将不同桶里数据挨个捞出来,为下一轮高位排序做准备,由于靠近桶底的元素排名靠前,因此从桶底先捞
37         int k = 0;
38         for(int b = 0; b < buckets.length; b++){
39 
40             for(int p = 0; p < bktLen[b]; p++){
41                 // important here
42                 arr[k++] = buckets[b][p];
43             }
44         }
45 
46         base *= 10;
47     }
48 }

技术图片

 

参考:

https://www.cnblogs.com/skywang12345/p/3597597.html

https://blog.csdn.net/hguisu/article/details/7776068

以上是关于8大排序的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段13——Vue的状态大管家

VSCode自定义代码片段13——Vue的状态大管家

VSCode自定义代码片段13——Vue的状态大管家

VSCode自定义代码片段8——声明函数

归并排序

8大排序算法图文讲解