一、冒泡排序 [了解]
1.1 基本介绍
冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就像水底下的气泡一样逐渐向上冒。
优化:因为排序的过程中, 各元素不断接近自己的位置, 如果一趟比较下来没有进行过交换, 就说明序列有序, 因此要在排序过程中设置一个标志 flag 判断元素是否进行过交换。 从而减少不必要的比较。 (这里说的优化, 可以在冒泡排序写好后, 再进行)
1.2 思路
比较相邻的元素。如果第一个比第二个大,就交换它们两个;
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素就是最大的数;
排除最大的数,接着下一轮继续相同的操作,确定第二大的数…
重复步骤1-3,直到排序完成。
1.3 动画演示
淡蓝色表示还未进行排序,绿色表示正在进行比较排序,橙色表示已完成排序
1.4 代码示例
public class BubbleSort {
public static void main(String
[] args
) {
int[] arr
= {3,1,9,10,2};
bubbleSort(arr
);
}
public static void bubbleSort(int[] arr
) {
int temp
= 0;
boolean flag
= false;
for (int i
= 0; i
< arr
.length
- 1; i
++) {
for (int j
= 0; j
< arr
.length
- 1 - i
; j
++) {
if (arr
[j
] > arr
[j
+1]) {
flag
= true;
temp
= arr
[j
];
arr
[j
] = arr
[j
+1];
arr
[j
+1] = temp
;
}
}
System
.out
.println("第" + (i
+1) + "趟排序的结果");
System
.out
.println(Arrays
.toString(arr
));
if (!flag
) {
break;
} else {
flag
= false;
}
}
}
}
1.5 程序执行结果
第1趟排序的结果
[1, 3, 9, 2, 10]
第2趟排序的结果
[1, 3, 2, 9, 10]
第3趟排序的结果
[1, 2, 3, 9, 10]
第4趟排序的结果
[1, 2, 3, 9, 10]
平均时间复杂度:O(n²)
二、选择排序 [了解]
2.1 基本介绍
选择排序也属于内部排序法,是从预排序的数据中,按指定的规则选出某一个元素,再依规定交换位置后打到排序的目的。
2.2 思路
第一轮,找到最小的元素,和数组第一个数交换位置。
第二轮,找到第二小的元素,和数组第二个数交换位置…
直到最后一个元素,排序完成。
原始的数组:10,34,40,1
第一轮排序:1,[34,40,10] 下一轮排序在后面3个数中找到最小元素,进行比较交换…
第二轮排序:1,10,[40,34] 下一轮排序在后面2个数中找到最小元素,进行比较交换…
第三轮排序:1,10,34,40
完成排序!
2.3 动画演示
淡蓝色表示还未进行排序,红色表示两个值进行最小值比较,绿色表示正在查找最小值,橙色表示已找出最小值
2.4 代码示例
public static void selectSort2(int[] arr
) {
int minIndex
= 0;
int min
= arr
[0];
for (int j
= 1; j
< arr
.length
; j
++) {
if (min
> arr
[j
]) {
min
= arr
[j
];
minIndex
= j
;
}
}
if (minIndex
!= 0) {
arr
[minIndex
] = arr
[0];
arr
[0] = min
;
}
System
.out
.println("第1轮后~~");
System
.out
.println(Arrays
.toString(arr
));
minIndex
= 1;
min
= arr
[1];
for (int j
= 2; j
< arr
.length
; j
++) {
if (min
> arr
[j
]) {
min
= arr
[j
];
minIndex
= j
;
}
}
if (minIndex
!= 1) {
arr
[minIndex
] = arr
[1];
arr
[1] = min
;
}
System
.out
.println("第2轮后~~");
System
.out
.println(Arrays
.toString(arr
));
minIndex
= 2;
min
= arr
[2];
for (int j
= 3; j
< arr
.length
; j
++) {
if (min
> arr
[j
]) {
min
= arr
[j
];
minIndex
= j
;
}
}
if (minIndex
!= 2) {
arr
[minIndex
] = arr
[2];
arr
[2] = min
;
}
System
.out
.println("第3轮后~~");
System
.out
.println(Arrays
.toString(arr
));
}
------
第
1轮后
~~
[1, 34, 40, 10]
第
2轮后
~~
[1, 10, 40, 34]
第
3轮后
~~
[1, 10, 34, 40]
public class SelectSort {
public static void main(String
[] args
) {
int[] arr
= {10,34,40,1};
selectSort(arr
);
}
public static void selectSort(int[] arr
) {
for (int i
= 0; i
< arr
.length
- 1; i
++) {
int minIndex
= i
;
int min
= arr
[i
];
for (int j
= i
+ 1; j
< arr
.length
; j
++) {
if (min
> arr
[j
]) {
min
= arr
[j
];
minIndex
= j
;
}
}
if (minIndex
!= i
) {
arr
[minIndex
] = arr
[i
];
arr
[i
] = min
;
}
System
.out
.println("第" + (i
+1) + "轮交换的值");
System
.out
.println(Arrays
.toString(arr
));
}
}
}
2.5 程序执行结果
第1轮交换的值
[1, 34, 40, 10]
第2轮交换的值
[1, 10, 40, 34]
第3轮交换的值
[1, 10, 34, 40]
默认为从小到大排序,如要从大到小排序只需将 min > arr[j] 改为 min < arr[j] 即可
平均时间复杂度:O(n²)
三、插入排序 [熟悉]
3.1 基本介绍
插入式排序属于内部排序法,是对于预排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的。
3.2 思路
把 n 个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素, 无序表中包含有 n-1 个元素, 排序过程中每次从无序表中取出第一个元素, 把它的排序码依次与有序表元素的排序码进行比较, 将它插入到有序表中的适当位置, 使之成为新的有序表。
3.3 动画演示
淡蓝色表示还未进行排序,红色表示待插入的数,绿色表示正在进行排序,橙色表示已完成排序
3.4 代码示例
public static void insertSort2(int[] arr
) {
int insertVal
= arr
[1];
int insertIndex
= 0;
while (insertIndex
>= 0 && insertVal
< arr
[insertIndex
]) {
arr
[insertIndex
+ 1] = arr
[insertIndex
];
insertIndex
--;
}
arr
[insertIndex
+ 1] = insertVal
;
System
.out
.println("第1轮插入");
System
.out
.println(Arrays
.toString(arr
));
insertVal
= arr
[2];
insertIndex
= 1;
while (insertIndex
>= 0 && insertVal
< arr
[insertIndex
]) {
arr
[insertIndex
+ 1] = arr
[insertIndex
];
insertIndex
--;
}
arr
[insertIndex
+ 1] = insertVal
;
System
.out
.println("第2轮插入");
System
.out
.println(Arrays
.toString(arr
));
insertVal
= arr
[3];
insertIndex
= 2;
while (insertIndex
>= 0 && insertVal
< arr
[insertIndex
]) {
arr
[insertIndex
+ 1] = arr
[insertIndex
];
insertIndex
--;
}
arr
[insertIndex
+ 1] = insertVal
;
System
.out
.println("第3轮插入");
System
.out
.println(Arrays
.toString(arr
));
}
------
第
1轮插入
[10, 34, 40, 1]
第
2轮插入
[10, 34, 40, 1]
第
3轮插入
[1, 10, 34, 40]
arr[insertIndex + 1] = arr[insertIndex] : 作用:把大的元素放到后面 insertIndex-- :作用:是对有序表中的元素再进行大小的比对
public class InsertSort {
public static void main(String
[] args
) {
int[] arr
= {34,10,40,1};
insertSort(arr
);
}
public static void insertSort(int[] arr
) {
int insertVal
= 0;
int insertIndex
= 0;
for (int i
= 0; i
< arr
.length
- 1; i
++) {
insertVal
= arr
[i
+1];
insertIndex
= i
;
while (insertIndex
>= 0 && insertVal
< arr
[insertIndex
]) {
arr
[insertIndex
+ 1] = arr
[insertIndex
];
insertIndex
--;
}
arr
[insertIndex
+ 1] = insertVal
;
System
.out
.println("第" + (i
+1) + "轮插入");
System
.out
.println(Arrays
.toString(arr
));
}
}
}
3.5 程序执行结果
第1轮插入
[10, 34, 40, 1]
第2轮插入
[10, 34, 40, 1]
第3轮插入
[1, 10, 34, 40]
int[] arr
= {34,10,40,1,7,50};
第
1轮插入
[10, 34, 40, 1, 7, 50]
第
2轮插入
[10, 34, 40, 1, 7, 50]
第
3轮插入
[1, 10, 34, 40, 7, 50]
第
4轮插入
[1, 7, 10, 34, 40, 50]
第
5轮插入
[1, 7, 10, 34, 40, 50]
默认为从小到大排序,如要从大到小排序只需将 insertVal < arr[insertIndex] 改为 insertVal > arr[insertIndex] 即可
平均时间复杂度:O(n²)
四、希尔排序 [了解]
4.1 基本介绍
希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。
4.2 思路
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;
随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
也就是说,把数组分割成若干(h)个小组(一般数组长度length/2),然后对每一个小组分别进行插入排序。每一轮分割的数组的个数逐步缩小,h/2->h/4->h/8,并且进行排序,保证有序。当h=1时,则数组排序完成。
4.3 图解示例
4.4 代码示例
public static void shellSortExchange2(int[] arr
) {
int temp
= 0;
for (int i
= 5; i
< arr
.length
; i
++) {
for (int j
= i
- 5; j
>= 0; j
-= 5) {
if (arr
[j
] > arr
[j
+ 5]) {
temp
= arr
[j
];
arr
[j
] = arr
[j
+ 5];
arr
[j
+ 5] = temp
;
}
}
}
System
.out
.println("希尔排序第1轮=" + Arrays
.toString(arr
));
for (int i
= 2; i
< arr
.length
; i
++) {
for (int j
= i
- 2; j
>= 0; j
-= 2) {
if (arr
[j
] > arr
[j
+ 2]) {
temp
= arr
[j
];
arr
[j
] = arr
[j
+ 2];
arr
[j
+ 2] = temp
;
}
}
}
System
.out
.println("希尔排序第2轮=" + Arrays
.toString(arr
));
for (int i
= 1; i
< arr
.length
; i
++) {
for (int j
= i
- 1; j
>= 0; j
-= 1) {
if (arr
[j
] > arr
[j
+ 1]) {
temp
= arr
[j
];
arr
[j
] = arr
[j
+ 1];
arr
[j
+ 1] = temp
;
}
}
}
System
.out
.println("希尔排序第3轮=" + Arrays
.toString(arr
));
}
------
希尔排序第
1轮
=[3, 5, 1, 6, 0, 8, 9, 4, 7, 2]
希尔排序第
2轮
=[0, 2, 1, 4, 3, 5, 7, 6, 9, 8]
希尔排序第
3轮
=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
public class ShellSort {
public static void main(String
[] args
) {
int[] arr
= {8,9,1,7,2,3,5,4,6,0};
shellSortExchange2(arr
);
}
public static void shellSortDisplaced(int[] arr
) {
int count
= 0;
for (int gap
= arr
.length
/ 2; gap
> 0; gap
/= 2) {
for (int i
= gap
; i
< arr
.length
; i
++) {
int j
= i
;
int temp
= arr
[j
];
if (arr
[j
] < arr
[j
- gap
]) {
while (j
- gap
>= 0 && temp
< arr
[j
- gap
]) {
arr
[j
] = arr
[j
- gap
];
j
-= gap
;
}
arr
[j
] = temp
;
}
}
System
.out
.println("希尔排序第" + (++count
) + "轮=" + Arrays
.toString(arr
));
}
}
public static void shellSortExchange(int[] arr
) {
int temp
= 0;
int count
= 0;
for (int gap
= arr
.length
/ 2; gap
> 0; gap
/= 2) {
for (int i
= gap
; i
< arr
.length
; i
++) {
for (int j
= i
- gap
; j
>= 0; j
-= gap
) {
if (arr
[j
] > arr
[j
+ gap
]) {
temp
= arr
[j
];
arr
[j
] = arr
[j
+ gap
];
arr
[j
+ gap
] = temp
;
}
}
}
System
.out
.println("希尔排序第" + (++count
) + "轮=" + Arrays
.toString(arr
));
}
}
}
4.5 程序执行结果
希尔排序第1轮=[3, 5, 1, 6, 0, 8, 9, 4, 7, 2]
希尔排序第2轮=[0, 2, 1, 4, 3, 5, 7, 6, 9, 8]
希尔排序第3轮=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
int[] testArr
= new int[80000];
for (int i
= 0; i
< 80000; i
++) {
testArr
[i
] = (int) (Math
.random() * 80000);
}
long start
= System
.currentTimeMillis();
shellSortDisplaced(testArr
);
long end
= System
.currentTimeMillis();
System
.out
.println("移位式程序执行时间(ms):" + (end
- start
));
① 交换式程序执行时间(ms):6299 ② 6131ms ③ 6268ms
① 移位式程序执行时间(ms):27 ② 22ms ③ 22ms
五、快速排序 [重点]
5.1 基本介绍
快速排序(Quicksort)是对冒泡排序的一种改进。面试最喜欢问的排序算法。这是运用分治法的一种排序算法。
5.2 思路
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
5.3 动画演示
5.4 代码示例
public class QuickSort {
public static void main(String
[] args
) {
int[] arr
= {9,48,10,23,1,37};
quickSort(arr
, 0, arr
.length
- 1);
System
.out
.println("快速排序后:" + Arrays
.toString(arr
));
}
public static void quickSort(int[] arr
, int left
, int right
) {
int l
= left
;
int r
= right
;
int pivot
= arr
[(left
+ right
) / 2];
int temp
= 0;
while (l
< r
) {
while (arr
[l
] < pivot
) {
l
++;
}
while (arr
[r
] > pivot
) {
r
--;
}
if (l
>= r
) {
break;
}
temp
= arr
[l
];
arr
[l
] = arr
[r
];
arr
[r
] = temp
;
if (arr
[l
] == pivot
) {
r
--;
}
if (arr
[r
] == pivot
) {
l
++;
}
}
if (l
== r
) {
l
+= 1;
r
-= 1;
}
if (left
< r
) {
quickSort(arr
, left
, r
);
}
if (right
> l
) {
quickSort(arr
, l
, right
);
}
}
}
5.5 程序执行结果
快速排序后:[1, 9, 10, 23, 37, 48]
int[] testArr
= new int[80000];
for (int i
= 0; i
< 80000; i
++) {
testArr
[i
] = (int) (Math
.random() * 80000);
}
long start
= System
.currentTimeMillis();
quickSort(testArr
, 0, testArr
.length
- 1);
long end
= System
.currentTimeMillis();
System
.out
.println("快排耗时(ms):" + (end
- start
));
① 快排耗时(ms):36 ② 24ms ③ 55ms ④ 49ms ⑤ 51ms ⑥ 19ms ⑦ 33ms
六、归并排序 [重点]
6.1 基本介绍
归并排序(MERGE-SORT) 是利用归并的思想实现的排序方法, 该算法采用经典的分治(divide-and-conquer)策略
分治法将问题分(divide)成一些小的问题然后递归求解, 而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起, 即分而治之。
归并排序是采用分治法的典型应用,而且是一种稳定的排序方式,不过需要使用到额外的空间。
6.2 思路
把数组不断划分成子序列,划成长度只有2或者1的子序列。然后利用临时数组,对子序列进行排序,合并,再把临时数组的值复制回原数组。反复操作1~2步骤,直到排序完成。
6.3 动画演示
6.4 代码示例
public class MergerSort {
public static void main(String
[] args
) {
int[] arr
= {8,4,5,7,1,3,6,2};
int[] temp
= new int[arr
.length
];
mergeSort(arr
, 0, arr
.length
- 1, temp
);
System
.out
.println("归并排序后=" + Arrays
.toString(arr
));
}
public static void mergeSort(int[] arr
, int left
, int right
, int[] temp
) {
if (left
< right
) {
int mid
= (left
+ right
) / 2;
mergeSort(arr
, left
, mid
, temp
);
mergeSort(arr
, mid
+ 1, right
, temp
);
merger(arr
, left
, mid
, right
, temp
);
}
}
public static void merger(int[] arr
, int left
, int mid
, int right
, int[] temp
) {
int i
= left
;
int j
= mid
+ 1;
int t
= 0;
while (i
<= mid
&& j
<= right
) {
if (arr
[i
] <= arr
[j
]) {
temp
[t
] = arr
[i
];
t
+= 1;
i
+= 1;
} else {
temp
[t
] = arr
[j
];
t
+= 1;
j
+= 1;
}
}
while (i
<= mid
) {
temp
[t
] = arr
[i
];
t
+= 1;
i
+= 1;
}
while (j
<= right
) {
temp
[t
] = arr
[j
];
t
+= 1;
j
+= 1;
}
t
= 0;
int tempLeft
= left
;
while (tempLeft
<= right
) {
arr
[tempLeft
] = temp
[t
];
t
+= 1;
tempLeft
+= 1;
}
}
}
6.5 程序执行结果
归并排序后=[1, 2, 3, 4, 5, 6, 7, 8]
int[] testArr
= new int[80000];
int[] testTemp
= new int[testArr
.length
];
for (int i
= 0; i
< 80000; i
++) {
testArr
[i
] = (int) (Math
.random() * 80000);
}
long start
= System
.currentTimeMillis();
mergeSort(testArr
, 0, testArr
.length
- 1, testTemp
);
long end
= System
.currentTimeMillis();
System
.out
.println("归并排序耗时(ms):" + (end
- start
));
① 归并排序耗时(ms):20 ② 25ms ③ 22ms
七、基数排序 [了解]
7.1 基本介绍
1)基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用
2)基数排序法是属于稳定性的排序,基数排序法的效率高的稳定性排序法
3)基数排序(Radix sort)是桶排序的扩展
4)基数排序是1887年赫尔曼·何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较。
7.2 思路
将所有待比较数值统一为同样的数位长度, 数位较短的数前面补零。然后, 从最低位开始, 依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
7.3 动画演示
7.4 代码示例
public static void radixSort2(int[] arr
) {
int[][] bucket
= new int[10][arr
.length
];
int[] bucketElementCounts
= new int[10];
for(int j
= 0; j
< arr
.length
; j
++) {
int digitOfElement
= arr
[j
] % 10;
bucket
[digitOfElement
][bucketElementCounts
[digitOfElement
]] = arr
[j
];
bucketElementCounts
[digitOfElement
]++;
}
int index
= 0;
for(int k
= 0; k
< bucketElementCounts
.length
; k
++) {
if(bucketElementCounts
[k
] != 0) {
for(int l
= 0; l
< bucketElementCounts
[k
]; l
++) {
arr
[index
++] = bucket
[k
][l
];
}
}
bucketElementCounts
[k
] = 0;
}
System
.out
.println("第1轮,对个位的排序处理arr= " + Arrays
.toString(arr
));
for(int j
= 0; j
< arr
.length
; j
++) {
int digitOfElement
= arr
[j
] /10 % 10;
bucket
[digitOfElement
][bucketElementCounts
[digitOfElement
]] = arr
[j
];
bucketElementCounts
[digitOfElement
]++;
}
index
= 0;
for(int k
= 0; k
< bucketElementCounts
.length
; k
++) {
if(bucketElementCounts
[k
] != 0) {
for(int l
= 0; l
< bucketElementCounts
[k
]; l
++) {
arr
[index
++] = bucket
[k
][l
];
}
}
bucketElementCounts
[k
] = 0;
}
System
.out
.println("第2轮,对个位的排序处理arr= " + Arrays
.toString(arr
));
for(int j
= 0; j
< arr
.length
; j
++) {
int digitOfElement
= arr
[j
] /100 % 10;
bucket
[digitOfElement
][bucketElementCounts
[digitOfElement
]] = arr
[j
];
bucketElementCounts
[digitOfElement
]++;
}
index
= 0;
for(int k
= 0; k
< bucketElementCounts
.length
; k
++) {
if(bucketElementCounts
[k
] != 0) {
for(int l
= 0; l
< bucketElementCounts
[k
]; l
++) {
arr
[index
++] = bucket
[k
][l
];
}
}
bucketElementCounts
[k
] = 0;
}
System
.out
.println("第3轮,对个位的排序处理arr= " + Arrays
.toString(arr
));
}
------
第
1轮,对个位的排序处理arr
= [542, 53, 3, 14, 214, 748]
第
2轮,对个位的排序处理arr
= [3, 14, 214, 542, 748, 53]
第
3轮,对个位的排序处理arr
= [3, 14, 53, 214, 542, 748]
- 排序的轮数与数组中最大的位数有关
public class RadixSort {
public static void main(String
[] args
) {
int[] arr
= {53,3,542,748,14,214};
radixSort(arr
);
}
public static void radixSort(int[] arr
) {
int max
= arr
[0];
for (int i
= 0; i
< arr
.length
; i
++) {
if (arr
[i
] > max
) {
max
= arr
[i
];
}
}
int maxLength
= (max
+ "").length();
int[][] bucket
= new int[10][arr
.length
];
int[] bucketElementCounts
= new int[10];
for(int i
= 0 , n
= 1; i
< maxLength
; i
++, n
*= 10) {
for(int j
= 0; j
< arr
.length
; j
++) {
int digitOfElement
= arr
[j
] / n
% 10;
bucket
[digitOfElement
][bucketElementCounts
[digitOfElement
]] = arr
[j
];
bucketElementCounts
[digitOfElement
]++;
}
int index
= 0;
for(int k
= 0; k
< bucketElementCounts
.length
; k
++) {
for (int l
= 0; l
< bucketElementCounts
[k
]; l
++) {
arr
[index
++] = bucket
[k
][l
];
}
bucketElementCounts
[k
] = 0;
}
System
.out
.println("第"+(i
+1)+"轮,对个位的排序处理 arr =" + Arrays
.toString(arr
));
}
}
}
7.5 程序执行结果
第1轮,对个位的排序处理 arr =[542, 53, 3, 14, 214, 748]
第2轮,对个位的排序处理 arr =[3, 14, 214, 542, 748, 53]
第3轮,对个位的排序处理 arr =[3, 14, 53, 214, 542, 748]
int[] testArr
= new int[80000];
for (int i
= 0; i
< 80000; i
++) {
testArr
[i
] = (int) (Math
.random() * 80000);
}
long start
= System
.currentTimeMillis();
radixSort(testArr
);
long end
= System
.currentTimeMillis();
System
.out
.println("基数排序耗时(ms):" + (end
- start
));
① 基数排序耗时(ms):36 ② 15ms ③ 41ms ④ 38ms ⑤ 37ms ⑥ 33ms
7.6 基数排序说明
基数排序是对传统桶排序的扩展, 速度很快
基数排序是经典的空间换时间的方式, 占用内存很大,当对海量数据排序时, 容易造成 OutOfMemoryError 。
基数排序时稳定的。 [注:假定在待排序的记录序列中, 存在多个具有相同的关键字的记录, 若经过排序, 这些记录的相对次序保持不变, 即在原序列中, r[i]=r[j], 且 r[i]在 r[j]之前, 而在排序后的序列中, r[i]仍在 r[j]之前,则称这种排序算法是稳定的; 否则称为不稳定的]
有负数的数组, 我们不用基数排序来进行排序, 如果要支持负数, 参考: https://code.i-harness.com/zh-CN/q/e98fa9
八,堆排序 [重点]
8.1 基本介绍
堆排序是利用堆这种数据结构而设计的一种排序算法, 堆排序是一种选择排序, 它的最坏, 最好, 平均时间复杂度均为 O(nlogn), 它也是不稳定排序。堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值, 称为大顶堆,注意 : 没有要求结点的左孩子的值和右孩子的值的大小关系。每个结点的值都小于或等于其左右孩子结点的值, 称为小顶堆
大顶堆举例说明
小顶堆举例说明
8.2 思路
将待排序序列构造成一个大顶堆此时, 整个序列的最大值就是堆顶的根节点将其与末尾元素进行交换, 此时末尾就为最大值然后将剩余 n-1 个元素重新构造成一个堆, 这样会得到 n 个元素的次小值。 如此反复执行, 便能得到一个有序序列了
可以看到在构建大顶堆的过程中, 元素的个数逐渐减少, 最后就得到一个有序序列了
8.3 代码示例
public class HeapSort {
public static void main(String
[] args
) {
int[] arr
= {4, 6, 8, 5, 9};
heapSort(arr
);
}
public static void heapSort(int[] arr
) {
int temp
= 0;
for (int i
= arr
.length
/ 2 - 1; i
>= 0; i
--) {
adjustHeap(arr
, i
, arr
.length
);
}
for (int j
= arr
.length
- 1; j
> 0; j
--) {
temp
= arr
[j
];
arr
[j
] = arr
[0];
arr
[0] = temp
;
adjustHeap(arr
, 0, j
);
}
System
.out
.println("数组= " + Arrays
.toString(arr
));
}
public static void adjustHeap(int[] arr
, int i
, int length
) {
int temp
= arr
[i
];
for (int k
= i
* 2 + 1; k
< length
; k
= k
* 2 + 1) {
if (k
+ 1 < length
&& arr
[k
] < arr
[k
+ 1]) {
k
++;
}
if (arr
[k
] > temp
) {
arr
[i
] = arr
[k
];
i
= k
;
} else {
break;
}
}
arr
[i
] = temp
;
}
}
8.4 程序执行结果
数组= [4, 5, 6, 8, 9]
int[] testArr
= new int[80000];
for (int i
= 0; i
< 80000; i
++) {
testArr
[i
] = (int) (Math
.random() * 80000);
}
long start
= System
.currentTimeMillis();
heapSort(testArr
);
long end
= System
.currentTimeMillis();
System
.out
.println("堆排序耗时(ms):" + (end
- start
));
① 堆排序耗时(ms):11 ② 14ms ③ 10ms ④ 11ms ⑤ 13ms ⑥ 15ms
常用排序算法总结何对比
1、比较图
2、相关术语解释
稳定:如果 a 原本在 b 前面, 而 a=b, 排序之后 a 仍然在 b 的前面;不稳定:如果 a 原本在 b 的前面, 而 a=b, 排序之后 a 可能会出现在 b 的后面;内排序: 所有排序操作都在内存中完成;外排序: 由于数据太大, 因此把数据放在磁盘中, 而排序通过磁盘和内存的数据传输才能进行;时间复杂度: 一个算法执行所耗费的时间;空间复杂度: 运行完一个程序所需内存的大小;n: 数据规模;k: “桶” 的个数;In-place:不占用额外内存;Out-place:占用额外内存。