个人主页:♡喜欢做梦 

欢迎 👍点赞 ➕关注 ❤️收藏 💬评论

一、排序

1.排序的概念

排序:将一组数据进行按照特定的顺序(如升序或者降序)进行排列的操作。

  • 稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,在排完序列后,r[i]仍在r[j]之前。这种排序叫做稳定的,否则为不稳定。

例如:

第1个3是在第二个3之前,排序后,两个位置发生前后变化,这种是不稳定性。如果前后排序没有发生变化那么是稳定性。 

2.常见的排序算法

二、插入排序

1.直接插入排序

基本思想

基本思想:把待排序的元素插入到已经排序好的部分序列中合适的位置,直到全部元素都排序好。例如:我们在整理手中的扑克牌的时候,我们将拿到的牌,将其插入前面排中合适的位置。

实现过程

以上有部分步骤省略,但影响不大 

思路:
  • i从1下标开始遍历,j从i-1开始;
  • 将下标i中的元素给tmp;
  • 将j下标的元素与tmp中的元素进行比较:
    如果j中的元素大于tmp中的元素,那么将j中的元素放到j+1的元素中
    如果j中的元素小于tmp中的元素,那么说明j+1是适合tmp元素的位置,将其插入,退出循环

代码: 

 public static  void insertSort(int[] array){
        for (int i = 1; i < array.length; i++) {
            //将下标i中的元素给tmp;
            int tmp=array[i];
            int j = i-1;
            for (; j >=0; j--) {
                //将j中的元素与tmp中的元素进行比较
                //如果j中的元素大于tmp中的元素,那么将j中的元素放到j+1的元素中
                if(tmp<array[j]){
                    array[j+1]=array[j];
                }else{
                    //否则说明j+1是适合tmp元素的位置,将其插入
                    array[j+1]=tmp;
                    break;
                }
            }
            array[j+1]=tmp;
        }
    }

可能会有的疑问:

1.为什么j的循环之后,还有写array[j+1]=tmp? 

       将j的下标中的元素给到j+1中,随后j--,这个时候j<0,不满足循环条件,那么退出循环,这个时候如果不在i循环中加array[j+1]=tmp。那么结果就只有13 13 23 12.没有将tmp插入到合适的位置中


2.为什么是将j下标的元素给j+1,而不是给i下标,j+1的元素不就是i下标的元素吗?

       在起始i的时候,执行j循环的时候,确实j+1下标,但是在每次指向完一次j的循环,j的下标就向前移动,而在j移动的过程中i是不变的。


3.为什么j要--,看图中好像我只要把前后两个元素进行笔记不就好了吗?

       在比较过程中,可以算是每次从该位置,从后开始比较,如果两个位置前后大小交换,那么在交换的元素的前后位置可能也要发生交换。

  • 空间复杂度:O(1)
  •  时间复杂度:O(N^2)
    最坏的情况下:逆序;
    最好的情况下:本身有序,越有序越快
  • 稳定性:稳定的排序

2.希尔排序(缩小增量排序)

基本思想

基本思想:先将整个待排序的数据元素,分成若干个组,并对每一组的元素进行排序。这些子序列的元素一开始间隔相对较大,随后逐步缩小间隔,继续进行插入排序,直到这个间隔=1时,所有元素在一组内在进行排序。

实现过程

除了在分组与直接插入排序的思想不同,但在排序的思想与直接插入排序的思想基本相同

以上有省略部分步骤,影响不大 

思路:
  • 分组:gap=gap/2,直到gap小于1不在排序
  • 排序:
  • i从gap下标开始遍历,j从i-gap开始;
  • 将下标i中的元素给tmp;
  • 将j下标的元素与tmp中的元素进行比较:
    如果j中的元素大于tmp中的元素,那么将j中的元素放到j+gap的元素中
    如果j中的元素小于tmp中的元素,那么说明j+gap是适合tmp元素的位置,将其插入,退出循环

细心的同学可以发现,其实思路只是把直接插入排序的1改成了gap;

代码: 

  public static  void shellSort(int[] array){
        int gap=array.length;
        while(gap>=1){
            gap=gap/2;
            //gap=gap/3+1;
            shell(array,gap);
        }
    }
public static  void shell(int[] array,int gap){
        for (int i =gap; i < array.length; i++) {
            //将下标i中的元素给tmp;
            int tmp=array[i];
            int j = i-gap;
            for (; j >=0; j-=gap) {
                //将j中的元素与tmp中的元素进行比较
                //如果j中的元素大于tmp中的元素,那么将j中的元素放到j+1的元素中
                if(tmp<array[j]){
                    array[j+gap]=array[j];
                }else{
                    //否则说明j+1是适合tmp元素的位置,将其插入
                    array[j+gap]=tmp;
                    break;
                }
            }
            array[j+gap]=tmp;
        }
    }

 可能会有的疑问:

不是说就是将直接插入排序中的1改成gap吗?为什么i是++,而不是加gap?

仔细看上面的图,我的箭头是向右的,而不是向下的。因为虽然他分组了,但是每一组的元素还是要遍历进行交换的,所以是i++。

  •  希尔排序是对直接插入排序的优化;
  • 稳定性:不稳定;
  • 时间复杂度:n^1.3~n^1.5;
  • 空间复杂度:O(1);

三、选择排序

1.选择排序

基本思想

基本思想:每一次从待排序的序列中找到最小(大)的元素,存放到排序序列中的起始位置,以此类推,直到全部待排序的数据元素排完。

方法1

实现过程:

纠错:以下图片mid为min

思路
  • i从头开始遍历,minIndex的起始位置从i开始;
  • j=i+1开始向后与minIndex中的元素进行比较,寻找当前最小的元素,记录最小元素下标到minIndex;
  • 将i位置的元素与minIndex的元素进行交换。

代码

    public static  void selectSort(int[] array){
        for (int i = 0; i < array.length; i++) {
            //记录最小元素下标
            int minIndex=i;
            for (int j=i+1; j < array.length ; j++) {
                //如果j下标的元素比mid的元素小,将j下标赋值给mid
                if(array[j]<array[minIndex]){
                    minIndex=j;
                }
            }
            //将i下标与当前的最小值进行交换
            swap(array,i,minIndex);
        }
    }
    public static  void swap(int[] array,int i,int j){
        int tmp=array[i];
        array[i]=array[j];
        array[j]=tmp;
    }

方法2

思路:
  • 设立left和right两个指针来确定数组区间,left从0开始向后移动,right从array.length-1向前移动,直到left大于等于right排序
  • 初始化minIndex和maxIndex为left,i下标从left+1开始,将i下标的元素分别与minIndex中的元素和maxIndex进行比较,寻找当前的最小值与最大值。
  • 每轮寻找完,将最小值与left下标进行交换,最大值与right进行交换,随后left向后移动,right向前移动
实现过程:

纠错:以下图片mid为min
 代码

    public static  void selectSort2(int[] array){
        int left=0;
        int right= array.length-1;
        while(left<right){
            int minIndex=left;
            int maxIndex=left;
            for (int i = left+1; i <=right ; i++) {
                if(array[i]>array[maxIndex]){
                    maxIndex=i;
                }
                if(array[i]<array[minIndex]){
                    minIndex=i;
                }
            }
            swap(array,left,minIndex);
            if(maxIndex==left){
                maxIndex=minIndex;
            }
            swap(array,right,maxIndex);
            left++;
            right--;
        }

    }
    public static  void swap(int[] array,int i,int j){
        int tmp=array[i];
        array[i]=array[j];
        array[j]=tmp;
    }

 可能会有的疑问:

为什么还要加下面这个代码?有什么用?

看上面的图,在left位置的时候,maxIndex已经是最大值的下标,在minIndex找到最小值的下标的时候,会将最小值与left位置进行交换,如果没有该代码,那么与maxIndex与right交换的元素,是交换后的最小值,而最大值被交换到原本最小值的位置,也就是minIndex。

  • 直接选择排序效率不高,用的少;
  • 稳定性:不稳定;
  • 时间复杂度:O(n^2);
  • 空间复杂度:O(1); 

2.堆排序

基本思想

基本思想:堆排序是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择排序。需要注意的是排升序要建立大堆,排降序要建立小堆。

堆排序的思路和图我就不在说啦,如果想看的话可以看我上一篇还在死磕算法?懂“堆”与“优先级队列”,代码效率飙升 中有写到堆排序

 public void heapSort(){
        int end=usedSize-1;
        while(end>0) {
            swap(elem, 0, end);
            siftDown1(0,end);
            end--;
        }
  • 效率会高一些;
  • 稳定性:不稳定;
  • 时间复杂度:O(n*\logN) ;
  • 空间复杂度:O(1);

这篇博客就到这里啦,其他排序内容有点多,我下篇在写,感谢支持🌹🌹🌹

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐