排序的深度剖析

文章目录

  • 系列文章目录
  • 1.插入排序:(1)直接插入排序
  • 2.选择排序:(1)选择排序
  •                        (2)堆排序
  • 3.交换排序     (1)冒泡排序
  •                          (2)快速排序
  • 4.归并排序
  •                         

这里所有的排序都是指从小到大的排序

稳定性:

指排序后相同元素的 相对位置不变

1.直接插入排序

简而言之就是给每个数据找一个合适的位置给放下

时间复杂度为:O(N2)

空间复杂度为:O(1)

稳定性:稳定

public static void insert(int[]arr,int left,int right)
    {
        int i=0;
        for(i=left+1;i<=right;i++)
        {
            int j=i-1;
            int tmp=arr[i];
            while(j>=0)
            {
                if(arr[i]<arr[j])
                {
                    arr[j+1]=arr[j];
                    j--;
                }
                else
                {
                    arr[j+1]=tmp;
                }
            }
            arr[0]=tmp;
        }
    }

2.选择排序

时间复杂度为O(N2)

空间复杂度为O(1)

稳定性:不稳定

(1)选择排序

    一共两种思路

第一种思路是遍历数组:依次找到最小的数据把它放在前面

public static void seletSort(int[] arr) {
        int i = 0;
        for (i = 0; i < arr.length; i++) {


            //int j = i + 1;
            int min = i;
            for(int j=i+1;j<arr.length;j++) {
                if(arr[j]<arr[min])
                {
                    min=j;
                }



            }


            if (min != i) {
                int tmp = arr[min];
                arr[min] = arr[i];
                arr[i] = tmp;
            }

        }
    }

第二种思路是

从前往后找最大的,从后往前找最小的,然后把最小的放在最左边,然后把最大的放在最右边,依次类推

public static void seletsort2(int[]arr)
    {
        int left=0;
        int right=arr.length-1;
        while(left<right)
        {
            int min=left;
            int max=left;
            int i=0;
            for(i=left+1;i<=right;i++)
            {
                if(arr[i]<arr[min])
                {
                    min=i;

                }
                if(arr[i]>arr[max])
                {
                    max=i;
                }
            }
            swap(arr,min,left);
            if(max==left)
            {
                max=min;
            }
            swap(arr,max,right);
            //swap(arr,min,left);
            //arr[left]=arr[min];
            //arr[right]=arr[max];
            left++;
            right--;
        }
    }

这里有一个点我们需要注意:当最前边的元素最大时,

此时left在0位置,right在4的位置,遍历完后此时最小值是1,下标是3,最大值是9在0下标,我们交换后那么最大值就在3 的位置,

此时最大值下标在最小值小标上,我们这时只需将max=min,将最大值下标更新一下就行了。 

3.堆排序

时间复杂度为:O(N*log2N)

空间复杂度:O(1)

稳定性:不稳定

我们可以建立成一个大根堆,然后将头顶元素依次(每个头顶元素都是当前大根堆里最大的元素)和最后一个元素交换

public static void creat()    //如果是从小到大排序的话,就建立一个大根堆
{
    for(int parent=(usesized-1-1)/2;parent>=0;parent--)
    {
        shit(usesized,parent);
    }

}
public static void shit(int len,int parent)
{
    int child=2*parent+1;
    while(child<len)
    {
        if(child+1<len&&elem[child]<elem[child+1])
        {
            child++;

        }


            if(elem[child]>elem[parent])
            {
                int tmp=elem[child];
                elem[child]=elem[parent];
                elem[parent]=tmp;
                parent=child;
                child=2*parent+1;
            }
            else
            {
                break;
            }

    }
public void paixu() {
    while (usesized >= 1) {
        int tmp1 = elem[0];
        elem[0] = elem[usesized - 1];
        elem[usesized - 1] = tmp1;
        usesized--;

        shit(usesized, 0);


    }
}

3.交换排序:

(1)冒泡排序:

    时间复杂度为O(N2)

    空间复杂度为O(1)

     稳定性:稳定

  

 public static void maopao(int[]arr)
    {
       int i=0;
       for(i=0;i<arr.length-1;i++)
       {
           int j=0;
           boolean a=false;
           for(j=0;j<arr.length-1-i;j++)
           {
               if(arr[j]>arr[j+1])
               {
                   swop(arr,j);
                   a=true;
               }
           }
           if(a==false)   //说明此时序列已经有序
           {
               return;
           }
       }
    }

   (2)快速排序

         时间复杂度:O(N)

        空间复杂度:O(log2N)

        稳定性:不稳定

            Hore版

         

先把6作为基准,R先从右边找比6大的,找到停下,然后L再从左边找,找到比6 小的,然后交换,然后R再找找完L再找,直到两者相遇,此时将相遇点的值和基准交换,此时我们可以观察到6的左边都是比6小的,6的右边都是比6大的,然后我们分而治之

直到l r重合,返回。

 public static int findmid2(int[]arr,int left,int right)
    {
        int tmp=arr[left];
        int c=left;
        while(left<right)
        {
            while(left<right&&arr[right]>=tmp)
            {
                right--;
            }
            while(left<right&&arr[left]<=tmp)
            {
                left++;
            }
           swop(arr,left,right);


        }
        swop(arr,c,left);
        return left;

    }
    public static void quiklysort(int[]arr,int left,int right)
    {
        if(left>=right)
        {
            return;
        }
        int pre=findmid2(arr,left,right);
        quiklysort(arr,left,pre);
        quiklysort(arr,pre+1,right);
    }

 2.挖孔法:

最后相遇的时候把6在放入这个坑位。

public static void quklysort2(int[]arr,int left,int right)
    {
        if(left>=right)
        {
            return;
        }

       int b= findmid(arr,left,right);
        //int tmp=arr[b];
        //arr[b]=arr[left];
        //arr[left]=tmp;
        int pre=find1(arr,left,right);
        quklysort2(arr,left,pre-1);
        quklysort2(arr,pre+1,right);


    }
public static int find1(int[]arr,int left,int right)
    {
        int tmp=arr[left];
        while(left<right)
        {
            while(left<right&&arr[right]>=tmp)
            {
                right--;
            }
            arr[left]=arr[right];
            //left++;
            while(left<right&&arr[left]<=tmp)
            {
                left++;
            }
            arr[right]=arr[left];
            //right--;

        }
        arr[left]=tmp;
        return left;
    }

 我们在解题的时候先考虑挖坑法,再用Hore版

快排的优化

我们知道数据是越来越有序的,那么我们如果在递归多的区间进行选择排序,那么快排就会变快

像这个其实递归多的地方主要在下边 

 public static void quklysort4(int[]arr,int left,int right)
    {
        if(left>=right)
        {
            return;
        }
        if(right-left>=2)
        {
            selectsort3(arr,left,right);
        }
        int pre=find1(arr,left,right);
        qiuklysort(arr,left,pre-1);
        qiuklysort(arr,pre+1,right);
    }

像这样一组数据,我们如果还是始终以第一个数据为基准的话,就会排成这样

时间复杂度也会变为O(N2),空间复杂度会变为O(N)那么什么办法去解决这个问题吗?

我们用三数取中法来解决这个问题:

我们将left 、right  以及  (left+right )/2下标的数值进行比较,找将它作为基准,这样就可以使基准左右都有数据。

 public static int  findmi(int[]arr,int left,int right)
    {
        int mid=(left+right)/2;
        if(arr[left]>arr[right])
        {
            if(arr[mid]>arr[left])
            {
                return left;
            }
            else if(arr[mid]<arr[right])
            {
                return right;
            }
            else
            {
                return mid;
            }
        }
        else
        {
            if(arr[mid]>arr[right])
            {
                return right;
            }
            else if(arr[mid]<arr[left])
            {
                return left;
            }
            else
            {
                return mid;
            }
        }

    }
    public static void quiklysort(int[]arr,int left,int right)
    {
        if(left>=right)
        {
            return;
        }
        if(left-right>=2)
        {
            insertpar(arr,left,right);
        }
        int mid=findmi(arr,left,right);
        swop(arr,mid,left);
        int pre=findmid2(arr,left,right);
        quiklysort(arr,left,pre);
        quiklysort(arr,pre+1,right);
    }

 快排的非递归方法

建立一个栈,排序找基准,然后将基准两侧的left,right分别放入栈中,(这里要注意当基准一侧只有一个元素时,说明基准的这一侧已经有序,这一侧的left和right不需要再放入栈中),然后每次再弹出两个坐标,再后再排序,找基准再将基准放入栈中,再弹出,以此类推,直到栈为空

 

 public static void fei(int[]arr)
    {
        Stack<Integer>stack=new Stack<>();
        int left=0;
        int right=arr.length-1;
        int mid=find(arr,left,right);
        if(left<mid-1)
        {
            stack.push(left);
            stack.push(mid-1);

        }
        if(mid<right-1)
        {
            stack.push(mid+1);
            stack.push(right);
        }
        while(!stack.isEmpty())
        {
             right=stack.pop();
             left=stack.pop();
            mid=find(arr,left,right);
            if(left<mid-1)
            {
                stack.push(left);
                stack.push(mid-1);

            }
            if(mid<right-1)
            {
                stack.push(mid+1);
                stack.push(right);
            }
        }
    }
    public static int find(int[]arr,int left,int right)
    {
        int tmp=arr[left];
        int i=left;
        while(left<right)
        {
            while(left<right&&arr[right]>=tmp)
            {
                right--;
            }
            while(left<right&&arr[left]<=tmp)
            {
                left++;
            }
            swop(arr,left,right);

        }
        swop(arr,left,i);
        return left;

    }
    public static void swop(int[]arr,int left,int right)
    {
        int tmp=arr[left];
        arr[left]=arr[right];
        arr[right]=tmp;
    }

 归并排序:

时间复杂度O(N*logN)

空间复杂度O(N)

稳定性:稳定

合并排序的思路就是:先把序列一步步的分解,分解成单个,然后再返回,再将子序列逐步地排序

最后就能形成有序的序列

//归并public static void fen(int[]arr,int left,int right)
{
    if(left==right)
    {
        return ;
    }
    int mid=(left+right)/2;
    fen(arr,left,mid);
    fen(arr,mid+1,right);
    paixu(arr,left,mid,right);
}
public static void paixu(int[]arr,int left,int mid,int right)
{
    int[]arr2=new int[right-left+1];
    int i=left;
    int j=mid+1;
    int k=0;
    while(i<=mid&&j<=right)
    {
        if(arr[i]<=arr[j])
        {
            arr2[k++]=arr[i++];
        }
        else
        {
            arr2[k++]=arr[j++];
        }
    }
    while(i<=mid)
    {
        arr2[k++]=arr[i++];
    }
    while(j<=right)
    {
        arr2[k++]=arr[j++];
    }
    for(i=0;i<k;i++)
    {
        arr[i+left]=arr2[i];
    }


}

不用比较的排序:

计数数组:(适用于集中的数)

举个例子假设有一个数组arr里面都是0-9的数,那么如果我们再定义一个大小为10的数组,然后把arr里面的每个数当成计数数组的下标,计数数组每次下标相同时这个下标元素的值都要加加

假设int[]arr={0,0,1,3,2,1,4,5,5,5,6,7,8,9};

我们在定义一个计数数组

每个count[i]都表示arr数组这个下标的元素有几个,这样我们就按照下标训序打印,但是注意个数,比如0 有两个,就打印两次

//计数排序public static void count(int[]arr)
{
    int i=0;
    int min=arr[0];
    int max=arr[0];
    for(i=1;i<arr.length;i++)
    {
        if(arr[i]<min)
        {
            min=arr[i];
        }
        if(arr[i]>max)
        {
            max=arr[i];
        }

    }
    int size=max-min+1; //确定计数数组的大小
    int[]arr2=new int[size];
    for(i=0;i<arr.length;i++)
    {
        arr2[arr[i]-min]++;//处理如果数据不是0-9这样类似的数据,这时让每个数据减去最小值就0k
    }
    for(i=0;i<size;i++)
    {
        while(arr2[i]>=1)
        {

            System.out.print(i+min+" ");//打印的时候再加上min就变成原数了
            arr2[i]--;
        }


    }


}


  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值