写在前面的话:本篇文章是我自己阅读程杰老师的大话数据结构之后自己所做的总结,网上有很多关于排序的详细介绍,我推荐伍迷家园所写的文章,介绍的非常详细。
排序是我们在程序中经常要用到的一种算法,好的排序可以极大的提高我们的工作效率,本篇主要介绍几种常见的排序算法;
(简单说明:下面的java程序是用来排序的,其中sum为排序数组,sum[0]不进行排序,只作为哨兵或者临时变量)
1、冒泡排序:冒泡排序是一种交换排序,其的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。
1 //冒泡排序2 最正宗的冒泡排序,i每循环一次,最小的数就像气泡一样慢慢浮到水面上
2 public static void BubbleSort_2(int[] num){
3 for(int i = 1; i < num.length; i++){
4 for(int j = num.length-1; j > i; j--){
5 if(num[j-1] > num[j]){
6 Swap(num,j-1,j); //交换函数 交换num[j-1]和num[j]的值
7 }
8 }
9 }
10 }
2、选择排序:简单选择排序法(Simple Selection Sort)就是通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1≤i≤n)个记录交换之。
1 //选择排序
2 public static void SelectSort(int[] num){
3 int min;
4 for(int i = 1; i < num.length; i++){
5 min = i;
6 for(int j = i + 1; j <num.length; j++){
7 if(num[min] > num[j]){
8 min = j;
9 }
10 }
11 if(i != min){
12 Swap(num,i,min); //交换函数 交换num[j-1]和num[j]的值
13 }
14 }
15 }
3、插入排序:直接插入排序(Straight Insertion Sort)的基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表。
1 //插入排序
2 public static void InsertSort(int[] num){
3 int j;
4 for(int i = 2; i < num.length; i++){
5 if(num[i] < num[i-1]){
6 num[0] = num[i];
7 for( j = i-1; num[j] > num[0]; j--){
8 num[j+1] = num[j]; //记录右移
9 }
10 num[j+1] = num[0]; //插入到正确位置
11 num[0] = 0; //把num[0] 置为0,还回到初始值
12 }
13 }
14 }
4、希尔排序:我们将原本有大量记录数的记录进行分组。分割成若干个子序列,此时每个子序列待排序的记录个数就比较少了,然后在这些子序列内分别进行直接插入排序,当整个序列都基本有序时,注意只是基本有序时,再对全体记录进行一次直接插入排序。
1 //希尔排序
2 public static void ShellSort(int[] num){
3 int n = num.length;
4 int j;
5 do{
6 n = n/3 + 1; //增量序列
7 for(int i = n; i < num.length; i++){
8 if(num[i] < num[i-n]){ //需要将num[i]插入到有序增量子表中
9 num[0] = num[i];
10 for(j = i - n; j > 0 && num[0] < num[j];j -= n){
11 num[j+n] = num[j];
12 }
13 num[j+n] = num[0];
14 }
15 }
16 }while(n > 1);
17 }
5、堆排序:堆排序(Heap Sort)就是利用堆(假设利用大顶堆)进行排序的方法。它的基本思想是,将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根结点。将它与堆数组的末尾元素进行交换,此时末尾元素就是最大值,然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次小值。如此反复执行,便能得到一个有序序列了。(大顶堆:在完全二叉树中,每个结点的值都大于或等于其左右孩子结点的值)
1 //堆排序算法 堆排序的时候一定要注意数组的下标,很容发生数组下标越界的情况
2 public static void HeadSort(int[] num){
3 for(int i = (num.length-1)/2; i>0; i--){ //将num构建成一个大顶堆
4 HeapAdjust(num,i,num.length-1);
5 }
6 for(int i = num.length-1; i > 1; i--){ //排序num[3]~num[n] 其中数组开始两个元素num[1]和num[2]没有进行交换
7 Swap(num,1,i); //交换函数 交换num[j-1]和num[j]的值
8 HeapAdjust(num,1,i-1);
9 }
10 }
11 /*本函数是为了将num调整为一个大顶堆,num除了num【1】以外,
12 其他节点都符合大顶堆的定义,这个函数是为堆排序算法服务的*/
13 public static void HeapAdjust(int[] num,int start,int stop){
14 int i, num_x;
15 num_x = num[start];
16 for(i = 2*start; i <= stop; i*=2){ //沿关键字较大的纪录项下筛选
17 if(i+1 >= num.length){ //没有右孩子节点时,单独判断,否则num[i+1]会越界
18 if(num[i/2] < num[i]){
19 num[i/2] = num[i];
20 start = i;
21 break;
22 }else{
23 break;
24 }
25 }
26 if(num[i] < num[i+1] && i < stop){
27 ++i; //i为关键字较大的记录的下标
28 }
29 if(num_x >= num[i]){ //不在向下找了,num_x已经是最大的了
30 break;
31 }
32 num[start] = num[i];
33 start = i;
34 }
35 num[start] = num_x;
36 }
6、归并排序:归并排序(Merging Sort)就是利用归并的思想实现的排序方法。它的原理是假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归 并,得到⌈n/2⌉(⌈x⌉表示不小于x的最小整数)个长度为2的有序子序列;再两两归并,…… 如此重复,直至得到一个长度为n的有序序列为止,这 种排序方法称为2路归并排序。
1 //归并排序
2 public static void MergeSort(int[] num){
3 int[] temp =new int[num.length];
4 MSort(num,temp ,1,num.length-1);
5 }
6 //Msort函数是将list数组进行分割,Merge函数是将分割后的list数组进行排序在组合
7 public static void MSort(int[] list ,int[] temp,int left,int right){
8 if(left == right){
9 return;
10 }else{
11 int m = (left + right) / 2;
12 MSort(list,temp,left,m);
13 MSort(list,temp,m+1,right);
14 Merge(list,temp,left,m+1,right);
15 }
16 }
17 public static void Merge(int[] list,int[] temp,int left,int mid,int right){
18 int j = 0;
19 int left_x = left;
20 int mid_x = mid -1;
21 int n = right - left +1; //当前temp数组中数的个数
22 while(left <= mid_x && mid <= right){ //左右数组第一个数进行比较 把较小的数如到temp数组中
23 if(list[left] < list[mid]){
24 temp[j++] = list[left++];
25 }else{
26 temp[j++] = list[mid++];
27 }
28 }
29 while(left <= mid_x){ //如果左边的数比右边的数多,就将剩下的入到temp数组中 j
30 temp[j++] = list[left++];
31 }
32
33 while(mid <= right){ //如果右边打数比左边的数多,就将右边剩下的数加入到temp数组当中去
34 temp[j++] = list[mid++];
35 }
36
37 for(j = 0; j < n; j++){ // 将得到的temp数组加到list数组中
38 list[left_x+j] = temp[j];
39 }
40 }
7、快速排序:快速排序(Quick Sort)的基本思想是通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
1 //快速排序
2 public static void QuickSort(int[] num){
3 QSort(num,1,num.length-1);
4 }
5 public static void QSort(int[] num, int left, int right){
6 int mid = Partition(num,left,right);
7
8 QSort(num,left,mid-1);
9 QSort(num,mid+1,right);
10 }
11 //返回一个关键字,使得这个关键字左边的数都比它小,右边的数都比他大
12 public static int Partition(int[] num, int left, int right){
13 int result;
14 result = num[left];
15 while(left < right){
16 while( left < right && num[right] >= result){
17 right++;
18 }
19 Swap(num,left,right);
20 while( left < right && num[left] <= result){
21 left++;
22 }
23 Swap(num,left,right);
24 }
25 return left;
26 }
8、快速排序优化
1 //快速排序优化
2 public static void QuickSort_Op(int[] num){
3 Qsort_Op(num,1,num.length-1);
4 }
5 public static void Qsort_Op(int[] num, int left, int right){
6 final int N = 7; //数组长度的阀值 当数组长度低于这个长度是就不在使用快速排序,而是使用插入排序
7 if((right-left) > N){
8 //尾递归优化
9 while(left < right){
10 int mid = Partition_Op(num,left,right);
11
12 Qsort_Op(num, left, mid-1); //对底子表进行递归排序
13 left = mid; //尾递归
14 }
15 }else{ //当right-left小于7时,用直接插入排序
16 InsertSort(num);
17 }
18 }
19 //返回一个关键字,使得这个关键字左边的数都比它小,右边的数都比他大
20 public static int Partition_Op(int[] num, int left, int right){
21 int result;
22
23 //优化选取轴承 三数取中(取左端、右端,中间三个),进行排序,取中间数作为轴承
24 int m = left + (right - left);
25 if(num[left] > num[right]){
26 Swap(num,left,right);
27 }
28 if(num[m] > num[right]){
29 Swap(num,m,right);
30 }
31 if(num[left] < num[m]){
32 Swap(num,left,m);
33 }
34 result = num[left]; //y用子表的第一个记录作为轴承记录
35 num[0] = result; //将轴承关键字备份到num[0]
36 while( left < right ){
37 while(left < right && num[right] > result){
38 right--;
39 }
40 num[left] = num[right]; //选择替换而不是交换方式进行
41 while(left < right && num[left] < result){
42 left++;
43 }
44 num[right] = num[left]; //选择替换而不是交换方式进行操作
45 }
46 num[left] = num[0];
47 return left;
48 }
下面代码包含了上述所有排序算法,是一段总结代码:
1 /**
2 * 2016/5/3
3 * 排序算法大总结
4 * 说明:本java程序是用来排序的,其中sum为排序数组,sum[0]不进行排序,只作为哨兵或者临时变量
5 */
6 package cn.Link;
7 public class Sort{
8 //测试主函数
9 public static void main(String[] args){
10 int n = 20; //数组长度
11 int[] num = new int[n]; //要排序的数组
12 num[0] = 0; //数组首位不进行排序操作,仅作为临时变量或者是哨兵
13 for(int i = 1; i < n; i ++){
14 num[i] = (int)(Math.random()*100); //随机生成一个数作为数组元素
15 }
16
17 System.out.println("排序前的数组:");
18 Print_num(num);
19
20 //冒泡排序1
21 int[] num_1 = (int[]) num.clone(); //将num数组全部复制给num_1数组
22 BubbleSort_1(num_1);
23 System.out.print("\n冒泡排序1排序后的结果");
24 Print_num(num_1);
25
26 //冒泡排序2
27 int[] num_2 = (int[]) num.clone(); //将num数组全部复制给num_2数组
28 BubbleSort_2(num_2);
29 System.out.print("\n冒泡排序2排序后的结果:");
30 Print_num(num_2);
31
32 //冒泡排序3 对冒泡排序2的优化
33 int[] num_3 = (int[]) num.clone(); //将num数组全部复制给num_3数组
34 BubbleSort_3(num_3);
35 System.out.print("\n冒泡排序3排序后的结果:");
36 Print_num(num_3);
37
38 //选择排序
39 int[] num_4 = (int[]) num.clone(); //将num数组全部复制给num_4数组
40 SelectSort(num_4);
41 System.out.print("\n选择排序排序后的结果:");
42 Print_num(num_4);
43
44 //插入排序
45 int[] num_5 = (int[])num.clone(); //将num数组全部复制给num_5数组
46 InsertSort(num_5);
47 System.out.print("\n插入排序排序后的结果");
48 Print_num(num_5);
49
50 //希尔排序
51 int[] num_6 = (int[])num.clone(); //将num数组全部复制给num_6数组
52 InsertSort(num_6);
53 System.out.print("\n希尔排序排序后的结果");
54 Print_num(num_6);
55
56 //堆排序
57 int[] num_7 = (int[])num.clone(); //将num数组全部复制给num_7数组
58 HeadSort(num_7);
59 System.out.print("\n堆排序排序后的结果");
60 Print_num(num_7);
61
62 //堆排序
63 int[] num_8 = (int[])num.clone(); //将num数组全部复制给num_8数组
64 MergeSort(num_8);
65 System.out.print("\n归并排序排序后的结果");
66 Print_num(num_8);
67
68 //快速排序
69 int[] num_9 = (int[])num.clone(); //将num数组全部复制给num_8数组
70 MergeSort(num_9);
71 System.out.print("\n快速排序排序后的结果");
72 Print_num(num_9);
73
74 //快速排序优化
75 int[] num_10 = (int[])num.clone(); //将num数组全部复制给num_8数组
76 MergeSort(num_10);
77 System.out.print("\n快速排序优化后排序后结果");
78 Print_num(num_10);
79
80 }
81
82 //交换数组中的两个数
83 public static void Swap(int[]num,int i,int j){
84 int num_x;
85 num_x = num[i];
86 num[i] = num[j];
87 num[j] = num_x;
88 }
89
90 //遍历输出一个数组
91 public static void Print_num(int[] num){
92 System.out.println();
93 for(int u:num){ //for-each
94 System.out.print(u+" ");
95 }
96 }
97
98 //冒泡排序1 最简单的两两比较,反序则交换的排序
99 public static void BubbleSort_1(int[]num){
100 for(int i = 1; i < num.length; i++){
101 for(int j = i+1; j < num.length; j++){
102 if(num[i] > num[j]){
103 Swap(num,i,j);
104 }
105 }
106 }
107 }
108
109 //冒泡排序2 最正宗的冒泡排序,i每循环一次,最小的数就像气泡一样慢慢浮到水面上
110 public static void BubbleSort_2(int[] num){
111 for(int i = 1; i < num.length; i++){
112 for(int j = num.length-1; j > i; j--){
113 if(num[j-1] > num[j]){
114 Swap(num,j-1,j); //交换函数 交换num[j-1]和num[j]的值
115 }
116 }
117 }
118 }
119
120 //冒泡排序3 对冒泡排序的优化
121 public static void BubbleSort_3(int[] num){
122 boolean flag = true;
123 for(int i = 1; i < num.length && flag; i++){
124 flag = false; //flag初始化为假
125 for(int j = num.length-1; j>i ; j--){
126 if(num[j-1] > num[j]){
127 Swap(num,j-1,j); //交换函数 交换num[j-1]和num[j]的值
128 flag = true;
129 }
130 }
131 }
132 }
133
134 //选择排序
135 public static void SelectSort(int[] num){
136 int min;
137 for(int i = 1; i < num.length; i++){
138 min = i;
139 for(int j = i + 1; j <num.length; j++){
140 if(num[min] > num[j]){
141 min = j;
142 }
143 }
144 if(i != min){
145 Swap(num,i,min); //交换函数 交换num[j-1]和num[j]的值
146 }
147 }
148 }
149
150 //插入排序
151 public static void InsertSort(int[] num){
152 int j;
153 for(int i = 2; i < num.length; i++){
154 if(num[i] < num[i-1]){
155 num[0] = num[i];
156 for( j = i-1; num[j] > num[0]; j--){
157 num[j+1] = num[j]; //记录右移
158 }
159 num[j+1] = num[0]; //插入到正确位置
160 num[0] = 0; //把num[0] 置为0,还回到初始值
161 }
162 }
163 }
164 //希尔排序
165 public static void ShellSort(int[] num){
166 int n = num.length;
167 int j;
168 do{
169 n = n/3 + 1; //增量序列
170 for(int i = n; i < num.length; i++){
171 if(num[i] < num[i-n]){ //需要将num[i]插入到有序增量子表中
172 num[0] = num[i];
173 for(j = i - n; j > 0 && num[0] < num[j];j -= n){
174 num[j+n] = num[j];
175 }
176 num[j+n] = num[0];
177 }
178 }
179 }while(n > 1);
180 }
181
182 //堆排序算法 堆排序的时候一定要注意数组的下标,很容发生数组下标越界的情况
183 public static void HeadSort(int[] num){
184 for(int i = (num.length-1)/2; i>0; i--){ //将num构建成一个大顶堆
185 HeapAdjust(num,i,num.length-1);
186 }
187 for(int i = num.length-1; i > 1; i--){ //排序num[3]~num[n] 其中数组开始两个元素num[1]和num[2]没有进行交换
188 Swap(num,1,i); //交换函数 交换num[j-1]和num[j]的值
189 HeapAdjust(num,1,i-1);
190 }
191 }
192 /*本函数是为了将num调整为一个大顶堆,num除了num【1】以外,
193 其他节点都符合大顶堆的定义,这个函数是为堆排序算法服务的*/
194 public static void HeapAdjust(int[] num,int start,int stop){
195 int i, num_x;
196 num_x = num[start];
197 for(i = 2*start; i <= stop; i*=2){ //沿关键字较大的纪录项下筛选
198 if(i+1 >= num.length){ //没有右孩子节点时,单独判断,否则num[i+1]会越界
199 if(num[i/2] < num[i]){
200 num[i/2] = num[i];
201 start = i;
202 break;
203 }else{
204 break;
205 }
206 }
207 if(num[i] < num[i+1] && i < stop){
208 ++i; //i为关键字较大的记录的下标
209 }
210 if(num_x >= num[i]){ //不在向下找了,num_x已经是最大的了
211 break;
212 }
213 num[start] = num[i];
214 start = i;
215 }
216 num[start] = num_x;
217 }
218
219
220 //归并排序
221 public static void MergeSort(int[] num){
222 int[] temp =new int[num.length];
223 MSort(num,temp ,1,num.length-1);
224 }
225 //Msort函数是将list数组进行分割,Merge函数是将分割后的list数组进行排序在组合
226 public static void MSort(int[] list ,int[] temp,int left,int right){
227 if(left == right){
228 return;
229 }else{
230 int m = (left + right) / 2;
231 MSort(list,temp,left,m);
232 MSort(list,temp,m+1,right);
233 Merge(list,temp,left,m+1,right);
234 }
235 }
236 public static void Merge(int[] list,int[] temp,int left,int mid,int right){
237 int j = 0;
238 int left_x = left;
239 int mid_x = mid -1;
240 int n = right - left +1; //当前temp数组中数的个数
241 while(left <= mid_x && mid <= right){ //左右数组第一个数进行比较 把较小的数如到temp数组中
242 if(list[left] < list[mid]){
243 temp[j++] = list[left++];
244 }else{
245 temp[j++] = list[mid++];
246 }
247 }
248 while(left <= mid_x){ //如果左边的数比右边的数多,就将剩下的入到temp数组中 j
249 temp[j++] = list[left++];
250 }
251
252 while(mid <= right){ //如果右边打数比左边的数多,就将右边剩下的数加入到temp数组当中去
253 temp[j++] = list[mid++];
254 }
255
256 for(j = 0; j < n; j++){ // 将得到的temp数组加到list数组中
257 list[left_x+j] = temp[j];
258 }
259 }
260
261 //快速排序
262 public static void QuickSort(int[] num){
263 QSort(num,1,num.length-1);
264 }
265 public static void QSort(int[] num, int left, int right){
266 int mid = Partition(num,left,right);
267
268 QSort(num,left,mid-1);
269 QSort(num,mid+1,right);
270 }
271 //返回一个关键字,使得这个关键字左边的数都比它小,右边的数都比他大
272 public static int Partition(int[] num, int left, int right){
273 int result;
274 result = num[left];
275 while(left < right){
276 while( left < right && num[right] > result){
277 right++;
278 }
279 Swap(num,left,right);
280 while( left < right && num[left] < result){
281 left++;
282 }
283 Swap(num,left,right);
284 }
285 return left;
286 }
287
288 //快速排序优化
289 public static void QuickSort_Op(int[] num){
290 Qsort_Op(num,1,num.length-1);
291 }
292 public static void Qsort_Op(int[] num, int left, int right){
293 final int N = 7; //数组长度的阀值 当数组长度低于这个长度是就不在使用快速排序,而是使用插入排序
294 if((right-left) > N){
295 //尾递归优化
296 while(left < right){
297 int mid = Partition_Op(num,left,right);
298
299 Qsort_Op(num, left, mid-1); //对底子表进行递归排序
300 left = mid; //尾递归
301 }
302 }else{ //当right-left小于7时,用直接插入排序
303 InsertSort(num);
304 }
305 }
306 //返回一个关键字,使得这个关键字左边的数都比它小,右边的数都比他大
307 public static int Partition_Op(int[] num, int left, int right){
308 int result;
309
310 //优化选取轴承 三数取中(取左端、右端,中间三个),进行排序,取中间数作为轴承
311 int m = left + (right - left);
312 if(num[left] > num[right]){
313 Swap(num,left,right);
314 }
315 if(num[m] > num[right]){
316 Swap(num,m,right);
317 }
318 if(num[left] < num[m]){
319 Swap(num,left,m);
320 }
321 result = num[left]; //y用子表的第一个记录作为轴承记录
322 num[0] = result; //将轴承关键字备份到num[0]
323 while( left < right ){
324 while(left < right && num[right] > result){
325 right--;
326 }
327 num[left] = num[right]; //选择替换而不是交换方式进行
328 while(left < right && num[left] < result){
329 left++;
330 }
331 num[right] = num[left]; //选择替换而不是交换方式进行操作
332 }
333 num[left] = num[0];
334 return left;
335 }
336
337 }