几种排序的总结(包括含有重复数字的快速排序)

2023-11-08

1.冒泡排序

冒泡排序(Bubble Sort):一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
假设一个无序数组的长度N,利用冒泡排序将其从小到到进行排序:
基本思路:
1)要进行N - 1趟,此处趟数用i来表示
2)每一趟中要进行N - i次(i表示第几趟,从而表明了趟数 + 每一趟中进行交换的次数 == N
3)进行前后比较,如果顺序错误,那么就要将其交换过来
在这里插入图片描述由图可以知道,一共有7个数,最多进行了 7 - 1 = 6 趟,在第i趟中进行了N - i次交换

代码实现:

  for(int i = 0; i<arr.length - 1; i++){
              for(int j = 0; j<arr.length - i-1; j++){
                  if(arr[j+1] < arr[j]){//如果顺序错误,那么进行交换
                      int temp = arr[j+1];
                      arr[j+1] = arr[j];
                      arr[j] = temp;
                  }
              }
        }

冒泡排序的优化:由上面可以知道,走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。所以只要有一个标记flag,在每一趟中都没有进行过一次交换,那么说明了数组有序了,不需要再进行下一趟的交换。

        for(int i = 0; i<arr.length - 1; i++){
              flag = false;//注意要将flag重置为false
              for(int j = 0; j<arr.length - i-1; j++){
                  if(arr[j+1] < arr[j]){
                      int temp = arr[j+1];
                      arr[j+1] = arr[j];
                      arr[j] = temp;
                      flag = true;//说明已经交换
                  }
              }
              if(flag == false)//如果这一趟没有进行过交换,那么就说明了这一次排序使得数字有序,退出循环
                  break;
        }

2.选择排序

选择排序(SelectSort):一种简单直观的排序算法,无论什么数据进去都是 O(n²) 的时间复杂度。所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。
算法步骤
1)在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
2)再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
3)重复第二步,直到所有元素均排序完毕。
在这里插入图片描述
说明:
1)最多进行了arr.length - 1趟交换
2)在每一趟排序中,需要找到最小值,然后和当前这个数进行比较,如果当前的值比最小值要大,那么要交换,否则不用(此处是按照升序进行排序),进入下一趟排序(就好比上面图中的20,要遍历其后面的数,找到最小值,然后将其交换)

代码实现:

   int minIndex;//最小值对应的下标
   int min;//表示最小值
   for(int i = 0; i<arr.length; i++){
            minIndex = i;
            min = arr[i];
            for(int j = i+1; j<arr.length;j++){
                if(arr[j] < min) {
                    minIndex = j;//表示最小值的下标
                    min = arr[j];//表示最小值
                }
            }
            if(minIndex != i){
                //如果min不是i,那么就说明当前下标的后面还有数比当前的下标对应的数字要小,所以进行交换
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }

3.插入排序

插入排序(InsertSort):
插入排序算法思想:每趟将一个元素,按照其关键字的大小插入到它前面已经排序的子序列中,依此重复,直到插入全部元素。
步骤:
1)将从下标为1的数字开始遍历,将当前的数字赋给临时变量temp
2)将temp和当前下标(temp对应的下标)之前的数字进行比较,如果temp大于等于前面的数字y,那么表明了y的后面就是temp要插入的位置否则就将前面的数字后移

代码实现:

//这个代码是一边输入,然后一边进行插入排序的
 for(int i = 0; i<arr.length; i++) {
            arr[i] = (int)(Math.random()*800);//产生8000个[0,800)的随机数
            temp = arr[i];//将arr[i]赋给临时变量num,然后拿num这个临时变量和arr[i]之前的元素进行比较(要根据需求来比较)
            for(j = i - 1; j>=0; j--){
                if(arr[j] <= temp)//当num这个临时变量比arr[j]要大,那么就说明j后一个元素就是num
                    break;
                arr[j + 1] = arr[j];//否则就要将i前面的元素后移
            }
            arr[j+1] = num;//注意是j+1,而不是j,因为num比j下标对应的值大或者相等
        }

//先输入完毕,再进行排序
         for(int i = 1; i<arr.length; i++) {
            num = arr[i];//将arr[i]赋给临时变量num,然后拿num这个临时变量和arr[i]之前的元素进行比较
            for(j = i - 1; j>=0; j--){
                if(arr[j] <= num)//当num这个临时变量比arr[j]要大,那么就说明j后一个元素就是num
                    break;
                arr[j + 1] = arr[j];//否则就要将i前面的元素后移
            }
            arr[j+1] = num;//注意是j+1,而不是j,因为num比j下标对应的值大或者相等
        }

可能大家会疑惑,为什么要将当前的数字赋给临时变量temp呢?如果不赋的话(那下面的代码说明一下),如果arr[i]不赋值给temp,那么如果arr[i]小于其前面的数字,那么会后移,arr[i]的值就变了,所以需要将arr[i]赋值给temp

4.折半插入排序

折半插入排序的原理和折半查找类似的,具体的定义可以百度进行搜索。
所以根据它的定义,我们在一个有序数组中找到新元素插入的位置t,然后将这个插入位置后的元素往后移,从而给这个新元素插入腾出一个位置,然后将新元素插入到位置t,从而实现有序.
具体的代码:

public class Demo01 {
    public static void main(String[] args) {
        int[] arr = new int[]{8,7,6,9,0,1,3,635,34232,123,89,8};
        System.out.print("没有进行排序时:");
        display(arr);
        binarySort(arr,arr.length);//调用方法,从而进行折半插入排序
        System.out.print("进行排序之后:");
        display(arr);
    }
    public static void display(int[] arr){
        for(int num: arr){
            System.out.print(num + " ");
        }
        System.out.println();
    }
    public static void binarySort(int[] arr,int n){
        int i,j,tmp;//tmp用于存放临时变量
        //low表示的下标为i的前面有序数组的起点下标,high表示的是下标为i之前的有序数组的起点终点
        int low,high,mid;//mid表示子数组中的中间下标
        for(i = 1; i < n; i++){
            tmp = arr[i];
            low = 0;
            high = i - 1;
            while(low <= high){
                //如果至少含有一个元素的时候就继续循环,进行比较,获得tmp要插入的位置
                mid = (low + high) / 2;
                /*
                如果代码这样写的话,会怎样咧?
                事实上,如果输入的数组没有重复的数字,那么的确没有什么影响
                但是输入的数组含有重复数字的话,那么就没有办法更新low或者
                high的值了,进而会陷入死循环中,从而造成超时
                if(arr[mid] > tmp) 
                    high = mid - 1;
                else  if(arr[mid] < tmp) 
                    low = mid + 1;
                */
                if(arr[mid] > tmp){ //如果tmp小于中间值,就说明tmp在左边,所以更改high的值
                    high = mid - 1;
                }else { 
                /*
                 否则,如果tmp大于等于中间值,说明tmp在右边,更改low的值
                 (一定是else,没有else if了,如果是else if的话,会造成死
                 循环)
                */
                    low = mid + 1;
               }
            }
            /*当low> high,这时候就意味着没有了元素,这时候可以退出循环
            了,并且high的后面都是大于tmp的元素(也就是说high + 1这个
            下标对应的数是最后一个大于tmp的数),因此需要将这些元素后移,
             这里的for循环之所以是从j = i - 1开始,是因为下标为i前面的有
             序数组是[0 , i - 1],那么我们现在需要将大于等于tmp的元素往后
             移,给tmp插入腾出位置,所以是从i - 1开始遍历,往后移,直到
             j = low - 1的时候结束后移)
             
             当然,这里的for循环的循环判断条件也可以是j >= low,因为退出
             while循环的时候,low必然大于high,并且low - high = 1(这个结
             论大家可以尝试者用一组数据进行证明,例如1 2 3 4 5 6 7,
             如果当前下标i对应的值是7,也就是说他前面的有序数组有6个数,
             如果当前下标i对应的值是6,那么他前面的有序数组有5个数)
             */
            for(j = i - 1; j >= low; j--)
                arr[j + 1] = arr[j];
            arr[j+1] = tmp;

        }
    }
}

运行结果:
在这里插入图片描述

4.希尔排序

希尔排序(ShellSort):也是一种插入排序,他是简单插入排序改进之后的一个更加高效的版本,也称为缩小增量排序。

算法描述:
1)将一个数据序列分成若干组,每组由若干相隔一段距离(称为增量)的元素组成,在一个组内采用直接插入排序算法进行排序。

2)增量初值通常为数据序列长度的一半,以后每趟增量减半,最后值为1,此时就成为了直接插入排序了。随着增量逐渐减小,组数也减小,组内元素个数增加,数据序列接近有序。
在这里插入图片描述
当增量gap变成了1,此时就只有一组,变成了直接插入排序(增量为1),当gap 为0时,数组实现了有序。

代码实现:

        int gap= arr.length / 2;//步长
        int j,temp;
        while(gap != 0){//当gap等于0的时候,退出循环,数组实现有序
            for(int i = gap; i<arr.length; i++){
                temp = arr[i];//将arr[i]赋给一个临时变量,然后和其同一组中并且在i前面的数字进行比较
                for(j = i-gap; j>=0 ; j-=gap){//这一步近似于插入排序,将temp和i前面的而且是同一组的数字进行比较
                    if(arr[j] <= temp){//此处是升序排序
                        break;
                    }
                    arr[j+gap] = arr[j];//如果temp小于arr[j]的值,那么后移(注意这里不是arr[j + 1] = arr[j]了哈,因为要在每一组中进行直接插入排序嘛)
                }
                arr[j+gap] = temp;//注意这一步是arr[j + length],为什么不是j呢?上面的插入排序已经说明清楚了,这里就不说了哈
            }
            gap /= 2;
        }

5.归并排序

归并的含义:将两个或两个以上的有序表合并成一个新的有序表。

归并排序(MergeSort):采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

在这里插入图片描述

递归实现归并排序

其算法思路如下:

1.如果给的数组只有一个元素的话,直接返回(也就是递归到最底层的一个情况)—作为结束递归的条件

2.把整个数组分为尽可能相等的两个部分(分)

3.对于两个被分开的两个部分再次进行操作2(也就是递归)

4.把两个被分开且排好序的数组拼接在一起

代码实现:

这是我本来的代码,后来发现如果有重复的数字,就不可以,会发生报错,所以在这里标记一下,既是给自己一个提醒,也给大家一个提醒,希望大家不要像我一样犯这样的错误,哈哈哈~~~

import java.util.Scanner;

public class MergeSort {
    public static int[] arr = new int[10];
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        for(int i = 0; i<arr.length; i++)
            arr[i] = sc.nextInt();
        merge(); //调用方法,从而新建一个新的临时数组,将子数组放到这个临时数组中,然后将临时数组赋值给原来的数组,从而排好序的
        System.out.println("进行排序之后:");
        for(int a: arr){
            System.out.print(a+ " ");
        }
        System.out.println();
    }
    public static void merge(){
        //建立一个新数组,从而将子数组复制个这个数组,将所有的子数组进行归并之后,将这个数组赋值给原来的数组即可
        int[] array = new int[arr.length];
        mergeSort(array,0,arr.length - 1);
    }
    public static void mergeSort(int[] array, int n, int m){
        if(n >= m){
            //如果n>m,那么范围不对,如果相等,就说明只有一个元素,此时不需要进行交换
            return;
        }
        int index = (n + m ) / 2;//将数组分成两部分
        mergeSort(array,n,index);
        mergeSort(array,index + 1, m);//利用递归,将一个数组分成两个子数组
        merge(array,n,index,m);//将两个子数组进行归并
    }
    public static void merge(int[] array,int lower,int mid, int upper){
        int n = upper - lower + 1;//统计这个子数组中有多少个元素
        int lowerBound = lower;
        int lower2 = mid + 1;//右子数组的最小下标
        int j = 0;
        while(lower <= mid && lower2 <= upper){
            //当两个子数组都有元素的时候,则将两者的元素进行比较,谁小的谁先插入到新的数组中
            if(arr[lower] <=arr[lower2]) {
                array[j++] = arr[lower];
                lower++;
            }else{
                array[j++] = arr[lower2];
                lower2++;
            }
        }
        //考虑子数组的长度不一样的情况
        //当左子数组没有遍历完的时候
        while(lower <= mid){
            array[j++] = arr[lower++];
        }
        //当右子数组没有遍历完的时候
        while(lower2 <= upper){
            array[j++] = arr[lower2++];
        }
        //将已经排好序的新数组赋值给原来的数组
        for(j = 0; j<n; j++,lowerBound++)
            arr[lowerBound] = array[j];
    }
}

改正后,实现数组含有重复数字的情况的代码:

//有重复的数字都可以进行排序的代码(改正了)
import java.util.Scanner;

public class MergeSort {
    public static int[] arr = new int[10];
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        for(int i = 0; i<arr.length; i++)
            arr[i] = sc.nextInt();
        merge(); //调用方法,从而新建一个新的临时数组,将子数组放到这个临时数组中,然后将临时数组赋值给原来的数组,从而排好序的
        System.out.println("进行排序之后:");
        for(int a: arr){
            System.out.print(a+ " ");
        }
        System.out.println();
    }
    public static void merge(){
        //建立一个新数组,从而将子数组复制个这个数组,将所有的子数组进行归并之后,将这个数组赋值给原来的数组即可
        int[] array = new int[arr.length];
        mergeSort(array,0,arr.length - 1);
    }
    public static void mergeSort(int[] array, int n, int m){
        if(n >= m){
            //如果n>m,那么范围不对(就好比区间[2,3],那么2肯定是比3小的,所以左边的一定比右边的小)如果相等,就说明只有一个元素,此时不需要进行交换
            return;
        }
        int index = (n + m ) / 2;//将数组分成两部分
        mergeSort(array,n,index);
        mergeSort(array,index + 1, m);//利用递归,将一个数组分成两个子数组
        merge(array,n,index,m);//将两个子数组进行归并
    }
    public static void merge(int[] array,int lower,int mid, int upper){
        int n = upper - lower + 1;//统计这个子数组中有多少个元素
        int lowerBound = lower;
        int lower2 = mid + 1;//右子数组的最小下标
        int j = 0;
        //将两个子数组进行归并
        while(lower <= mid && lower2 <= upper){
            //当两个子数组都有元素的时候,则将两者的元素进行比较,谁小的谁先插入到新的数组中,从而实现升序排序
            if(arr[lower] <=arr[lower2]) {//如果左子数组的数小于等于右子数组的数,那么先将左子数组的数插入(注意这里是小于等于,要考虑出现重复的数字的情况)
                array[j++] = arr[lower];
                lower++;
            }else {
                array[j++] = arr[lower2];
                lower2++;
            }
        }
        //考虑子数组的长度不一样的情况
        //当左子数组没有遍历完的时候
        while(lower <= mid){
            array[j++] = arr[lower++];
        }
        //当右子数组没有遍历完的时候
        while(lower2 <= upper){
            array[j++] = arr[lower2++];
        }
        //将已经排好序的新数组赋值给原来的数组
        for(j = 0; j<n; j++,lowerBound++)
            arr[lowerBound] = array[j];
    }

非递归方式实现归并排序(C语言)

#include<stdio.h>
#define MAX_SIZE 100
int array[MAX_SIZE];
void display(int arr[],int n){
     int i;
     for(i = 0; i < n; i++){
        printf("%d ",arr[i]);
     }
     printf("\n");
}
void copy_arr(int arr[],int n){
     int i;
     for(i = 0; i < n; i++)
        arr[i] = array[i];
}
void merge(int arr[],int low,int left_end,int high){
     int index = low; //这一步是必须的,而不是从0开始
     int right_low = left_end + 1; //获取右子数组的起点下标
     while(low <= left_end && right_low <= high){
        if(arr[low] <= arr[right_low]){
            array[index++] = arr[low++];
        }else{
            array[index++] = arr[right_low++];
        }
     }
     while(low <= left_end){
        array[index++] = arr[low++];
     }
     while(right_low <= high){
        array[index++] = arr[right_low++];
     }
}
/*
利用迭代的方式,实现归并排序:
1、首先定义步长length = 1,表示子数组的长度。
2、从下标i为0开始遍历,在[i,i + length - 1],[i + length,i + 2 * length - 1]
这两个子数组调用merge方法,从而将这两个子数组进行归并排序
3、下标i不断自增,只是应该是 i += 2 * length,表示下一个子数组的起点下标
重复步骤2
4、重复步骤2,3,直到i > N - 2 * length
之所以N - 2 * length,是因为i = N - 2 * gap是最后一个满足这一次归并的下标,
如果i = N - 2 * length的话,那么左子数组[N - 2 * length,N - length - 1],
右子数组为[N - length,N - 1]
5、退出步骤4之后,需要比较i + gap和N的大小关系,如果i+length <= N,说明
这时候有两个子数组进行合并,需要再次进行合并,调用方法
merge,否则,就将array复制到arr,完成一次归并排序
6、重复上述步骤,直到length >= N
*/
void mergeSort(int arr[],int n,int length){
     int i;
     for(i = 0; i <= n - length * 2; i += 2 * length){
            //左子数组[i,i + length - 1],右子数组[i + length,i + 2 * length - 1]
       // printf("i = %d,i + length - 1 = %d, i + 2 * length - 1 = %d\n",i,i + length - 1,i + 2 * length - 1);
        merge(arr,i,i + length - 1,i + 2 * length - 1);
     }
     if(i + length < n){
     //如果这一步中符合条件,说明这时候有2个子数组需要进行归并排序
        merge(arr,i,i + length - 1,n - 1);
     }else{
       /*
       这一步是必须的,如果没有这一步,那么就会导致输出的时候有一部分数据
       发生丢失,这时候之所以需要这样,是因为考虑到最后有些有些数据并没有
       进行归并的情况,这里例子挺好的(输入的是一个奇数个数的时候)
       输入21个数:1 7 4 6 9 8 2 0 3 5 10 15 16 14 18 19 17 16 14 11 20,
       如果没有这一步,那么最后结果并不是升序排序
       */
       
       for(; i < n; i++){
         array[i] = arr[i];
       }
   }
     copy_arr(arr,n); //注意,这里不需要else,直接将array数组赋值给arr即可
     display(arr,n);
}
void sort(int arr[],int n){
    int length = 1;//定义子数组长度(理解这个含义,才可以明白mergeSort中调用merge为什么是那样子)
    while(length < n){
        //当子数组的长度小于n的时候,那么需要将子数组进行归并
      //  printf("length = %d\n",length);
        mergeSort(arr,n,length);
        length *= 2;//完成一次归并之后,下一次循环中,子数组的长度就是length * 2了
    }
}
int main(){
  int arr[MAX_SIZE];
  int n,i,j,k,left_index,right_index,num,index;
  scanf("%d",&n);
  for(i = 0; i < n; i++)
    scanf("%d",&arr[i]);
  for(i = 0; i < n; i++)
    array[i] = 0;
  sort(arr,n);
  return 0;
}

运行结果:
在这里插入图片描述

6.快速排序

快速排序(QuickSort): 快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为较小和较大的2个子序列,然后递归地排序两个子序列。

步骤为:

1、挑选基准值:从数列中挑出一个元素,称为“基准”(pivot),此处选择数组中间的那个数作为基准。即arr[index] = pivot,此处的index 是数组中间数的下标

2、分割:重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。在这个分割结束之后,已经将数组分割成了两个子数组,左子数组都是小于基准的值,右子数组都是大于基准的值

那么实现分割操作呢?
基准值arr[ index ] 赋给临时变量pivot,然后分别从两边进行遍历如果左边遇到第一个比pivot大的数,右边遇到第一个比pivot小的数,那么将两者进行交换

有同学可能又会问这里为什么又要将arr[index]赋给临时变量pivot呢?
因为在分割的过程中进行了交换,可能会使arr[index]的值发生变化,从而导致错误。比如一个例子 :一个数组{5,9,2,6,0,1,2} ,那么index = (0 + 6)/2,有pivot为6,那么从两边进行遍历,从而实现分割得到{5,2,2,1,0,6,9},那么如果不将arr[index]赋给pivot,那么arr[index]对应的值会发生改变,变为1,而不再是6了

3、递归排序子序列:通过递归,将左右两个子数组进行排序(重复上面1、2操作)-----结束递归的条件是当数组只有一个元素的时候,结束递归,此时实现有序

代码实现:

这是一个片面的代码:只能将一个不含有重复数字的数组进行排序,如果数组含有重复的数字,就会发生报错,在这里做个标记,提醒自己,也希望能够提醒大家,希望大家不要像我一样犯同样错误哦,哈哈哈~~~~

import java.util.Scanner;

public class QuickSort {
    public static int[] arr = new int[10];

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        for (int i = 0; i < arr.length; i++)
            arr[i] = sc.nextInt();
        //调用方法进行快速排序
        quickSort(0, arr.length - 1);
        for (int a : arr)
            System.out.print(a + " ");
        System.out.println();
    }

    public static void quickSort(int n, int m) {
        if (n < m) {//因为m的下标是右边的,那么必然是大于n的,就说明不只一个元素,此时进行交换
            //定义基准的下标
            int index = (m + n) / 2;
            int pivot = arr[index];//将中间的数作为基准,并将其值赋给临时变量pivot
            int m2 = m, n2 = n;
            int partition = getPartition(n,m,pivot);//分割,其中返回的值是分割的值的下标
            //利用递归,将左右两个子数组进行排序
            quickSort(n2, partition-1);//左子数组
            quickSort(partition+1, m2);//右子数组
        }else{
            //如果n等于m,就说明了只有一个元素,若大于m,那么范围不对(就好比区间[2,3],那么2肯定是比3小的,所以左边的一定比右边的小)
            return;
        }
    }
    public static int getPartition(int n, int m, int pivot){
        //将数组分成两个子数组,其中左子数组的值小于中间枢纽pivot的值,而右子数组的值大于中间枢纽的值
        while (true) {
            //当n>=m,说明了左边都是小于标注的值,这时候就可以退出循环
            while (n<m && arr[n] < pivot) {
                //需要在n<m的前提下进行判断,如果左边是小于标准的值,那么就右移,直到找到第一个大于标准的值的下标
                //注意这里需要有n<m,否则有可能n一直移到最右边,那么很容易发生溢出,从而会报错
                n++;
            }
            while (n<m && arr[m] > pivot) {
                //需要在n<m的前提下进行判断,如果右边找到了第一个小于标准的值,那么就退出循环,否则往左移
                //注意这里需要有n<m,否则有可能m一直移到最左边,那么很容易发生溢出,从而会报错
                m--;
            }
            if (n >= m) {
                //相遇了,那么就退出循环,说明基准的左边都是小于基准的数,右边都是大于基准的数,此时基准的下标为n或m,而不是原来的index
                break;
            } else {
                swap(n,m);
            }
        }
        return n;//返回基准的下标
    }
    public static void swap(int n , int m){
        //没有相遇,将二者进行交换
        int temp = arr[n];
        arr[n] = arr[m];
        arr[m] = temp;
    }
}

含有重复数字的快速排序的情况分析: 这里利用三向切分进行分析的。也就是将数组分为三部分:小于当前切分元素的部分,等于当前切分元素的部分,大于当前切分元素的部分。

步骤:
1)每次切分从数组的左边到右边遍历一次,同时第一个数对应的值作为基准pivot,然后遍历,将基准后一个数字进行比较
①如果基准比其后的一个数字要大,那么直接将两者进行交换,之后将基准的下标更改,随之的其后的数字的下标也要更改
②如果基准比其后的一个数字要小,那么将其后的数字和数组最后的数字要交换,此时要将原来数组最后一个数字的下标要更改,然后再进行比较,如果还是基准的值小,那么重复②的操作,直到出现另外两种情况
③如果基准的值和其后一个数字相等,那么将基准后一个数的下标后移,注意这里的后移并不是指基准的下标的后移,而是其后的数字进行后移,下面的图可以解释

在这里插入图片描述
2)每次切分之后,位于gt指针和lt指针之间的元素的位置都已经被排定,不需要再去移动了。之后将(lo,lt-1),(gt+1,hi)分别作为处理左子数组和右子数组的递归函数的参数传入,递归结束,整个算法也就结束。
在这里插入图片描述
这时候我们对左子数组进行分析:
在这里插入图片描述

代码实现:

import java.util.Scanner;

public class QuickSort2 {
    public static int[] arr = new int[10];
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        for(int i = 0; i<arr.length; i++)
            arr[i] = sc.nextInt();
        System.out.println("进行快速排序之后:");
        quickSort(0,arr.length - 1);
        for(int a: arr){
            System.out.print(a+" ");
        }
        System.out.println();
    }
    public static void quickSort(int n, int m){
        if(n >= m){
            //如果只有一个元素或者范围不正确,就结束递归
            return;
        }else{
            int lo = n, lt = lo + 1, gt = m;//lo表示基准的下标,lt表示基准后的数字得下标,gt表示数组最后一个数的下标
            int pivot = arr[lo];//将每一个数组的第一个数作为基准
            //遍历基准后的数,如果比基准小的,就直接交换两者的值,如果比基准大的数,那么就将这个数和数组最后面的数替换,再让基准和当前的数字进行比较
            //如果相等,就将基准后一个数后移,即lt++,通过这个操作实现了基准左边都是小于基准的数,右边都是大于基准的数字
            while(lt <= gt){
                if(arr[lt] > pivot)//如果当前的数字大于基准,那么就将当前的数字和数组最后一个数字进行替换
                    swap(lt,gt--);
                else if(arr[lt] < pivot)//如果当前的数字小于基准,那么就将当前的数字和基准进行替换
                    swap(lt++,lo++);
                else
                    lt++;//如果当前的数字等于基准,那么就直接基准后一个数的下标后移
            }
            quickSort(n,lo - 1);//对左子数组进行快速排序
            quickSort(gt + 1, m);//对右子数组进行快速排序
        }
    }
    public static void swap(int n, int m){
        int temp = arr[n];
        arr[n] = arr[m];
        arr[m] = temp;
    }
}

7.基数排序

基数排序(RadixSort):主要思路是,将所有待比较数值(注意,必须是正整数)统一为同样的数位长度,数位较短的数前面补零. 然后, 从最低位开始, 依次进行一次稳定排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列.

图解:
在这里插入图片描述
方法一:通过数组来实现:
思路(个人的思路,有什么疑惑的地方请指教哈,哈哈哈):
①通过给出的数组arr,获得最大值max与最小值min,从而max - min的值有多少位数,进而可以知道要进行多少次递归(当然也可以通过循环来实现)
②新建一个数组,统计0 - 9 这10个数字分别出现了多少次
同时新建一个二维数组,其中有10行,arr.length列,来储存数组对应的数字,为什么是10行arr.length呢?因为每一行的下标对应的就是0 - 9这是个数字,列有arr.length是为了避免溢出,因为如果数字很多的情况呢,所以我觉得有arr.length列是避免溢出的情况。
遍历数组arr,从而可以知道对应的0-9这10个数字出现的次数,并将对应的值赋给二维数组对应的行中去
④二维数组复制完毕之后,将遍历二维数组,将其值赋给原来的数组arr,然后重复上述操作max - min次,从而实现有序

考虑到数组中有负数的情况,那么要怎么解决呢?–通过将数组转换成含有非负数的数组
将数组中的值都减去min这个值,从而将数组转化成含有非负数的数组,然后再将进行上述操作,不过注意的是,将二维数组的值赋给arr以后,arr并不是原来的数组了,因为转化成非负数的时候所有的数字都减去了min,所以还要遍历arr,将所有的数都加上min,才是要求的

代码实现:

import java.util.Scanner;

public class RadixSort {
    public static int[] arr = new int[10];
    public static int k = 1;
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int max = 0;
        int min = 0;
        //输入数字,同时获得最大值、最小值
        for(int i = 0; i<arr.length;i++) {
            arr[i] = sc.nextInt();
            if(max < arr[i])
                max = arr[i];
            if(min > arr[i])
                min = arr[i];
        }
        //获取数组中最多有多少位数字 ---由于有了负数的存在,那么最大值的位数需要改变,因为下面的将数组化为整数的时候,最大值要改变,所以此时
        //含有负数的数组化为非负整数的时候,最大值发生了改变,为max - min,如果数组是非负整数,因为下面的将数组化为整数的时候,最大值要改变
        //所以此时的最大值还是max - min
        int count = getCount(max - min);
        getRadixSort(arr,count,min);//调用方法,进行基数排序
        System.out.println("进行基数排序之后:");
        for(int a : arr)
           System.out.print(a+" ");
        System.out.println();
    }
    public static int  getCount(int max){
        int count = 1;
        max /= 10;
        while(max != 0){
            count++;
            max /= 10;
        }
        return count;
    }
    public static void getRadixSort(int[] arr, int total,int min){
        //利用递归来求解
        if(total <= 0){
            return;
        }else{
            int[] count = new int[10]; //新建一维数组,来统计每个数位出现的次数
            int[][] array = new int[10][arr.length];//定义一个二维数组
            for(int i = 0; i<arr.length; i++){
                //获得一个数的个位、十位等数字
                int digit = (arr[i] - min) / k % 10;//注意这里要是arr[i] - min,以防止负数的存在
                array[digit][count[digit]]= (arr[i] - min);//将数组中的值赋值到有序数组中
                count[digit] ++;
            }
            //将二位数组中的值赋给原来的数组
            int m = 0;
            for(int i = 0; i<10; i++){
                    if(count[i] != 0){
                        for(int j = 0; j<count[i]; j++){
                            arr[m++] = array[i][j];
                        }
                    }
            }
            //因为防止有负数,本来将数组的值都减去了min,所以要变成原来的数组,要将所有的数字都加上min才可以
            for(int i = 0; i<arr.length; i++)
                    arr[i] += min;
            k *= 10;
            total--;
            getRadixSort(arr,total,min);
        }
        //利用循环来实现
//        while(total > 0){
//            //统计每个数位出现的次数
//            int[] count = new int[10];
//            int[][] array = new int[10][arr.length];//定义一个有序数组
//            for(int i = 0; i<arr.length; i++){
//                int digit = (arr[i] - min) / k % 10;//获得最小值
//                array[digit][count[digit]]= (arr[i] - min);//将数组中的值赋值到有序数组中
//                count[digit] ++;
//            }
//            //将有序数组中的值赋给原来的数组
//            int m = 0;
//            for(int i = 0; i<10; i++){
//                    if(count[i] != 0){
//                        for(int j = 0; j<count[i]; j++){
//                            arr[m++] = array[i][j];
//                        }
//                    }
//            }
//            for(int i = 0; i<arr.length; i++)
//                    arr[i] += min;
//            k *= 10;
//            total--;
//        }
    }
}

通过链表来实现的代码:

/**
 * 通过链表来实现基数排序
 * 思路:
 * 1、新建链表数组,从而表示有序数组
 * 2、获得arr中的最大值、最小值,从而知道arr要转换成有序链表要循环max - min次
 * 3、获得arr数组中的每一项中每一个位数出现的次数,然后将这个数放在链表数组对应的位置中
 * 4、获得链表数组之后,遍历这个数组,然后将值赋给arr
 * 5、获得arr并不是要求的有序数组,需要遍历这个数组,将每一项都要加上min才是题目要求的
 * 为什么要怎么做?因为要考虑负数存在的情况,因为有负数,所以进行基数排序,我们可以通过将
 * 数组转换成最小值为0的数组(注意只是改变其值而已,而不是新建的数组),而要实现将数组转换成
 * 含有非负数的,那么将每一项的值都减去min
 * 6、重复上述操作3、4、5,直至循环max - min次
 */
package sort;

import java.util.Scanner;

public class RadixSort2 {
    public static int[] arr = new int[10];
    public static int k = 1;
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int max = 0, min = 0;
        //输入数组,同时获得数组中的最大值、最小值
        for(int i = 0; i<arr.length; i++) {
            arr[i] = sc.nextInt();
            if(max < arr[i])
                max = arr[i];
            if(min > arr[i])
                min = arr[i];
        }
        int count = getCount(max - min);//获得数组arr转换成有序数组循环的次数
        getRadixSort(count,min);
        System.out.println("进行基数排序之后:");
        for(int a: arr)
            System.out.print(a+ " ");
        System.out.println();
    }

    /**
     * 获得arr要转换成有序数组循环的次数 --- 也就是数组max - min的值有多少位数
     * @param max
     * @return
     */
    public static int getCount(int max){
        int count = 1;
        max /= 10;
        while(max != 0){
            count++;
            max /= 10;
        }
        return count;
    }

    /**
     * 通过链表来实现基数排序
     * @param total 表示要转换的次数
     * @param min  表述数组中的最小值
     */
    public static void getRadixSort(int total, int min){
    //通过循环来实现
        while(total > 0){
            LinkedList[] list = new LinkedList[10];//新建一个链表数组,长度为10,对应的是0-9这10个数字
            for(int i = 0; i<list.length; i++)
                list[i] = new LinkedList();//新建链表!!!注意这一步必须要有,否则没有办法初始化链表,也就没有实现其链表的相关操作
            for(int i = 0; i<arr.length; i++){
                int digit = ((arr[i] - min) / k) % 10;//将arr[i] - min以防数组中含有负数,从而将负数的数组转换成非负数的数组
                list[digit].insert(arr[i] - min);//将对应的数插入到链表数组对应的位置 -- 减去min是避免了存在负数的情况
            }
            int m = 0;
            //将链表数组中的值赋值给arr
            for(int i = 0; i<list.length; i++){
                if(!list[i].isEmpty()){
                    Node current = list[i].head.next;//因为定义的是带假节点的链表,所以链表真正的头节点就是head.next
                    //遍历链表,将值赋给数组
                    while(current != null){
                        arr[m++] = current.value;
                        current = current.next;
                    }
                }
            }
            //这里和上面所说的一样的道理,都是避免负数的存在,转化非负数是将所有的值减去min,所以这里转化成原来的数组要将所有的值都加min才可以
            for(int i = 0; i<arr.length; i++){
                arr[i] += min;
            }
            k *= 10;
            total--;
        }
    }
}

8.堆排序

思路:
①首先按照要求将数组构建成大顶堆或者小顶堆。所谓的大顶堆,就是根节点是数组的最大值,反之,小顶堆的根节点是数组的最小值。
在这里插入图片描述

②然后在构建完毕之后,将根节点和数组的最后一个节点进行交换,交换完毕之后,数组的个数要减1,从而在之后的操作中不在涉及到最后的数,保证了最大或最小的数在最后。
③然后通过递归,重复上面的操作①②,直到数组的个数为1结束递归

public class SortApp {
		public static int[] arr = new int[10];//定义一个数组
		public static int count = arr.length;//元素的个数
		public static void main(String[] args) {
			for(int i = 0; i<arr.length; i++) {
				arr[i] = (int)(Math.random() * 100);//随机产生10个随机数
			}
			System.out.print("未进行堆排序之前:");
			display();
			for(int i = 1; i<arr.length;i++)
				tickUp(i);//构建大顶堆
			sort();//进行堆排序
			System.out.print("进行堆排序之后:");
			display();//输出
		}
		public static void display() {
			for(int a: arr)
				System.out.print(a +" ");
			System.out.println();
		}
		  public static void sort() {
			if(count == 1)//如果只有一个元素,那么结束递归
    		  return;
    	  swap(0,count - 1);//将根节点和最后一个节点进行交换,然后将元素个数减1,从而将最后一个数固定
    	  count --;
    	  /*
    	  不知道细心的小伙伴有没有发现如果这样写的话会存在一个bug,
    	  那就是如果一开始数组就只有一个元素,那么这时候经过swap之后
    	  count的值变成了0,此时不等于1,如果这样写的话,那么就会
    	  继续递归,此时就会发生报错,因为-1发生越界了,所以
    	  需要将这一步代码放到swap之前,或者判断是count <= 1,但明显放到
    	  前面会好些,因为不需要进行swap这一步操作
    	  if(count == 1)//如果只有一个元素,那么结束递归
    		  return;
    	 */
			tickDown(0);//由上面可以知道,根节点和最后一个节点交换了,那么此时根节点不一定是最大的,那么需要和它的子节点进行比较,从而再次构建大顶堆
			sort();//构造完毕之后,再次递归,进行堆排序
		}
		  public static void swap(int i, int j) {
			   int temp = arr[i];
			   arr[i]  = arr[j];
			   arr[j] = temp;//将根节点和最后一个节点进行交换
		  }
		/**
		 * 向下筛选,从而再次构建大顶堆
		 * @param index
		 */
		public static void tickDown(int index) {
			int larger;
			int temp = arr[index];//将父节点赋给一个临时变量,然后和它的子节点进行比较,如果大于,就说明他的位置是对的,否则进行交换
			while(index < count / 2) {
				//循环条件必须是index < count / 2,否则再计算左右子节点的时候发生报错
				int leftChild = index * 2 + 1;
				int rightChild = index * 2 + 2;
				if(rightChild < count && arr[leftChild] < arr[rightChild]) {
					//如果含有右子节点,而且左右子节点中右子节点最大,那么larger = rightChild
					larger = rightChild;
				}else {
					//这里有两种情况,首先如果没有右子节点,那么larger 就是leftChild,其次如果有右子节点,而且左子节点大于右子节点,那么larger = leftChild
					larger = leftChild;
				}
				if(arr[larger] <= temp) {
					//如果父节点大于等于子节点,那么这个位置就是当前节点的位置,退出循环,注意不可以在这里写arr[index] = temp,然后在结束循环的时候不写这一步了
					//因为有可能一直是arr[larger]一直大于temp,直到index = count / 2,退出循环,从而没有办法插入temp,导致数组中有两个相同的数
					break;
				}else {
					arr[index] = arr[larger];//如果父节点小于子节点,那么子节点替换到其父节点的位置,父节点替换到其子节点的位置
					index = larger;
				}
				arr[index] = temp;//这里有两种情况,首先第一种是index = count / 2,从而结束循环,第二种是arr[larger] <= temp,从而结束循环
			}
		}
		/**
		 * 通过向上筛选进行将节点插入到堆中
		 * @param index
		 */
		public static void tickUp(int index) {
			int temp = arr[index];
			int parent = (index - 1) / 2;//获取其父节点
			while(index > 0 && arr[parent] < temp) {
				//当父节点小于子节点的时候,将其进行交换
				arr[index] = arr[parent];
				index = parent;
				parent = (parent - 1) / 2;
			}
			arr[index] = temp;
		}

}

通过参考其他大佬的代码,将只保留tickDown方法:

public class Sorting {
      public static int[] arr = new int[10];
      public static int count = arr.length;//定义数组的个数
      public static void main(String[] args) {
    	  for(int i = 0; i<count; i++)
    		  arr[i] = (int)(Math.random()*100);
    	  System.out.println("没有进行堆排序之前:");
    	  display();
    	  heapify();//首先将一个无序数组构建成一个大顶堆
    	  sort();//进行堆排序
    	  System.out.println("进行堆排序之后:");
    	  display();
    	  
    	  
      }
      /**
       * 遍历数组
       */
      public static void display() {
    	  for(int a:arr)
    		  System.out.print(a+" ");
    	  System.out.println();
      }
      public static void sort() {
          if(count == 1)//如果只有一个元素,那么结束递归
    		  return;
    	  swap(0,count - 1);//将根节点和最后一个节点进行交换,然后将元素个数减1,从而将最后一个数固定
    	  count --;
    	  /*
    	  不知道细心的小伙伴有没有发现如果这样写的话会存在一个bug,
    	  那就是如果一开始数组就只有一个元素,那么这时候经过swap之后
    	  count的值变成了0,此时不等于1,如果这样写的话,那么就会
    	  继续递归,此时就会发生报错,因为-1发生越界了,所以
    	  需要将这一步代码放到swap之前,或者判断是count <= 1,但明显放到
    	  前面会好些,因为不需要进行swap这一步操作
    	  if(count == 1)//如果只有一个元素,那么结束递归
    		  return;
    	 */
    	  adjustHeap(0);//再次构建最大堆
    	  sort();//递归,从而再次将根节点和最后一个节点交换
      }
      public static void swap(int i, int j) {
    	  int temp = arr[i];
    	  arr[i] = arr[j];
    	  arr[j] = temp;
      }
      public static void heapify() {
    	  //从最后一个非叶子节点开始遍历,将其和它的子节点进行比较,如果自身小于它的子节点,那么就进行交换,从而构建大顶堆
    	  for(int i = arr.length / 2 - 1; i >= 0; i --)
    		  adjustHeap(i);
      }
      public static void adjustHeap(int index) {
    	  int temp = arr[index];//将父节点的值赋给临时变量,将其和它的子节点进行比较
    	  int larger;//标记左右子节点中哪一个子节点的值更大,然后将其和temp进行比较,如果temp大,那么这个父节点的位置是正确的,否则进行交换
    	  while(index < count / 2) {
    		  int leftChild = index * 2 + 1;
    		  int rightChild = index * 2 + 2;
    		  if(rightChild < count && arr[leftChild] < arr[rightChild]) {
    			  //这里必须要有rightChild,因为考虑到当index 为count / 2 - 1时,只有一个左子节点
    			  //当存在右子节点时,那么将左右子节点进行比较,从而获得两者中的最大值
    			  larger = rightChild;
    		  }else {
    			  //当没有右子节点时,那么larger 为leftChild,当有右子节点,而且两个节点中,左子节点最大,那么larger 为leftChild
    			  larger = leftChild;
    		  }
    		  if(arr[larger] <= temp) {
    			  //当父节点的值比其左右子节点都大,那么就退出循环
    			  break;
    		  }else {
    		  //当父节点的值小于其子节点的值,那么进行交换
    			  arr[index] = arr[larger];
    			  index = larger;
    		  }
    			  
    	  }
    	  arr[index] = temp;
      }
}

我是第一次写博客的,有写的不好的地方,还请大家原谅啊,哈哈哈

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

几种排序的总结(包括含有重复数字的快速排序) 的相关文章

  • Spring 事务传播行为

    如果对事务不了解的可以先看下 我的上一篇文章 数据库事务详解 概述 一般SSH的项目都是使用三层架构即Controller Services DAO Spring 的事务一般都在Services定义 而Controller DAO都不定义事
  • LVGL入门 常用的几个命令(个人笔记)

    前言 学习LVGL的过程中 常常知道有这个命令 也知道大概怎么用 但总想不起来命令叫什么 在整个库中找也显得麻烦 搞得每次写程序还要翻之前的Demo 所以在这里将学习过程中用到的命令 存放在这里 方便再使用的时候方便的找到命令名字 lv o
  • 深度学习速成(11)BiLSTM

    BiLSTM即双向长短期记忆网络 Bidirectional Long Short Term Memory BiLSTM 最早由J rgen Schmidhuber和Sepp Hochreiter等人于1997年在论文 Long short

随机推荐

  • 激光雷达与相机外参标定(附open3d python代码)

    现在的激光雷达与相机的标定程序基本都是Ubuntu框架下面的 并且都是C 代码 需要安装的依赖也比较复杂 于是自己写了一个python版本的标定程序 依赖非常简单 Windows系统也可以运行 并且代码简单一个文件搞定 符合python简单
  • 2.2 面向对象(分类和static)

    1 案例驱动模式 1 1案例驱动模式概述 理解 通过我们已掌握的知识点 先实现一个案例 然后找出这个案例中 存在的一些问题 在通过新知识点解决问题 1 2案例驱动模式的好处 理解 解决重复代码过多的冗余 提高代码的复用性 解决业务逻辑聚集紧
  • uniapp小程序,根据小程序的环境版本,控制的显页面功能按钮的示隐藏

    需求 根据小程序环境控制控制页面某个功能按钮的显示隐藏 下面是官方文档和功能实现的相关代码 实现上面需要 用到了uni getAccountInfoSync uni getAccountInfoSync 是一个 Uniapp 提供的同步方法
  • qnap 文件传输服务器,如何将 QNAP NAS 作为 RADIUS 服务器使用?

    QNAP NAS 的远程身份验证拨入用户服务 RADIUS 服务器功能可以为要连接并使用网络服务的计算机提供集中身份验证和授权管理 系统用户帐户仅支持 PAP EAP TLS PAP 和 EAP TTLS PAP 身份验证 仅支持 WPA
  • 使用Electron打包Http地址为应用程序

    使用Electron打包Http地址为应用程序 NodeJS环境安装 下载NodeJS 安装NodeJS 配置镜像地址 配置npm镜像地址 配置Electron镜像地址 编辑项目配置文件 编辑package js文件 编辑main js文件
  • react路由根据用户角色设置权限校验

    react路由根据用户角色设置权限校验 前言 做前端项目的时候 我们经常会有这样的情况 一个系统会有多种权限不同的角色 每个角色都有自己能访问的模块角色间能访问的模块 页面 并不完全相同 因此我们经常会有根据不同的角色管理不同的给 每个路由
  • 学习SVG(九)裁剪和蒙版

    裁剪 使用clipPath元素创建裁剪区域 区域内的部分显示 区域外的隐藏
  • WIN11任务栏出现空白,卡任务栏的解决办法

    WIN11全球出现BUG 具体现象是卡任务栏 任务栏显示空白 一直卡着 出现今天这个问题的原因是微软授时服务器出现故障 所以按Ctrl Alt Delete进入任务管理器 左上角新建任务输入 control exe 打开控制面板 在控制面板
  • Advanced Level 1001 A+B Format (20 point(s))

    PAT甲级系列 PAT Advanced Level 文章目录 英文 Title Input Specification Output Specification Sample Input Sample Output 中文 题目 输入格式
  • Java List<Map<String, Object>> 排序

    name key值 List
  • TypeError: decoding str is not supported

    打开文件报错 修改后代码如下 fi open D 文件下载 sensor txt rb fo open D 文件下载 earpa001 txt wt for line in fi ls str line encoding utf 8 str
  • /var/run/yum.pid 已被锁定,PID 为 8843 的另一个程序正在运行

    原因 yum处于锁定状态中 解决方法 强制关掉yum进程 直接在终端运行命令将该文件删除 然后再次运行yum即可 命令 rm f var run yum pid
  • React中监听鼠标滚轮事件

    设计师要求首页轮播图加上滚动监听 撸完代码揪出来个demo const rdom require react dom class Hello extends React Component render handleScroll e con
  • next和hasnext_使用Java中的next()和hasNext()方法遍历List元素

    next和hasnext Given a List of integers and we have to traverse print all its element using next and hasNext methods 给定一个整
  • C# 生成网站不报错,发布网站时报错

    这个问题困扰了几天 几天早上起来突然灵感再现 查找原因发现 生成好的BLL点击发布时又会还原到之前的版本 查看bll发现里面的方法又恢复到之前的版本了 所以会报错提示update方法不存在2个参数 打开报错的文件夹 发现居然是在debug里
  • 《零基础入门学习Python》第088讲:Pygame:摩擦摩擦

    Play The Ball 这个小游戏现在已经有了背景音乐 有了小球 有了碰撞检测 接下来我们要做的就是摩擦摩擦 我们有一块玻璃面板的图片 如下图所示 为了方便 我把文字也打上去了 是透明的哦 还是老样子 有需要素材的可以在评论区留下邮箱或
  • 设计模式Template Method模式(Template Method)摘录

    23种子GOF设计模式一般分为三类 创建模式 结构模型 行为模式 创建模式抽象的实例 怎样创建 组合和表示它的那些对象 一个类创建型模式使用继承改变被实例化的类 而一个对象创建型模式将实例化托付给还有一个对象 创建型模式有两个不断出现的主旋
  • 毕业设计记录-Pytorch学习-使用预训练好的VGG16网络训练

    2021 12 7的记录 文章目录 2021 12 7的记录 2021 12 8的记录 一些代码和函数的理解 照着书把代码打了一遍 训练了10个epoch 可以说是没有啥下降的趋势 有问题 2021 12 8的记录 检查了很多遍代码之后 发
  • 论文 :审稿意见

    我第一次给英文期刊审稿 是导师安排的任务 我当时的审稿程序是这样的 首先打开google翻译查生词 要知道人家写的英文还有很多不认识的单词 不查哪行啊 就这样 我几乎花了三四天的时间 总算把人家的论文看完了 看完以后这审稿意见可怎么写啊 没
  • 几种排序的总结(包括含有重复数字的快速排序)

    文章目录 1 冒泡排序 2 选择排序 3 插入排序 4 折半插入排序 4 希尔排序 5 归并排序 递归实现归并排序 非递归方式实现归并排序 C语言 6 快速排序 7 基数排序 8 堆排序 1 冒泡排序 冒泡排序 Bubble Sort 一种