0
点赞
收藏
分享

微信扫一扫

常见几种排序的思想

sullay 2022-03-22 阅读 44
java

注意:下面排序都默认从小到大
1、直接插入排序 思想:让i从第二个数字开始往后走,j是i左边的第一个数字,然后把i的数字给tmp,比较j与tmp的大小,如果j大于tmp,就把j往后挪一个位置,然后j继续往左边走,知道j小于0,证明左边已经没有数字了,然后i继续往后走,在用上面的方法进行判断。最后要将tmp的值在赋给j+1的位置。
代码:
for (int i = 1; i <array.length ; i++) {
int j=i-1;
int tmp=array[i];
for (;j>=0;j–){
if(array[j]>tmp){
array[j+1]=array[j];
}else{
break;
}
}
array[j+1]=tmp;
}
}
时间复杂度:当数据有序的时候是最好的 N 最坏的情况是无序的n的平方
空间复杂度:1
稳定性:稳定
结论:当一组数据量不大,且趋近于有序此时用插入排序时间更快。
2:希尔排序:希尔排序是对直接插入排序的优化,把整个数组分成若干组gap,然后进行排序,每次排序完,gap都要变小,然后重复,直到gap变成1,然后就成为了直接插入排序。
代码:for(int i=gap;i<array.length;i++){
int tmp=array[i];
int j=i-gap;
for (;j>=0;j=j-gap){
if(array[j]>tmp){
array[j+gap]=array[j];
}else{
break;
}
}
array[j+gap]=tmp;
}
3:选择排序:在i的后面依次选择J,与i进行比较,谁小放在前面。然后i循环。
for(int i=0;i<array.length;i++){
int j=i+1;
for (; j <array.length ; j++) {
if(array[j]<array[i]){
int tmp=array[j];
array[j]=array[i];
array[i]=tmp;
}
}
}
时间复杂度:n的2次方
空间复杂度:1
稳定性:不稳定
4:堆排序:堆就是一个数组 ,首先我们要先建立一个大根堆,大根堆的创建就是从最后一个子树,然后向下调整,然后进行排序,每一次把第一个和最后一个树交换,没交换一次调用一下大根堆,这样最大的树就会一直在最下面,这样就排序了。
创建大根堆 就需要向下调整,向下调整就需要先找到最后一课子树,然后开始向下调整。
createHeap(array);
int end=array.length-1;
while(end>0){
int tmp=array[0];
array[0]=array[end];
array[end]=tmp;
siftDown(array,0,end);
end–;
}
时间复杂度:每一层的数子乘以数组的高度 n*log n
空间复杂度:1
稳定性:不稳定

5:冒泡排序:每相邻的两个数字进行比较,i表示一共要比多少个数字(从哪一个数字开始) j表示相邻的多少趟
for (int i = 0; i <array.length-1 ; i++) {
boolean flag=false;
for (int j = 0; j < array.length-1-i; j++) {
if(array[j+1]<array[j]){
int tmp=array[j+1];
array[j+1]=array[j];
array[j]=tmp;
flag=true;
}

        }
        if(flag==false){
            break;
        }
        这里面有对冒泡排序进行的优化。
        时间复杂度:有序的话N  无序的话是n的平方
        空间复杂度:1
        稳定性:稳定
 6://归并排序 把一组数采用分治法,将有序的子序合并成一个有序表。成为二路归并.把一组数据分组,直到分成一个数字为一组,然后进行合并,合并的时候要排大小,类似于二叉树的遍历。下面的方法采用递归的方法进行
 代码:分解
 public static void mergeSortInternal(int array[],int low,int high){
    //分解
    if(low>=high){
        return;
    }
    int mid=(low+high)/2;
    mergeSortInternal(array,low,mid);
    mergeSortInternal(array,mid+1,high);
    这里递归完就成为了每个数字,就好比二叉树的叶子结点。
    然后进行合并:
    //这里的是合并的思想,合并两个有序的数组 链表 都可以用这种思想。
public static void merge(int [] array,int low,int mid,int high){
    int s1=low;
    int e1=mid;
    int s2=mid+1;
    int e2=high;
    int [] tmp=new int[high-low+1];
    int k=0;
    //谁小把谁放进去
    while(s1<=e1&&s2<=e2){//这里代表稳定性,谁小与等于放在前面。就可以保证第一次出现相同的值放在前面。
        if(array[s1]<=array[s2]){
            tmp[k++]=array[s1++];
        }else{
            tmp[k++]=array[s2++];
        }
    }
    //判断那个数组走完了 哪个数组还没有走完
    while(s1<=e1){
        tmp[k++]=array[s1++];
    }
    while(s2<=e2){
        tmp[k++]=array[s2++];
    }
    //然后把tmp的值都传到array中
    for (int i = 0; i <tmp.length; i++) {
        array[i+low]=tmp[i];//因为如果右子树的话,不是从0开始传的。
    }
}

合并的思想很多地方都可以用,就是设置s1 e1 s2 e2然后两组数字一边走一边比较大小,谁小把谁先放到数组中,若是那个数组为空,就字节把另一个数组整个放到里面。
//时间复杂度:n*log n 每一次有n个值,然后乘以树的高度(走了多少次)。
//空间复杂度:n
//稳定性:稳定
还有非递归的方法:是设置一个区间,然后两个小区间利用合并的思想进行合并
代码:
public static void merge2(int [] array,int gap){
int [] tmp=new int[array.length];
int k=0;
int s1=0;
int e1=s1+gap-1;
int s2=e1+1;
int e2=s2+gap-1<array.length?s2+gap-1:array.length-1;
//因为会有好几组所以这里是一个循环
while(s2<array.length){
while(s1<=e1&&s2<=e2){
if(array[s1]<=array[s2]){
tmp[k++]=array[s1++];
}else{
tmp[k++]=array[s2++];
}
}
while(s1<=e1){
tmp[k++]=array[s1++];
}
while(s2<=e2){
tmp[k++]=array[s2++];
}
//一组结束了,确定新的区间的开始和结束
s1=e2+1;
e1=s1+gap-1;
s2=e1+1;
e2=s2+gap-1<array.length?s2+gap-1:array.length-1;
}
while(s1<=array.length-1){
tmp[k++]=array[s1++];
}
for (int i = 0; i <tmp.length ; i++) {
array[i]=tmp[i];

     }

7:快速排序:选择一个partition值,一般选择最左端的值,然后从右边开始走,找到比part值小的数放在左边low的位置,然后从low的位置往右走,找到比p大的值放在右边high的位置,等low和high相遇的时候暂停寻找。
优化:一般先进行array[mid]<=array[start]<=array[end]的比较,如果不符合上述规则,就把三个值相互调换,符合规则为准,这样p的值就在最左侧了。
优化2:可以设定当数组的区间分成小区间,小区间的值满足多少的时候,可以直接使用插入排序法。
找到一个partition的值然后分成左右区间,继续找partition的值,也类似于二叉树,直到数组不能分解。
//先找partition结点 挖坑法
public static int partition(int [] array,int low,int high){
int tmp=array[low];
while(low<high){
while(low<high && array[high]>=tmp){
high–;
}
array[low]=array[high];
while(low<high&&array[low]<=tmp){
low++;
}
array[high]=array[low];

    }
    array[low]=tmp;
    return low;
}

//找到基准以后,然后分左右进行递归
public static void quickSortchild(int []array,int start,int end){
    if(start>=end){
        return;
    }
    //1万个数字,等达到100就可以直接插入排序
    if(end-start+1<=100){//当数组达到一定的规模的时候,可以直接使用直接插入排序。
        insertSort2(array,start,end);
        return;//这里一定要返回,一旦有序就不用走下面的代码了。
    }
    int  mid=(start+end)/2;
    Sthree(array,start,mid,end);
    int pivot=partition(array,start,end);//经过三数取中后 才执行这段代码
    quickSortchild(array,start,pivot-1);
    quickSortchild(array,pivot+1,end);

/快速排序的优化 三数取中array[mid]<=array[start]<=array[end]
public static void Sthree(int [] array,int start,int mid,int end ){
//array[mid]<=array[start]<=array[end]
if(array[start]>array[end]){
swap(array[start],array[end]);
}
if(array[mid]>array[start]){
swap(array[mid],array[start]);
}
if(array[mid]>array[end]){
swap(array[mid],array[end]);
}

}
    //时间复杂度:最好情况下 n*log n 均匀的分割下  最坏n的平方
//空间复杂度:最好 log n  最坏N
//稳定性:不稳定
举报

相关推荐

0 条评论