原博客网址http://blog.csdn.net/roamer_nuptgczx/article/details/51524466
目录
- 前言
- 冒泡排序
- 选择排序
- 插入排序
- 快速排序
- 归并排序
- 堆排序
- 希尔排序
- 基数排序
- 参考资料
前言
排序算法在笔试面试中几乎是必考的,因为它是很多复杂算法的基础,也是我们学习数据结构与算法的入门知识。目前网上介绍各类排序算法的博客和帖子非常多,但其中有不少作者提供的代码有错误或者代码中没有考虑特殊情况。在此,我们再次总结一下常见的八大排序算法,试图讲清楚各个算法的基本原理,并提供Java代码以及详细的注释。所有代码都是对数组进行升序排列且已经测试通过,但没有使用足够的测试用例,如果代码中存在问题,请大家留言指出,或者有更好的算法思路,也可以交流讨论。
冒泡排序
/** * 冒泡排序 * * 原理: * 每一轮循环中依次比较相邻位置上元素的大小,使得较大的元素后移, * 且确保第i轮循环完之后能把第i大的元素移动到排序后的位置上 * * 改进: * 每一轮循环开始前设置标志位,如果本轮循环没有交换任何元素, * 则说明所有元素已经有序,可以提前结束排序 * * @author Roamer */void bubbleSort(int[] a) { if(a == null || a.length == 0) return; boolean flag; for(int i = a.length - 1; i > 0; --i) { flag = true; for(int j = 0; j < i; ++j) { if(a[j] > a[j+1]) { swap(a, j, j+1); flag = false; } } if(flag) break; }}
- 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
- 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
选择排序
/** * 选择排序 * * 原理: * 从数组的首元素开始,将第i个元素之后的所有元素通过相互比较找到最小值的索引, * 如果当前元素比这个最小元素大,则交换之,使得第i个位置的元素值为第i小, * 相当于每一轮循环是在所有未排序的元素之中选择出最小的元素。 * * @author Roamer */void selectSort(int[] a) { if(a == null || a.length == 0) return; for(int i = 0; i < a.length - 1; ++i) { int minIdx = i + 1; for(int j = i + 2; j < a.length; ++j) { if(a[j] < a[minIdx]) minIdx = j; } if(a[i] > a[minIdx]) swap(a, i, minIdx); }}
- 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
- 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
插入排序
/** * 插入排序 * * 原理: * 确保数组前i-1个元素已经排好序,在第i轮循环时,将第i个元素从后往前依次和 * 其前面的元素比较和交换,最后插入到前i-1个有序的子数组中的合适位置 * * @author Roamer */void insertSort(int[] a) { if(a == null || a.length == 0) return; for(int i = 1; i < a.length; ++i) { for(int j = i; j > 0 && a[j-1] > a[j]; --j) swap(a, j-1, j); }}/** * 插入排序改进 * * 如果当前元素已经位于正确的位置,则不必继续往前插入,可以提前结束本轮循环 * * @author liwendongyang */void insertSort2(int[] a) { if (a == null || a.length == 0) return; for (int i = 1; i < a.length; ++i) { for (int j = i; j > 0; --j) { if (a[j - 1] > a[j]) swap(a, j - 1, j); else break; } }}
- 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
- 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
快速排序
/** * 快速排序 * * 原理: * 将数组的首元素作为比较的基准,用两个指针从数组的两端往中间扫描, * 当左指针对应的元素大于基准值且右指针对应的元素小于基准值,则交换两者对应的元素值, * 使得每一轮遍历之后数组分成比基准值更大和更小的两部分, * 再把基准元素从数组首位交换到数组的中间,从而其左边的元素都不大于它, * 且其右边的元素都不小于它,然后将左右两个子数组递归调用快排函数,最终使数组有序 * * @author Roamer */void quickSort(int[] a, int low, int high) { if(a == null || a.length == 0) return; if(low < high){ int pivot = partition(a, low, high); quickSort(a, low, pivot - 1); quickSort(a, pivot + 1, high); }}private int partition(int[] a, int low, int high) { int pivot = low; while(low < high) { while(low < high && a[high] >= a[pivot]) --high; while(low < high && a[low] <= a[pivot]) ++low; swap(a, low, high); } swap(a, pivot, low); return low;}private int partition1(int[] a, int low, int high) { int pivotValue = a[low]; while(low < high) { while(low < high && a[high] >= pivotValue) --high; a[low] = a[high]; while(low < high && a[low] <= pivotValue) ++low; a[high] = a[low]; } a[low] = pivotValue; return low;}private int partition2(int[] a, int low, int high) { int pivotValue = a[low]; while(low < high) { while(low < high && a[high] >= pivotValue) --high; swap(a, low, high); while(low < high && a[low] <= pivotValue) ++low; swap(a, low, high); } return low;}
- 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
- 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
归并排序
/** * 归并排序(递归版) * * 原理: * 将数组均分成两个子数组,先将两个子数组分别进行排序,然后合并得到全体元素都有序的数组, * 为使上述的两个子数组分别有序,需要先对其各自的两个子数组进行排序再合并,因此需要递归地 * 对每个子数组的两个子数组进行归并排序,直到子数组只有2个元素,此时只需要直接进行合并 * * @author Roamer */void MergeSort(int[] a) { if(a == null || a.length == 0) return; int[] b = new int[a.length]; Merge(a, b, 0, a.length-1, (a.length-1)/2);}private void Merge(int[] a, int[] b, int low, int high, int pivot) { if(low < high) { Merge(a, b, low, pivot, (low+pivot)/2); Merge(a, b, pivot+1, high, (high+pivot+1)/2); } int i = low; int j = pivot+1; int k = low; while(i <= pivot && j <= high){ if(a[i] < a[j]) b[k++] = a[i++]; else b[k++] = a[j++]; } while(i <= pivot) b[k++] = a[i++]; while(j <= high) b[k++] = a[j++]; System.arraycopy(b, low, a, low, high-low+1);}/** * 归并排序(迭代版) * * 原理: * 先将整个数组依次划分成若干个长度为2的子数组,对每个子数组中的两个元素进行合并, * 再将整个数组依次划分成若干个长度为4的子数组,对每个子数组中的两个子数组进行合并, * 如此循环,直到只能将数组划分成两个子数组,这两个子数组已经分别有序,直接合并即可 * * @author Roamer */void MergeSort2(int[] a) { if(a == null || a.length == 0) return; int[] b = new int[a.length]; int len = 2; while(len <= a.length) { int i = 0; while(i + len <= a.length) { Merge2(a, b, i, i+len-1, i+(len-1)/2); i += len; } if(i != a.length) Merge2(a, b, i, a.length-1, (i+a.length)/2); len <<= 1; System.arraycopy(b, 0, a, 0, a.length); } if(len != a.length) { Merge2(a, b, 0, a.length-1, (len-1)/2); System.arraycopy(b, 0, a, 0, a.length); }}private void Merge2(int[] a, int[] b, int low, int high, int pivot) { int i = low; int j = pivot+1; int k = low; while(i <= pivot && j <= high){ if(a[i] < a[j]) b[k++] = a[i++]; else b[k++] = a[j++]; } while(i <= pivot) b[k++] = a[i++]; while(j <= high) b[k++] = a[j++];}
- 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
- 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
堆排序
/** * 堆排序 * * 原理: * 先将无序数组构成一个二叉堆(完全二叉树),使得每个节点都小于其子节点(兄弟节点之间可以无序), * 每次取出根节点后,重新调整堆使其仍满足上述特性,每次取出的根节点就构成了一个有序数组。 * * 建堆 * 用数组来存放整个二叉堆,且数组的首元素中存储着整个二叉堆的节点总个数, * 建堆时总是在二叉堆的叶子节点处插入新节点,然后“上浮”该节点,最终在数组中得到一个二叉堆。 * * 调整堆 * 每次在叶子节点处插入新节点即在数组末位插入新元素需要调整堆,比较新节点和其父节点的大小, * 通过不断交换使其“上浮”,并最终位于二叉树的合适位置; * 每次取出二叉堆的根节点即取出数组的第二个元素后需要调整堆,将二叉树的最后一个叶子节点作为新的根节点, * 然后依次比较其和子节点的大小,通过不断交换使其“下沉”,并最终位于二叉树的合适位置; * * @author Roamer */void heapSort(int[] a) { int[] b = new int[a.length + 1]; for(int i = 0; i < a.length; ++i) insert(b, a[i]); for(int i = 0; i < a.length; ++i) a[i] = getRoot(b);}private void insert(int[] heap, int ele) { heap[0] += 1; heap[heap[0]] = ele; goUp(heap);}private int getRoot(int[] heap) { if (heap[0] < 1) throw new RuntimeException("二叉堆已经为空,不能再取出元素!"); int root = heap[1]; heap[1] = heap[heap[0]]; heap[0] -= 1; goDown(heap); return root;}private void goDown(int[] heap) { int idx = 1; int left, right, minIdx; boolean flag = true; while(flag) { flag = false; left = (idx << 1); right = left + 1; if (left > heap[0]) break; else if (right > heap[0]) minIdx = left; else minIdx = (heap[left] < heap[right]) ? left : right; if (heap[idx] > heap[minIdx]) { swap(heap, idx, minIdx); idx = minIdx; flag = true; } } }private void goUp(int[] heap) { int idx = heap[0]; int parent = (idx >> 1); while(parent > 0 && heap[idx] < heap[parent]) { swap(heap, idx, parent); idx = parent; parent = (idx >> 1); }}
- 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
- 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
希尔排序
/** * 希尔排序(缩小增量排序) * * 原理: * 先设置一个较大的步长(增量),将数组分为若干个子序列,对每个子序列分别进行排序, * 再减少步长,再次将数组分为若干个更长的子序列,对每个子序列分别进行排序, * 如此循环,直到步长为1,即整个数组中只有一个子序列,此时整个数组已经有序 * 其中,子序列的排序可以采用任何其他排序算法,每一轮排序之后,数组将变得更加有序一些 * * @author Roamer */void shellSort(int[] a) { int step = 1; while(step < a.length) step = 3*step + 1; while(step > 1) { step = step / 3 + 1; for (int i = 0; i < step; ++i) { int nsub = (a.length - i - 1) / step + 1; int[] sub = new int[nsub]; for (int j = 0; j < nsub; ++j) sub[j] = a[i + j * step]; bubbleSort(sub); for (int j = 0; j < nsub; j++) a[i + j * step] = sub[j]; } }}
- 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
- 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
基数排序
/** * 基数排序 * * 原理: * 基数排数基于桶排序的思想。如果需要排序的元素是正整数,则可以通过依次比较他们每个数位上 * 数字的大小进行排序,由于十进制只有10个数码,所以只需要10个“桶”就够了。 * 基数排序的方式可以采用LSD(Least sgnificant digital)或MSD(Most sgnificant digital), * 如果是采用LSD算法,则从个位开始,将所有整数根据个位数字分别放到对应的10个桶中, * 再按顺序从各个桶中将所有整数取出依次填回原数组,然后将所有整数根据十位数字重新放到新的10个桶中, * 以此类推,直到所有元素的所有数位都遍历完,由于基数排序是稳定的,所以数组中的所有元素最终都有序了 * * @author Roamer */void radixSort(int[] a, int len) { int k = 0; int m = 1; int n = 1; int[][] bucket = new int[10][a.length]; int[] order = new int[10]; while(m <= len) { for(int i = 0; i < a.length; ++i) { int lsd = (a[i] / n) % 10; bucket[lsd][order[lsd]] = a[i]; order[lsd]++; } for(int i = 0; i < 10; i++) { if(order[i] != 0) { for(int j = 0; j < order[i]; j++) a[k++] = bucket[i][j]; order[i] = 0; } } k = 0; m++; n *= 10; }}
- 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
- 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
参考资料
1: 纸上谈兵: 排序算法简介及其C实现
2: 面试中的排序算法总结
3: 基数排序——百度百科