刷题笔记:排序算法汇总(1)(冒泡、快排、插入排序、希尔排序)

排序算法是算法题中非常常见的一类题,相关的排序方法也很多,如:快速排序、冒泡排序、归并排序、简单选择排序、堆排序等等。本文以Leetcode 面试题 17.14. 最小K个数为例,对以上排序算法做一个回顾。

冒泡排序

冒泡排序是一种交换排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
算法步骤:

  • 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
  • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
  • 针对所有的元素重复以上的步骤,除了最后一个;
  • 重复步骤1~3,直到排序完成。
    在这里插入图片描述
	public int[] smallestK(int[] arr, int k) {
    
    
        int len=arr.length;
        for(int i=0;i<len-1;i++){
    
    
            for(int j=0;j<len-i-1;j++)
            if (arr[j+1]<arr[j]){
    
    
                int tmp=arr[j+1];
                arr[j+1]=arr[j];
                arr[j]=tmp;
            }
        }

        int[] res=new int[k];
        for(int i=0;i<k;i++){
    
    
            res[i]=arr[i];
        }
        return res;
    }

快速排序

思路:
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:

  • 从数列中挑出一个元素,称为 “基准”(pivot);
  • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
//将数组完全快排
	public static int[] smallestK(int[] arr, int k) {
    
    
        if (k >= arr.length) {
    
    
            return arr;
        }

        int low = 0;
        int high = arr.length - 1;
        sort(arr,low,high);
        
        int[] dest = new int[k];
        //arr是要复制的数组,第一个0是源数组被复制的起始位置,dest是目标数组,第二个0是目标数组开始复制的位置,k代表要复制的数组的长度
        System.arraycopy(arr, 0, dest, 0, k);
        return dest;
    }


    public static void sort(int []arr, int left, int right) {
    
    
        int privot;
        if(left<right) {
    
    
            privot = partition(arr, left, right);
            sort(arr, left, privot-1);
            sort(arr, privot+1, right);
        }
    }

    private static int partition(int[] arr, int low, int high) {
    
    
        int povit=arr[low];
        while (low<high){
    
    
            while (low<high&&arr[high]>=povit){
    
    
                high--;
            }
            arr[low]=arr[high];
            while (low<high&&arr[low]<=povit){
    
    
                low++;
            }
            arr[high]=arr[low];
        }
        arr[low]=povit;

        return low;
    }

实际上,上面的代码有更简单的写法,已知要输出前k小的元素,所以,只需要将前k小的输出即可,所以代码可以改为:

public static int[] smallestK(int[] arr, int k) {
    
    
        if (k >= arr.length) {
    
    
            return arr;
        }

        int low = 0;
        int high = arr.length - 1;
        while (low < high) {
    
    
            int pos = partition(arr, low, high);

            if (pos == k - 1) {
    
    
                low = pos + 1;
            } else if (pos < k - 1) {
    
    
                low = pos + 1;
            } else {
    
    
                high = pos - 1;
            }
        }
        
        int[] dest = new int[k];
        //arr是要复制的数组,第一个0是源数组被复制的起始位置,dest是目标数组,第二个0是目标数组开始复制的位置,k代表要复制的数组的长度
        System.arraycopy(arr, 0, dest, 0, k);
        return dest;
    }
    private static int partition(int[] arr, int low, int high) {
    
    
        int povit=arr[low];
        while (low<high){
    
    
            while (low<high&&arr[high]>=povit){
    
    
                high--;
            }
            arr[low]=arr[high];
            while (low<high&&arr[low]<=povit){
    
    
                low++;
            }
            arr[high]=arr[low];
        }
        arr[low]=povit;

        return low;
    }

简单插入排序

在这里插入图片描述

	public static int[] smallestK(int[] arr, int k) {
    
    
        int len=arr.length;

        for (int i=1;i<len;i++){
    
    
            int j=i-1;
            int tmp=arr[i];
            while (j>=0&&tmp<arr[j]){
    
    
                arr[j+1]=arr[j];
                j--;
            }
            arr[j+1]=tmp;
        }
        
        int[] res=new int[k];
        for (int i=0;i<k;i++){
    
    
            res[i]=arr[i];
        }
        return res;
    }

希尔排序

思想:将整个数组以gap为间隔划分为若干个组,在若干个组内分别进行插入排序。然后降低gap,重复上面的步骤。到gap=1时,即为插入排序。

具体步骤:

  • 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
  • 按增量序列个数k,对序列进行k 趟排序;
  • 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

在这里插入图片描述

	public static int[] smallestK(int[] arr, int k) {
    
    
        int len=arr.length;
        int gap=len/2;

        while (gap>0){
    
    

            for (int num=gap;num<len;num++){
    
    
                int tmp=arr[num];
                int j;
                for (j=num;j-gap>=0&&tmp<arr[j-gap];j-=gap){
    
    
                    arr[j]=arr[j-gap];
                }
                arr[j]=tmp;
            }
            gap/=2;
        }

        int[] res=new int[k];
        for (int i=0;i<k;i++){
    
    
            res[i]=arr[i];
        }
        return res;
    }

猜你喜欢

转载自blog.csdn.net/qq_35531985/article/details/111585025