稳定的排序算法:开始的值相同的元素排序过后,相对位置仍然相同
1.直接插入排序:无序的序列依次插入有序的序列
复杂度o(n(2)) ; 稳定;空间复杂度 o(1)
public static int[] direct_insert_sort(int[] a){ for (int x = 1;x<a.length;x++){ //如果已经比有序的大了,则已经形成了有序不需要调整 if (a[x]<a[x-1]){ int temp = a[x]; for (int j = x-1;j>=0;j--){ if (a[j]>temp){ a[j+1] = a[j]; }else { a[j+1] = temp; break; } } } } return a; }
2.二分插入排序:在直接插入排序的基础上,确定插入位置时采用二分的方法
复杂度nlog2(n);稳定;空间复杂度 o(1)
public static int[] binary_insert_sort(int[] a){ for (int i = 2;i<a.length;i++){ if (a[i]<a[i-1]){ int temp = a[i]; int low = 0; int high = i-1; while (low<=high){ int mid = (low+high)/2; if (a[mid]<temp){ low = mid+1; }else { high = mid-1; } } //插入位置为low或high+1 for (int j = i-1;j>=low;j--){ a[j+1] = a[j]; } a[low] = temp; } } return a; }
3.希尔排序:将序列分为几个较小的序列,对这些序列先排序,再对较长的序列进行排序;划分的步长逐渐减小到1;
不稳定
public static int[] shell_insert_sort(int[] a){ int incre = a.length/2; while (incre>=1){ for(int i=0;i<a.length;i++){ //进行插入排序 for(int j=i;j<a.length-incre;j=j+incre){ if(a[j]>a[j+incre]){ int temp = a[j+incre]; int index = j; while (a[index]>temp && index>j){ a[index+incre] = a[index]; index = index - incre; } a[index+incre] = temp; } } } //设置新的增量 incre = incre/2; } return a; }
4.冒泡排序:基于两两比较和交换,每次冒出一个关键字最大的记录,直到某一趟未发生数据交换为止。
时间复杂度 o(n(2));稳定;空间复杂度o(1)
改进:若部分序列已经正序,则不需要再进行其间的冒泡->记录下本趟交换的最后位置
public static int[] bubble_sort(int[] a){ for (int j = 0;j<a.length;j++){ boolean swap = false; for (int i = 0;i<a.length-j-1;i++){ if (a[i]>a[i+1]){ int temp = a[i+1]; a[i+1] = a[i]; a[i] = temp; swap = true; } } //设置交换标志,节约循环的时间 if (swap == false){ break; } } return a; }
//优化算法,若后面的若干数据已经按关键字有序,则后面的若干数据不再参与冒泡
public static int[] bubble_modified_sort(int[] a){ int index = a.length; while (index>0){ int lastindex = 0; for (int j = 0;j<index-1;j++){ if (a[j]>a[j+1]){ int temp = a[j+1]; a[j+1] = a[j]; a[j] = temp; lastindex = j; } } index = lastindex; } return a; }
5.快速排序:以某个关键字为基准,将整个序列划分为两组,左边记录的关键字小于划分元,右边大于划分元,一般划分元取第一个元素
时间复杂度 o(nlog2(n);不稳定;空间复杂度o(log2(n))
public static int[] quick_sort(int[] a,int s,int t){ if (s<t){ int i = quick_partition(a,s,t); quick_sort(a,s,i-1); quick_sort(a,i+1,t); } return a; } public static int quick_partition(int[] a,int i,int j){ int temp = a[i]; while (i<j){ while (i<j && a[j]>=temp){ j--; } a[i] = a[j]; while (i<j && a[i]<=temp){ i++; } a[j] = a[i]; } a[i] = temp; return i; } }
6.选择排序:
简单选择排序:每次从无序序列部分选出一个关键字最大的记录添加到有序序列的尾部
时间复杂度 o(n(2));不稳定;空间复杂度o(1)
public static int[] simple_select_sort(int[] a){ for (int i = 0;i<a.length-1;i++){ int max = a[a.length-i-1]; int index = a.length - i-1; for (int j = 0;j<a.length - i-1;j++){ if (a[j]>max){ max = a[j]; index = j; } } int temp = a[a.length-i-1]; a[a.length-i-1] = max; a[index] = temp; } return a; }
树形排序:两两竞争,向上做出最大根,再将之前的最大根至为最小,再进行比较(胜者树)
时间复杂度 o(nlog2(n)
可以利用上一次排序的信息:第二次排序时,将胜者的兄弟节点直接升到父节点,从此层开始比较。
实现见:https://blog.csdn.net/liuquan0071/article/details/50462145
https://blog.csdn.net/a15994269853/article/details/21114045
7.二路归并排序:多路归并一般用在外部磁盘数据排序中,从2个一组开始向上迭代,也可采用递归方式
时间复杂度:o(nlog2(n));稳定;空间复杂度0(n)
public static int[] binary_merge_shell(int[] a){ int[] b = new int[a.length]; binary_merge_sort(a,b,0,a.length-1); return b; } public static void binary_merge_sort(int[] a,int[] b,int s,int t){ if (s == t){ b[s] = a[s]; }else { int m = (s + t)/2; binary_merge_sort(b,a,s,m); binary_merge_sort(b,a,m+1,t); merge(a,b,s,m,t); } } public static void merge(int[] a,int[] b,int s,int m,int t){ int i = s; int j = m+1; int k = s; while (i<=m && j<=t){ if (a[i]<a[j]){ b[k++] = a[i++]; }else { b[k++] = a[j++]; } } while (i<= m){ b[k++] = a[i++]; } while (j<=t){ b[k++] = a[j++]; } }
8.堆排序(选择排序的一种):输出堆顶,再建立新堆(最后一个元素替换到堆顶)
时间复杂度:o(nlog2(n);不稳定 ; 空间复杂度o(1)
实现见树章节
9.基数排序:多关键字排序