漫画算法(4)- 排序

冒泡排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class BubbleSort {

public static void sort(int[] array) {
int tmp = 0;
// 记录最后一次交换的位置
int lastExchangeIndex = 0;
// 无序数列的边界,每次比较只需要比到这里为止
int sortBorder = array.length - 1;
for (int i = 0; i < array.length; i++) {
// 有序标记,每一轮的初始是true
boolean isSorted = true;

for (int j = 0; j < sortBorder; j++) {
if (array[j] > array[j + 1]) {
tmp = array[j];
array[j] = array[j + 1];
array[j + 1] = tmp;
// 有元素交换,所以不是有序,标记变为false
isSorted = false;
// 把无序数列的边界更新为最后一次交换元素的位置
lastExchangeIndex = j;
}
}
sortBorder = lastExchangeIndex;
if (isSorted) {
break;
}
}
}

public static void main(String[] args) {
int[] array = new int[]{3, 4, 2, 1, 5, 6, 7, 8};
sort(array);
System.out.println(Arrays.toString(array));
}
}

鸡尾酒排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 排序过程像钟摆一样,第1轮从左到右,第2轮从右到左,第3轮从左到右...
public class CockTailSort {

public static void sort(int array[]) {
int tmp = 0;
for (int i = 0; i < array.length / 2; i++) {
// 有序标记,每一轮的初始是true
boolean isSorted = true;
// 奇数轮,从左向右比较和交换
for (int j = i; j < array.length - i - 1; j++) {
if (array[j] > array[j + 1]) {
tmp = array[j];
array[j] = array[j + 1];
array[j + 1] = tmp;
// 有元素交换,所以不是有序,标记变为false
isSorted = false;
}
}
if (isSorted) {
break;
}

// 偶数轮之前,重新标记为true
isSorted = true;
// 偶数轮,从右向左比较和交换
for (int j = array.length - i - 1; j > i; j--) {
if (array[j] < array[j - 1]) {
tmp = array[j];
array[j] = array[j - 1];
array[j - 1] = tmp;
// 有元素交换,所以不是有序,标记变为false
isSorted = false;
}
}
if (isSorted) {
break;
}
}
}

public static void main(String[] args) {
int[] array = new int[]{2, 3, 4, 5, 6, 7, 8, 1};
sort(array);
System.out.println(Arrays.toString(array));
}
}

快速排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
public class QuickSort {

// 递归
public static void quickSort(int[] arr, int startIndex, int endIndex) {
// 递归结束条件:startIndex大于等于endIndex
if (startIndex >= endIndex) {
return;
}
// 得到基准元素位置
int pivotIndex = partition(arr, startIndex, endIndex);
// 根据基准元素,分成两部分递归排序
quickSort(arr, startIndex, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, endIndex);
}

// 非递归
public static void quickSortWithStack(int[] arr, int startIndex, int endIndex) {
// 用一个集合栈来代替递归的函数栈
Stack<Map<String, Integer>> quickSortStack = new Stack<> ();
// 整个数列的起止下标,以哈希的形式入栈
Map rootParam = new HashMap();
rootParam.put("startIndex", startIndex);
rootParam.put("endIndex", endIndex);
quickSortStack.push(rootParam);

// 循环结束条件:栈为空时结束
while (!quickSortStack.isEmpty()) {
// 栈顶元素出栈,得到起止下标
Map<String, Integer> param = quickSortStack.pop();
// 得到基准元素位置
int pivotIndex = partition(arr, param.get("startIndex"), param.get("endIndex"));
// 根据基准元素分成两部分, 把每一部分的起止下标入栈
if (param.get("startIndex") < pivotIndex - 1) {
Map<String, Integer> leftParam = new HashMap<> ();
leftParam.put("startIndex", param.get("startIndex"));
leftParam.put("endIndex", pivotIndex - 1);
quickSortStack.push(leftParam);
}
if (pivotIndex + 1 < param.get("endIndex")) {
Map<String, Integer> rightParam = new HashMap<> ();
rightParam.put("startIndex", pivotIndex + 1);
rightParam.put("endIndex", param.get("endIndex"));
quickSortStack.push(rightParam);
}
}
}

/**
* 分治(双边循环法)
*
* @param arr 待交换的数组
* @param startIndex 起始下标
* @param endIndex 结束下标
*/
private static int partition(int[] arr, int startIndex, int endIndex) {
// 取第一个位置的元素作为基准元素(也可以选择随机位置)
int pivot = arr[startIndex];
int left = startIndex;
int right = endIndex;

while (left != right) {
// right指针比较并左移
while (left < right && arr[right] > pivot) {
right--;
}
// left指针比较并右移
while (left < right && arr[left] <= pivot) {
left++;
}
// 交换left和right指向的元素
if (left < right) {
int p = arr[left];
arr[left] = arr[right];
arr[right] = p;
}
}

// pivot和指针重合点交换
arr[startIndex] = arr[left];
arr[left] = pivot;

return left;
}

/**
* 分治(单边循环法)
*
* @param arr 待交换的数组
* @param startIndex 起始下标
* @param endIndex 结束下标
*/
private static int partitionV2(int[] arr, int startIndex, int endIndex) {
// 取第一个位置的元素作为基准元素(也可以选择随机位置)
int pivot = arr[startIndex];
int mark = startIndex;

for (int i = startIndex + 1; i <= endIndex; i++) {
if (arr[i] < pivot) {
mark++;
int p = arr[mark];
arr[mark] = arr[i];
arr[i] = p;
}
}

arr[startIndex] = arr[mark];
arr[mark] = pivot;
return mark;
}

public static void main(String[] args) {
int[] arr = new int[] {4, 4, 6, 5, 3, 2, 8, 1};
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
}

堆排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class HeapSort {

/**
* 下沉调整
*
* @param array 待调整的堆
* @param parentIndex 要下沉的父节点
* @param length 堆的有效大小
*/
public static void downAdjust(int[] array, int parentIndex, int length) {
int temp = array[parentIndex];
int childIndex = 2 * parentIndex + 1;
while (childIndex < length) {
// 如果有右孩子,且右孩子大于左孩子的值,则定位到右孩子
if (childIndex + 1 < length && array[childIndex + 1] > array[childIndex]) {
childIndex++;
}
// 如果父节点大于等于任何一个孩子的值,直接跳出
if (temp >= array[childIndex])
break;
array[parentIndex] = array[childIndex];
parentIndex = childIndex;
childIndex = 2 * childIndex + 1;
}
array[parentIndex] = temp;
}

/**
* 堆排序
*
* @param array 待调整的堆
*/
public static void heapSort(int[] array) {
// 1.把无序数组构建成最大堆
for (int i = (array.length - 2) / 2; i >= 0; i--) {
downAdjust(array, i, array.length);
}
System.out.println(Arrays.toString(array));
// 2.循环交换集合尾部元素到堆顶,并调节堆产生新的堆顶
for (int i = array.length - 1; i > 0; i--) {
// 最后一个元素和第一元素进行交换
int temp = array[i];
array[i] = array[0];
array[0] = temp;
// 下沉调整最大堆
downAdjust(array, 0, i);
}
}

public static void main(String[] args) {
int[] arr = new int[]{1, 3, 2, 6, 5, 7, 8, 9, 10, 0};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
}

计数排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class CountSort {

public static int[] countSort(int[] array) {
// 1.得到数列的最大值
int max = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
// 2.根据数列最大值确定统计数组的长度
int[] countArray = new int[max + 1];
// 3.遍历数列,填充统计数组
for (int i = 0; i < array.length; i++) {
countArray[array[i]]++;
}
// 4.遍历统计数组,输出结果
int index = 0;
int[] sortedArray = new int[array.length];
for (int i = 0; i < countArray.length; i++) {
for (int j = 0; j < countArray[i]; j++) {
sortedArray[index++] = i;
}
}
return sortedArray;
}

public static int[] countSortV2(int[] array) {
// 1.得到数列的最大值和最小值,并算出差值d
int max = array[0];
int min = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
if (array[i] < min) {
min = array[i];
}
}
int d = max - min;
// 2.创建统计数组并统计对应元素个数
int[] countArray = new int[d + 1];
for (int i = 0; i < array.length; i++) {
countArray[array[i] - min]++;
}
// 3.统计数组做变形,后面的元素等于前面的元素之和
for (int i = 1; i < countArray.length; i++) {
countArray[i] += countArray[i - 1];
}
// 4.倒序遍历原始数列,从统计数组找到正确位置,输出到结果数组
int[] sortedArray = new int[array.length];
for (int i = array.length - 1; i >= 0; i--) {
sortedArray[countArray[array[i] - min] - 1] = array[i];
countArray[array[i] - min]--;
}
return sortedArray;
}

public static void main(String[] args) {
int[] array = new int[]{4, 4, 6, 5, 3, 2, 8, 1, 7, 5, 6, 0, 10};
int[] sortedArray = countSort(array);
int[] sortedArray2 = countSort(array);
System.out.println(Arrays.toString(sortedArray));
System.out.println(Arrays.toString(sortedArray2));

array = new int[]{95, 94, 91, 98, 99, 90, 99, 93, 91, 92};
sortedArray = countSortV2(array);
sortedArray2 = countSortV2(array);
System.out.println(Arrays.toString(sortedArray));
System.out.println(Arrays.toString(sortedArray2));
}
}

桶排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/*
每一个桶代表一个区间范围,里面可以承载一个或多个元素。
所有的统都保存在 ArrayList 集合中,每一个桶都被定义成一个链表 LinkedList。
JDK 集合工具类 Collections.sort 底层采用归并排序或 TimSort。
*/
public class BucketSort {

public static double[] bucketSort(double[] array) {

// 1.得到数列的最大值和最小值,并算出差值d
double max = array[0];
double min = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
if (array[i] < min) {
min = array[i];
}
}
double d = max - min;

// 2.初始化桶
int bucketNum = array.length;
ArrayList<LinkedList<Double>> bucketList = new ArrayList<>(bucketNum);
for (int i = 0; i < bucketNum; i++) {
bucketList.add(new LinkedList<>());
}

// 3.遍历原始数组,将每个元素放入桶中
for (int i = 0; i < array.length; i++) {
int num = (int) ((array[i] - min) * (bucketNum - 1) / d);
bucketList.get(num).add(array[i]);
}

// 4.对每个桶内部进行排序
for (int i = 0; i < bucketList.size(); i++) {
// JDK底层采用了归并排序或归并的优化版本
Collections.sort(bucketList.get(i));
}

// 5.输出全部元素
double[] sortedArray = new double[array.length];
int index = 0;
for (LinkedList<Double> list : bucketList) {
for (double element : list) {
sortedArray[index] = element;
index++;
}
}
return sortedArray;
}

public static void main(String[] args) {
double[] array = new double[]{4.12, 6.421, 0.0023, 3.0, 2.123, 8.122, 4.12, 10.09};
double[] sortedArray = bucketSort(array);
System.out.println(Arrays.toString(sortedArray));
}
}

参考资料

《漫画算法》第 4 章


漫画算法(4)- 排序
https://lcf163.github.io/2020/11/17/漫画算法(4)- 排序/
作者
乘风的小站
发布于
2020年11月17日
许可协议