数据结构之排序

試著忘記壹切 提交于 2020-03-22 06:37:29

这篇文章主要讨论常见的排序算法。

  排序算法分为内部排序和外部排序两种,内部排序是指只利用内存来完成的排序,外部排序是指借助外部存储设备完成的排序。外部排序主要针对记录比较多、内存无法一次全部加载的情况。我们这里主要关注内部排序。

  内部排序大致分为四类:1)插入排序;2)交换排序;3)选择排序;4)归并排序;5)基数排序。我们下面会分别进行描述。

  首先,我们来确定排序的对象,这里我们假设排序的对象是随机生成的非重复整数数组,有下面的辅助方法:

 
 1 public static int[] createArray(int count, int max)
 2 {
 3     if (count < 1) return null;
 4     int[] arrResult = new int[count];
 5     java.util.Random r = new java.util.Random();
 6     for(int i = 0; i < arrResult.length; i++)
 7     {
 8         int temp = 0;
 9         while(true)
10         {
11             temp = r.nextInt(max);
12             int j = 0;
13             for (j = 0; j < i; j++)
14             {
15                 if (arrResult[j] == temp) break;
16             }
17             if (j == i) break;
18         }
19         arrResult[i] = temp;
20     }
21     
22     return arrResult;
23 }
24 
25 private static void printArray(int[] array)
26 {
27     if (array == null)
28     {
29         return;
30     }
31     
32     StringBuffer sb = new StringBuffer();
33     for(int i = 0; i < array.length; i++)
34     {
35         sb.append(array[i]).append("->");
36     }
37     System.out.println(sb.substring(0, sb.length() - 2));
38 }
 

  接下来,我们分别讨论上述几种排序算法。

  1) 插入排序。它的基本思想是将一个元素插入到一个已排序的序列中,构成一个更大的序列。直接插入排序和希尔排序(shell)都属于插入排序。

 
 1 public static void insertSort(int[] arrValue)
 2 {
 3     if (arrValue == null || arrValue.length < 2) return;
 4     printArray(arrValue);
 5     for (int i = 1; i < arrValue.length; i++)
 6     {
 7         int temp = arrValue[i];
 8         int j = i;
 9         for (j = i; j > 0; j--)
10         {
11             if (arrValue[j - 1] > temp)
12             {
13                 arrValue[j] = arrValue[j - 1];
14             }
15             else
16             {
17                 break;
18             }
19         }
20         arrValue[j] = temp;
21         printArray(arrValue);    
22     }
23 }
 

   运行结果

 
22->32->2->46->9->29->20->45->3->26
22->32->2->46->9->29->20->45->3->26
2->22->32->46->9->29->20->45->3->26
2->22->32->46->9->29->20->45->3->26
2->9->22->32->46->29->20->45->3->26
2->9->22->29->32->46->20->45->3->26
2->9->20->22->29->32->46->45->3->26
2->9->20->22->29->32->45->46->3->26
2->3->9->20->22->29->32->45->46->26
2->3->9->20->22->26->29->32->45->46
 
 
 1 public static void shellSort(int[] arrValue)
 2 {
 3     if (arrValue == null || arrValue.length < 2) return;
 4     int length = arrValue.length/2;
 5     printArray(arrValue);
 6     while(length >= 1)
 7     {
 8         shell(arrValue, length);
 9         length = length/2;
10     }
11 }
12 
13 private static void shell(int[] arrValue, int d)
14 {
15     for(int i = d; i < arrValue.length; i++)
16     {
17         if (arrValue[i] < arrValue[i -d])
18         {
19             int temp = arrValue[i];
20             int j = i;
21             while(j >= d)
22             {
23                 if (arrValue[j-d] > temp) 
24                 {
25                     arrValue[j] = arrValue[j - d];
26                     j = j - d;
27                 }
28                 else
29                 {
30                     break;
31                 }                
32             }
33             arrValue[j] = temp;
34         }
35         printArray(arrValue);
36     }
37 }
 

  运行结果

14->21->1->26->6->40->5->46->9->25
14->5->1->9->6->40->21->46->26->25
1->5->6->9->14->25->21->40->26->46
1->5->6->9->14->21->25->26->40->46

  2) 交换排序。它的基本思想是遍历待排序序列,不停交换元素,最终得到一个排序的序列。冒泡排序和快速排序都属于交换排序。

 
 1 public static void bubbleSort(int[] arrValue)
 2 {
 3     if (arrValue == null || arrValue.length < 2) return;
 4     printArray(arrValue);
 5     for(int i = 0; i < arrValue.length; i++)
 6     {
 7         for(int j = i+ 1; j < arrValue.length; j++)
 8         {
 9             if (arrValue[i] > arrValue[j])
10             {
11                 int temp = arrValue[i];
12                 arrValue[i] = arrValue[j];
13                 arrValue[j] = temp;
14             }
15         }
16         printArray(arrValue);
17     }
18 }
 

   运行结果

 
35->2->19->37->43->47->39->34->21->0
0->35->19->37->43->47->39->34->21->2
0->2->35->37->43->47->39->34->21->19
0->2->19->37->43->47->39->35->34->21
0->2->19->21->43->47->39->37->35->34
0->2->19->21->34->47->43->39->37->35
0->2->19->21->34->35->47->43->39->37
0->2->19->21->34->35->37->47->43->39
0->2->19->21->34->35->37->39->47->43
0->2->19->21->34->35->37->39->43->47
0->2->19->21->34->35->37->39->43->47
 
 
 1 public static void quickSort(int[] arrValue, int left, int right)
 2 {
 3     if(left < right)
 4     {
 5         int i = division(arrValue, left, right);
 6         quickSort(arrValue, left, i - 1);
 7         quickSort(arrValue, i + 1, right);
 8     }
 9 }
10 
11 private static int division(int[] arrValue, int left, int right)
12 {
13     int baseValue = arrValue[left];
14     int midPos = left;
15     printArray(arrValue);
16     for (int i = left + 1; i <= right; i++)
17     {
18         if(arrValue[i] < baseValue) midPos++;
19     }
20     
21     if (midPos == left)
22     {
23         return midPos;
24     }
25     
26     arrValue[left] = arrValue[midPos]; 
27     arrValue[midPos] = baseValue;
28     
29     if (midPos == right)
30     {
31         return midPos;
32     }
33     for (int i = left; i < midPos; i++)
34     {
35         if (arrValue[i] > baseValue)
36         {
37             for (int j = right; j > midPos; j--)
38             {
39                 if (arrValue[j] < baseValue)
40                 {
41                     int temp = arrValue[i];
42                     arrValue[i] = arrValue[j];
43                     arrValue[j] = temp;
44                     right--;
45                     break;
46                 }
47             }
48         }
49     }
50             
51     return midPos;
52 }
 

  运行结果

 
14->5->36->17->34->2->47->7->22->42
7->5->2->14->34->36->47->17->22->42
2->5->7->14->34->36->47->17->22->42
2->5->7->14->34->36->47->17->22->42
2->5->7->14->22->17->34->36->47->42
2->5->7->14->17->22->34->36->47->42
2->5->7->14->17->22->34->36->47->42
 

  3)选择排序。它的基本思想是每次都从子序列中取得最小或者最大的元素。简单选择排序和堆排序都属于选择排序。

 
 1 public static void selectSort(int[] arrValue)
 2 {
 3     if (arrValue == null || arrValue.length < 2) return;
 4     printArray(arrValue);
 5     for (int i = 0; i < arrValue.length; i++)
 6     {
 7         int minValue = arrValue[i];
 8         int minIndex = i;
 9         for (int j = i; j < arrValue.length; j++)
10         {
11             if (arrValue[j] < minValue) 
12             {
13                 minIndex = j;
14                 minValue = arrValue[j]; 
15             }
16         }
17         if (i != minIndex)
18         {
19             int temp = arrValue[i];
20             arrValue[i] = arrValue[minIndex];
21             arrValue[minIndex] = temp;
22         }
23         printArray(arrValue);
24     }
25 }
 

   运行结果

 
43->28->29->31->37->32->27->36->12->3
3->28->29->31->37->32->27->36->12->43
3->12->29->31->37->32->27->36->28->43
3->12->27->31->37->32->29->36->28->43
3->12->27->28->37->32->29->36->31->43
3->12->27->28->29->32->37->36->31->43
3->12->27->28->29->31->37->36->32->43
3->12->27->28->29->31->32->36->37->43
3->12->27->28->29->31->32->36->37->43
3->12->27->28->29->31->32->36->37->43
3->12->27->28->29->31->32->36->37->43
 
 
 1 public static void heapSort(int[] arrValue)
 2 {
 3     if (arrValue == null || arrValue.length < 2) return;
 4     printArray(arrValue);
 5     for (int i = arrValue.length/2 - 1; i>=0; i--)
 6     {
 7         heapAdjust(arrValue, i, arrValue.length);
 8     }
 9     printArray(arrValue);
10     for (int i = arrValue.length - 1; i > 0; i--)
11     {
12         int temp = arrValue[0];
13         arrValue[0] = arrValue[i];
14         arrValue[i] = temp;
15                     
16         heapAdjust(arrValue, 0, i);
17         printArray(arrValue);
18     }
19     
20 }
21 
22 private static void heapAdjust(int[] arrValue, int parent, int length)
23 {
24     int child = 2* parent + 1;
25     int temp = arrValue[parent];
26     while(child < length)
27     {
28         if (child + 1 < length && arrValue[child] < arrValue[child + 1])
29         {
30             child = child + 1;
31         }
32         if (temp > arrValue[child])
33         {
34             break;
35         }
36         arrValue[parent] = arrValue[child];
37         
38         parent = child;
39         child = parent * 2 + 1;
40         
41     }
42     arrValue[parent] = temp;
43 }
 

  运行结果

 
4->26->29->7->30->2->19->42->13->46
46->42->29->13->30->2->19->7->4->26
42->30->29->13->26->2->19->7->4->46
30->26->29->13->4->2->19->7->42->46
29->26->19->13->4->2->7->30->42->46
26->13->19->7->4->2->29->30->42->46
19->13->2->7->4->26->29->30->42->46
13->7->2->4->19->26->29->30->42->46
7->4->2->13->19->26->29->30->42->46
4->2->7->13->19->26->29->30->42->46
2->4->7->13->19->26->29->30->42->46
 

   4)归并排序。它的基本思想是递归和分治。

 
 1 public static void mergeSort(int[] arrValue, int start, int end)
 2 {
 3     if (arrValue == null || arrValue.length < 2) return;
 4     if (start + 1 < end)
 5     {
 6         int mid = (start + end)/2;
 7         mergeSort(arrValue, start, mid);
 8         mergeSort(arrValue, mid, end);
 9         merge(arrValue, start, mid, end);
10         printArray(arrValue);
11     }
12 
13 }
14 
15 private static void merge(int[] arrValue, int start, int mid, int end)
16 {
17     int[] temp = new int[end - start];
18     
19     int index1 = start;
20     int index2 = mid;
21     int index = 0;
22     while(index1 < mid && index2 < end)
23     {
24         if (arrValue[index1] < arrValue[index2])
25         {
26             temp[index] = arrValue[index1];
27             index1++;
28         }
29         else
30         {
31             temp[index] = arrValue[index2];
32             index2++;
33         }
34         index++;
35     }
36     
37     if (index1 < mid)
38     {
39         while(index1 < mid)
40         {
41             temp[index] = arrValue[index1];
42             index1++;
43             index++;
44         }
45     }
46     
47     if (index2 < end)
48     {
49         while(index2 < mid)
50         {
51             temp[index] = arrValue[index2];
52             index2++;
53             index++;
54         }
55     }
56     
57     for (int i = 0; i < index; i++)
58     {
59         arrValue[start + i] = temp[i];
60     }
61 }
 

  运行结果

 
41->49->9->31->23->0->25->2->6->7
41->49->9->23->31->0->25->2->6->7
41->49->9->23->31->0->25->2->6->7
9->23->31->41->49->0->25->2->6->7
9->23->31->41->49->0->25->2->6->7
9->23->31->41->49->0->25->2->6->7
9->23->31->41->49->0->25->2->6->7
9->23->31->41->49->0->2->6->7->25
0->2->6->7->9->23->25->31->41->49
 

   5)基数排序。基数排序的思想和桶排序类似,它首先按照个位数值将序列放入到0-9共10个桶中,然后依次输出,接着,对新的序列按照十位数值再次放入桶中,然后再输出,以此类推,直到所有树的位数都遍历完毕,就可以得到排好序的序列。

 
 1 public static void radixSort(int[] arrValue)
 2 {
 3     if (arrValue == null || arrValue.length < 2) return;
 4     
 5     HashMap<Integer, List<Integer>> map = new HashMap<Integer, List<Integer>>();
 6     int base = 10;
 7     printArray(arrValue);
 8     while(isNeedContinue(arrValue, base))
 9     {
10         map.clear();
11         for (int i = 0; i < arrValue.length; i++)
12         {
13             int key = arrValue[i]%base/(base/10);
14             if (!map.containsKey(key))
15             {
16                 map.put(key, new ArrayList<Integer>());
17             }
18             map.get(key).add(arrValue[i]);
19         }
20         for (int i = 0, j = 0; i < 10; i++)
21         {
22             if (map.containsKey(i))
23             {
24                 for(Integer value : map.get(i))
25                 {
26                     arrValue[j] = value;
27                     j++;
28                 }
29             }
30         }
31         base = base*10;
32         printArray(arrValue);
33     }
34 }
35 
36 private static boolean isNeedContinue(int[] arrValue, int base)
37 {
38     for(int i = 0; i < arrValue.length; i++)
39     {
40         if (base/10 <= arrValue[i]) return true;
41     }
42     return false;
43 }
 

  运行结果

38->16->48->27->45->44->3->22->14->42
22->42->3->44->14->45->16->27->38->48
3->14->16->22->27->38->42->44->45->48

  6)其他排序   也有一些其他排序算法比较巧妙,例如计数排序,它对于不重复序列排序来说,有时是一个很好的选择,它会首先计算序列的最小值和最大值,创建一个flag数组,数组长度为最大值和最小值之差,然后遍历序列,更新flag数组,最后遍历flag数组,输出排序结果。

 
 1 public static void smartSort(int[] arrValue)
 2 {
 3     if (arrValue == null || arrValue.length < 2) return;
 4     int min = arrValue[0];
 5     int max = arrValue[0];
 6     printArray(arrValue);
 7     for (int i = 1; i < arrValue.length; i++)
 8     {
 9         if (arrValue[i] < min) min = arrValue[i];
10         if (arrValue[i] > max) max = arrValue[i];
11     }
12     int[] arrFlag = new int[max - min + 1];
13     for (int i = 0; i < arrFlag.length; i++) arrFlag[i] = 0;
14     printArray(arrFlag);
15     for (int i = 0; i < arrValue.length; i++)
16     {
17         arrFlag[arrValue[i] - min] = 1;
18     }
19     printArray(arrFlag);
20     for(int i = 0, j = 0; i < arrFlag.length; i++)
21     {
22         if (arrFlag[i] == 1)
23         {
24             arrValue[j] = i + min;
25             j++;
26         }
27     }
28     printArray(arrValue);
29 }
 

   运行结果

2->32->35->0->38->26->21->10->22->5
0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0->0
1->0->1->0->0->1->0->0->0->0->1->0->0->0->0->0->0->0->0->0->0->1->1->0->0->0->1->0->0->0->0->0->1->0->0->1->0->0->1
0->2->5->10->21->22->26->32->35->38

  下面我们来总结分析上述各排序算法

排序算法 平均时间复杂度 最优时间复杂度 最坏时间复杂度 空间复杂度 稳定性
直接插入排序 O(n*n) O(n) O(n*n) O(1) 稳定
希尔(shell)排序       O(1) 不稳定
冒泡排序 O(n*n) O(n) O(n*n) O(1) 稳定
快速排序 O(n*logn) O(n*logn) O(n*logn) O(n*logn) 不稳定
简单选择排序 O(n*n) O(n) O(n*n) O(1) 不稳定
堆排序 O(n*logn) O(n*logn) O(n*logn) O(1) 不稳定
归并排序 O(n*logn) O(n*logn) O(n*logn) O(n) 稳定
基数排序 O(d(n+radix)) O(d(n+radix)) O(d(n+radix)) O(radix) 稳定
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!