作者有话说
目录
各个排序的复杂度比较
作者有话说:
首先呢,给友友们先上一张图简单说一下数据结构中的七大排序都有哪些
图中的标绿的算法都是相比较来说稳定的(不知道的友友可以去搜“排序的稳定性”)
插入排序
插排:每次从无序区间中选择最小值放在无序区间的最前端 又分为直接插入和希尔排序
在极端条件下 内循环为O(n) 插入排序是用作高阶排序算法的优化手段之一
直接插入(insertionSort):
基本思想:顺序地把待排序的序列中的各个元素按其关键字的大小,插入到已排序的序列的适当位置。如下图所示
(图是盗的若有侵权请联系我删除)
算法思想:1将数组分为有序区间和无序区间两个部分(一般以数组第一个数表示有序,后面为无序) 2 比较大小 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置(swap方法)。
public static void insertionSort(int[] arr){
for (int i = 1; i < arr.length; i++) { //已排序区间
for (int j = i; j >0 ; j--) { //未排序区间
if (arr[j]>=arr[j-1]){
break;
}else {
swap(arr,j,j-1);
}
}
private static void swap(int[] arr, int i, int j) {
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
希尔排序(shellSort):
缩小增量的排序 将原数组/2=gap个分组,组内进行插入排序 重复该流程 直到gap=1 再次插序一次即可
代码奉上~~~
public static void shellSort(int[] arr){
int gap= arr.length>>1;
while (gap>1){
insertionSortByGap(arr,gap);
gap=gap>>1;
}
insertionSort(arr);
}
private static void insertionSortByGap(int[] arr, int gap) {
for (int i = gap; i < arr.length; i++) {
for (int j = i; j-gap>=0&&arr[j]<arr[i-gap] ; j-=gap) {
swap(arr,j,j-gap);
}
}
}
选择排序
选择排序分为选择排序和 堆排(这里就不介绍了哈 详情在另一篇“优先级队列(堆)”)
选择排序基本思想就是每次从无序区间中选择最小值放在无序区间的最前端 经过一轮后有一个元素到达了位置 有序区间+1 此排序不是稳定的 因为元素交换时有可能改变相等值的先后顺序
public static void selectionSort(int[] arr){
for (int i=0;i< arr.length-1;i++){
int min=i;
for (int j=i+1;j< arr.length;j++){
if (arr[j]<arr[min]){
min=j;
}
}
swap(arr,min,i);
}
}
选择排序有优化的空间可以改为双向选择排序具体代码如下:low标为数组第一个索引 开始时假设最大和最小索引都为low 遍历数组找到min 和max后进行转换(swap)此时要考虑一个特殊情况当swap(arr,min,low)后最大值正好是第一个元素 就要先判断if (max==low)再转换最大值
public static void selectionSortOP(int[] arr){
int low=0;
int high= arr.length-1;
while (low<=high){
int min=low;
int max=low;
for (int i=low+1;i<=high;i++){
if (arr[i]<arr[min]){
min=i;
}
if (arr[i]>arr[max]){
max=i;
}
}
swap(arr,min,low);
if (max==low){
max=min;
}
swap(arr,max,high);
low+=1;
high-=1;
}
}
交换排序
交换排序分为冒泡排序和快速排序
冒泡排序很简单没啥说的代码如下
public static void bubbleSort(int[] arr){
for (int i = 0; i < arr.length-1; i++) {
boolean isSwaped =false;
for (int j = 0; j <arr.length-1-i; j++) {
if (arr[j]>arr[j+1]){
swap(arr,j,j+1);
isSwaped=true;
}
}
if (!isSwaped){
break;
}
}
快速排序的基本思想:1从待排序区间中选择一个数 作为基准值(pivot)
2遍历整个待排序区间,将比基准值小的或者相等的放在pivot左边,比它大的或者相等的放在右边
3采用分治思想,对左右两个区间继续以同样的方式进行处理直到小区间==1代表已经有序
public static void quickSort(int[] arr){
quickSortInternal(arr,0,arr.length-1);
}
private static void quickSortInternal(int[] arr, int l, int r) {
if (r-l<=15){
insertionSort(arr,l,r);
return;
}
int p=partition(arr,l,r);
quickSortInternal(arr,l,p);
quickSortInternal(arr,p+1,r);
}
public static final ThreadLocalRandom random=ThreadLocalRandom.current();
private static int partition(int[] arr, int l, int r) {
int randomIndex = random.nextInt(l,r);
swap(arr,l,randomIndex);
int v=arr[l];
int j=l;
for (int i = l+1; i <=r ; i++) {
if (arr[i]<v)
swap(arr,j+1,i);
j++;
}
swap(arr,l,j);
return j;
}
归并排序
基本思想:1将原数组不断拆分,一直拆到每个子数组只有一个元素时第一阶段(归)结束
2将相邻的两个数组合并为一个有序数组,直到整个数组有序(并)
public static void mergeSort(int[] arr){
mergeSortInternal(arr,0,arr.length-1);
}
private static void mergeSortInternal(int[] arr, int l, int r) {
// if (r-l<=15){
// insertionSort(arr,l,r);
// }
if (l>=r){
return;
}
int mid=l+((r-l)>>1);
mergeSortInternal(arr,l,mid);
mergeSortInternal(arr,mid+1,r);
if (arr[mid]>arr[mid+1]){
merge(arr,l,mid,r);
}
}
private static void merge(int[] arr, int l, int mid, int r) {
int[] aux=new int[r-l+1]; //左闭右开 会有1个差值
for (int i = 0; i < aux.length; i++) {
aux[i]=arr[i+l]; //会有l个偏移量
}
int i=l;//左侧开始索引
int j=mid+1; //右侧开始索引
// k表示当前正在合并的原数组的索引下标
for (int k = l; k <=r ; k++) {
// 边界条件
if (i>mid){ //左侧区间已经被处理完毕 只需要将右侧区间的值拷贝原数组即可
arr[k]=aux[j-l];
j++;
}else if (j>r){ //右侧区间已经被处理完毕
arr[k]=aux[i-l];
i++;
}else if (aux[i-l]<=aux[j-l]){
arr[k]=aux[i-l];
i++;
}
else {
arr[k]=aux[j-l];
j++;
}
}
}
归并排序有两个优点:
1当左右两个子区间走完了子函数后 左右两区间已经有序 此时arr[mid]<arr[mid+1]说嘛整个区间已经有序,没必要在执行merge过程。
各个排序的复杂度比较(如有侵权请联系我删除)