刷题模板 | 排序

快速排序(时间O(nlogn),空间O(1)就地排序):

    public void quickSort(int[] input,int p,int q) {
        int partition;
        if (p < q) {
            partition= partition(input,p,q);
            quickSort(input,p,partition-1);
            quickSort(input,partition+1,q);
        }
    }

    public int partition(int[] input, int p, int q) {
        int temp = input[p];
        int i = p,j = q;
        while (i != j) {
            while (input[j] >= temp && i < j) {  /**必须是i<j,不能是<=*/
                j--;
            }
            while (input[i] <= temp && i < j) {/**必须是i<j,不能是<=*/
                i++;
            }
            if (i >= j) {
                break;
            }
            swap(input,i,j);
        }

        /***======================必须==================**/
        input[p] = input[i];
        input[i] = temp;
        /***======================必须==================**/
        return i;
    }

堆排序(时间O(nlogn),空间O(1)就地排序):

参考https://blog.csdn.net/qq_36186690/article/details/82505569

用于求解 TopK Elements 问题,也就是 K 个最小元素的问题。可以维护一个大小为 K 的最小堆,最小堆中的元素就是最小元素。最小堆需要使用大顶堆来实现,大顶堆表示堆顶元素是堆中最大元素。这是因为我们要得到 k 个最小的元素,因此当遍历到一个新的元素时,需要知道这个新元素是否比堆中最大的元素更小,更小的话就把堆中最大元素去除,并将新元素添加到堆中。所以我们需要很容易得到最大元素并移除最大元素,大顶堆就能很好满足这个要求。

堆也可以用于求解 Kth Element 问题,得到了大小为 k 的最小堆之后,因为使用了大顶堆来实现,因此堆顶元素就是第 k 大的元素。

快速选择也可以求解 TopK Elements 问题,因为找到 Kth Element 之后,再遍历一次数组,所有小于等于 Kth Element 的元素都是 TopK Elements。

一般升序采用大顶堆,降序采用小顶堆

堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  

小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]  

二叉树最后一个非叶子节点序号:n/2-1(序号从0开始)

堆排序的基本思想是:

1) 从最后一个非叶子节点开始(因为叶子本身就是堆),将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。

2) 将其与末尾元素进行交换,此时末尾就为最大值。

3) 然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。

如此反复执行,便能得到一个有序序列了

//初始化堆:时间复杂度O(n)
private void heapSort(int[] input) {
        //1. 构建初始大顶堆
        for (int i = input.length/2 - 1 ; i >= 0 ;i--) {
//            从第一个非叶子节点开始,从下往上,从左往右
            adjustHeap(input,i,input.length);
        }
//        2. 调整堆结构,堆顶元素与末尾元素交换
        for (int i = input.length-1; i >= 0; i--) {
            swap(input,0,i);
//            重新调整
            adjustHeap(input,0,i);
        }
    }

    /**
     * 调整大顶堆,仅仅是调整,这时候大顶堆已经建立好
        时间复杂度O(nlogn)
     * @param arr
     * @param start
     * @param length
     */
    private void adjustHeap(int[] arr, int start, int length) {
        int temp = arr[start];
        // start的左子树
        for (int i = start*2+1;i < length;i = i*2+1) {
            //有右子树且左子树的值小于右子树的值,就是找出两个子树中比较大的那一个
            if (i+1 < length && arr[i] < arr[i+1]) {
                i++;
            }
            if (arr[i] > temp) {
                arr[start] = arr[i];
                start = i;
            }
            else {
                break;
            }
        }
        arr[start] = temp;
    }

    private void swap(int[] arr,int i,int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

归并排序(时间O(nlogn),空间O(n)就地排序): 

//两路归并算法,两个排好序的子序列合并为一个子序列
    public void merge(int []a,int left,int mid,int right){
        int []tmp=new int[a.length];//辅助数组
        int p1=left,p2=mid+1,k=left;//p1、p2是检测指针,k是存放指针

        while(p1<=mid && p2<=right){
            if(a[p1]<=a[p2])
                tmp[k++]=a[p1++];
            else
                tmp[k++]=a[p2++];
        }

        while(p1<=mid) tmp[k++]=a[p1++];//如果第一个序列未检测完,直接将后面所有元素加到合并的序列中
        while(p2<=right) tmp[k++]=a[p2++];//同上

        //复制回原素组
        for (int i = left; i <=right; i++) 
            a[i]=tmp[i];
    }

    public void mergeSort(int [] a,int start,int end){
        if(start<end){//当子序列中只有一个元素时结束递归
            int mid=(start+end)/2;//划分子序列
            mergeSort(a, start, mid);//对左侧子序列进行递归排序
            mergeSort(a, mid+1, end);//对右侧子序列进行递归排序
            merge(a, start, mid, end);//合并
        }
    }

出现频率问题:

桶排序!!

下标就是出现的次数

发布了53 篇原创文章 · 获赞 5 · 访问量 1524

猜你喜欢

转载自blog.csdn.net/zhicheshu4749/article/details/103364389