链接: python版本.
主要排序算法:冒泡、选择、插入、希尔、归并、快速、堆排序。时间复杂度和空间复杂度及稳定性(原始序列中相同的两个元素经排序后前后顺序不改变)总结如下图。
方法 | 平均时间复杂度 | 最坏时间复杂度 | 最好时间复杂度 | 空间复杂度 | 稳定 |
---|---|---|---|---|---|
冒泡 | O(n^2) | O(n^2) | O(n^2) | O(1) | 稳定 |
选择 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
插入 | O(n^2) | O(n^2) | O(n) | O(1) | 稳定 |
希尔 | O(nlogn) | O(n^2) | O(n) | O(1) | 不稳定 |
归并 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 |
快速 | O(nlogn) | O(n^2) | O(nlogn) | O(nlogn) | 不稳定 |
堆排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | 不稳定 |
1.冒泡排序
从序列第一个数开始,n-1趟遍历,每一趟比较相邻元素,逆序交换,目的在于使得每一趟最后一个元素是最大值。
public class Sort {
//冒泡排序
public static void main(String[] args){
int[] num = {5, 8, 2, 3, 9, 1, 0};
bubbleSort(num);
System.out.println(Arrays.toString(num));
}
public static void bubbleSort(int[] num){
int len = num.length;
for(int i = 0; i < len - 1; i++){
for(int j = 0; j < len - i - 1; j++){
if(num[j] > num[j + 1]){
int tem = num[j];
num[j] = num[j + 1];
num[j + 1] = tem;
}
}
}
}
}
可加入检测机制改进冒泡法,当检测到某一趟无数据交换发生说明已无逆序数,可结束循环得到有序序列。
public class Sort {
//冒泡排序
public static void main(String[] args){
int[] num = {5, 8, 2, 3, 9, 1, 0};
bubbleSort(num);
System.out.println(Arrays.toString(num));
}
public static void bubbleSort(int[] num){
int len = num.length;
for(int i = 0; i < len - 1; i++){
boolean flag = true;
for(int j = 0; j < len - i - 1; j++){
if(num[j] > num[j + 1]){
int tem = num[j];
num[j] = num[j + 1];
num[j + 1] = tem;
flag = false;
}
}
if(flag){
break;
}
}
}
}
2.选择排序
遍历序列,n-1趟,每一趟标记最大元素位置,选出最大元素与最后一个元素交换。相比于冒泡法交换次数减少。
public class Sort {
//选择排序
public static void main(String[] args){
int[] num = {5, 8, 2, 3, 9, 1, 0};
selectSort(num);
System.out.println(Arrays.toString(num));
}
public static void selectSort(int[] num){
int len = num.length;
for(int i = 0; i < len - 1; i++){
int tem = 0;
for(int j = 1; j < len - i; j++){
if(num[tem] < num[j]){
tem = j;
}
}
int t = num[len - i - 1];
num[len - i - 1] = num[tem];
num[tem] = t;
}
}
}
3.插入排序
类似于整理扑克牌,从第二个元素开始,一次处理一个元素,将该元素与已已排序列比较并寻找插入位置(若当前元素小于已排序中元素i,则i开始整体后移,腾出一个空位给当前元素)。共需要处理n-1(2~末尾)个元素。
public class Sort {
//插入排序
public static void main(String[] args){
int[] num = {5, 8, 2, 3, 9, 1, 0};
insertSort(num);
System.out.println(Arrays.toString(num));
}
public static void insertSort(int[] num){
int len = num.length;
for(int i = 1; i < len; i++){
int tem = num[i];
int j = i - 1;
while(j >= 0 && tem < num[j]){
num[j + 1] = num[j];
j--;
}
num[j + 1] = tem;
}
}
}
4.希尔排序
对于插入排序而言,原始序列越短或列表越接近有序,插入比对次数越少,插入排序越高效。希尔排序在插入排序的基础上改进,将无序表进行间隔划分(逻辑划分,可理解为等间隔采样)形成子列表,再对子列表进行插入排序,这样做的目的是构造短的和接近有序的子列表以提升插入排序效率。间隔从n/2开始依次减半,直到为1。
public class Sort {
//希尔排序
public static void main(String[] args){
int[] num = {5, 8, 2, 3, 9, 1, 0};
shellSort(num);
System.out.println(Arrays.toString(num));
}
public static void shellSort(int[] num){
int len = num.length;
//从增量开始插入排序,直至完毕
for(int step = len / 2; step > 0; step /= 2){
for(int i = step; i < len; i++){
int tem = num[i];
int j = i;
//j - step表示同组的隔壁元素
while(j - step >= 0 && tem < num[j - step]){
num[j] = num[j - step];
j -= step;
}
num[j] = tem;
}
}
}
}
5.归并排序
分治思想,将原始序列分为一个个子序列,对子序列进行排序合并,直到整个序列有序。
public class Sort {
//归并排序
public static void main(String[] args){
int[] num = {5, 8, 2, 3, 9, 1, 0};
int len = num.length;
mergeSort(num, 0, len - 1);
System.out.println(Arrays.toString(num));
}
public static void mergeSort(int[] num, int l, int r){
if(l >= r) return;
int mid = (l + r) / 2;
mergeSort(num, l, mid);
mergeSort(num, mid + 1, r);
merge(num, l, mid, r);
}
public static void merge(int[] num, int l, int mid, int r){
int[] tem = new int[num.length];
for(int i = l; i <= r; i++){
tem[i] = num[i];
}
int i = l, j = mid + 1;
for(int k = l; k <= r; k++){
if(i == mid + 1){
num[k] = tem[j];
j++;
}else if(j == r + 1){
num[k] = tem[i];
i++;
}else if(tem[i] > tem[j]){
num[k] = tem[j];
j++;
}else{
num[k] = tem[i];
i++;
}
}
}
}
6.快速排序
(1)选择原始序列中任意元素(一般为头、尾或中间)作为基值。本例选取中间值
(2)一趟排序:将所有小于基值的元素移动到左边,左右大于基值的元素移动到右边,基值放中间。
(3)重复(2)直到所有元素都排好。
快速排序有两种实现方式:
1.递归
public class Sort {
//快速排序--递归
public static void main(String[] args){
int[] num = {5, 8, 2, 3, 9, 1, 0};
int len = num.length;
quickSort(num, 0, len - 1);
System.out.println(Arrays.toString(num));
}
public static void quickSort(int[] num, int start, int end){
if(start >= end) return;
int index = partition(num, start, end);
quickSort(num, start, index - 1);
quickSort(num, index + 1, end);
}
public static int partition(int[] num, int start, int end){
int tem = num[(start + end) / 2];
int i = start, j = end;
while(i < j){
while (i < j && num[i] <= tem) i++;
while (i < j && num[j] >= tem) j--;
if(i <= j){
swap(num, i, j);
i++;
j--;
}
}
num[(start + end) / 2] = num[j];
num[j] = tem;
return j;
}
public static void swap(int[] num, int i, int j){
int tem = num[i];
num[i] = num[j];
num[j] = tem;
}
}
2.非递归–借助栈实现
public class Sort {
//快速排序--非递归
public static void main(String[] args){
int[] num = {5, 8, 2, 3, 9, 1, 0};
int len = num.length;
quickSort(num, 0, len - 1);
System.out.println(Arrays.toString(num));
}
//仅修改了以下函数
public static void quickSort(int[] num, int start, int end){
if(start < end) {
Stack<Integer> set = new Stack<>();
set.push(start);
set.push(end);
while (!set.isEmpty()) {
int _e = set.pop();
int _s = set.pop();
if (_s < _e) {
int index = partition(num, _s, _e);
set.push(_s);
set.push(index - 1);
set.push(index + 1);
set.push(_e);
}
}
}
}
public static int partition(int[] num, int start, int end){
int tem = num[(start + end) / 2];
int i = start, j = end;
while(i < j){
while (i < j && num[i] <= tem) i++;
while (i < j && num[j] >= tem) j--;
if(i <= j){
swap(num, i, j);
i++;
j--;
}
}
num[(start + end) / 2] = num[j];
num[j] = tem;
return j;
}
public static void swap(int[] num, int i, int j){
int tem = num[i];
num[i] = num[j];
num[j] = tem;
}
}
7.堆排序
应用二叉堆(满足堆次序的完全二叉树)实现排序。时间复杂度o(nlogn),不稳定。
#思路:建立大顶堆-堆顶与最后一个元素(最小值)交换减小规模形成不包含最后一个元素的子堆-堆调整函数实现子堆调整成大顶堆-重复直到堆规模为1。
public class Sort {
//堆排序
public static void main(String[] args){
int[] num = {5, 8, 2, 3, 9, 1, 0};
int len = num.length;
int size = len - 1;
maxHeap(num, len);
for(int i = 0; i < len; i++){
int tem = num[len - 1 - i];
num[len - i - 1] = num[0];
num[0] = tem;
heapify(num, 0, size);
size--;
}
System.out.println(Arrays.toString(num));
}
public static void maxHeap(int[] num, int size){
for(int i = size - 1; i >= 0; i--){
heapify(num, i, size);
}
}
public static void heapify(int[] num, int currentRootNode, int size){
if(currentRootNode < size){
int max = currentRootNode;
int left = currentRootNode * 2 + 1;
int right = currentRootNode * 2 + 2;
if(left < size){
if(num[max] < num[left]){
max = left;
}
}
if(right < size){
if(num[max] < num[right]){
max = right;
}
}
if(max != currentRootNode){
int tem = num[currentRootNode];
num[currentRootNode] = num[max];
num[max] = tem;
heapify(num, max, size);
}
}
}
}