『Leetcode』十大排序的应用

十大排序应用

十种排序方法sort以及各方法的复杂度分析

1. 冒泡排序

75. 颜色分类
283. 移动零

75. 颜色分类-思路

不好的处理方法:冒泡排序 ,时间复杂度为O(n^2)。
在这里插入图片描述
注意本题,只有三色球,即只有0,1,2三个数进行从小到大不减排序。归为经典的荷兰国旗问题,题目本质是要我们将数分成三段——双指针处理。(思想:F3)


循环中的++ii++结果一样的原因,但是性能不一样:

在大量数据的时候++i的性能要比i++的性能好原因: i++由于是在使用当前值之后再+1,所以需要一个临时的变量来转存。 而++i则是在直接+1,省去了对内存的操作的环节,相对而言能够提高性能。


283. 移动零-思路

注意前提:题目给的数组是含有零元素,且非零元素有序。
D1:两次遍历。第一次记录非零元素个数n,顺便排序;第二次将[n+1,len-1]赋值为0;

class Solution {
    
    
    public void moveZeroes(int[] nums) {
    
    
        if(nums==null) return;
        int len=nums.length;
        int i,j=0;
        //第一次遍历,给非零元素排序
        for(i=0;i<len;i++){
    
    
            if(nums[i]!=0){
    
    
                nums[j++]=nums[i];
            }
        }
        //第二次遍历,给非零元素之后的空位赋值为0
        for(i=j;i<len;i++){
    
    
            nums[i]=0;
        }
    }
}

在这里插入图片描述

D2:一次遍历。设两个指针同时从下标为0开始,非零元素则i,j交换,0元素则不进行任何操作。

class Solution {
    
    
    public void moveZeroes(int[] nums) {
    
    
        int n=nums.length;
        int i=0,j=0;
       
       if(nums==null) {
    
    
			return;
		}

        for(i=0;i<n;i++){
    
    
            if(nums[i]!=0){
    
    
                swap(nums,i,j++);
            }
        }
    }
    public void swap(int[] nums,int i,int j){
    
    
        int temp=nums[i];
        nums[i]=nums[j];
        nums[j]=temp;
    }
}

在这里插入图片描述

2. 选择排序

LC 912. 排序数组

扫描二维码关注公众号,回复: 14809004 查看本文章

3. 插入排序

LC 912. 排序数组
LC 147. 对链表进行插入排序

4. 希尔排序

LC 912. 排序数组
LC 506. 相对名次

5. 归并排序

LC 剑指Offer 51. 数组中的逆序对
LC 面试题10.01. 合并排序的数组

6. 快速排序

思想:

  • 选择基准值(一般将数组第一个数作为基准值);
  • 双指针分别从左右两边移动,左边寻找比基准值大的数,右边找比基准值小的数。 两者交换数值;
  • 左指针==右指针时,将 基准值 与 指针所指位置 交换数值;
  • 随后,将数组分成两部分,分别重复上述操作即可。

代码实现:

请添加图片描述
算法分析:
请添加图片描述

指针移动问题

注意:先移动右指针
原因:如果先移动左指针就导致每次交换基准值时,都将会把比基准值大的数移动到了左边数组的第一个数,导致左边数组的值不完全小于右边数组的值,递归快排都将出错。

例题:

LC 912. 排序数组
LC 169.多数元素

LC 215. 数组中的第K个最大元素

方案一:经典快排后得到一个从小到大递增数组,返回下标为len-k+1的数即为所求。
结果:超时。

class Solution {
    
    
    public int findKthLargest(int[] nums, int k) {
    
    
        int n=nums.length;
        QuickSort(nums,0,n-1);
        return nums[n-k];
    }
    public void QuickSort(int[] nums,int low,int high){
    
    
        if(low<high){
    
    
            int piont=Partition(nums,low,high);
            QuickSort(nums,0,piont-1);
            QuickSort(nums,piont+1,high);
        }
        
    }
    public int Partition(int[] nums,int low,int high){
    
    
        //找快排中的轴心
        int piont=nums[low];
        while(low<high){
    
    
            while(low<high && nums[high]>=piont){
    
    
                high--;
            }
            nums[low]=nums[high];
            while(low<high && nums[low]<=piont){
    
    
                low++;
            }
            nums[high]=nums[low];
        }
        nums[low]=piont;
        return low;

    }

 
}

方案二:快排改进。由此可以发现每次经过「划分」操作后,我们一定可以确定一个元素的最终位置,即 xxx 的最终位置为 q,并且保证 a[l⋯q−1] 中的每个元素小于等于 a[q],且 a[q] 小于等于 a[q+1⋯r]中的每个元素。所以只要某次划分的 q为倒数第 k 个下标的时候,我们就已经找到了答案。 我们只关心这一点,至于 a[l⋯q−1] 和 a[q+1⋯r]是否是有序的,我们不关心。(思想

我们知道快速排序的性能和「划分」出的子数组的长度密切相关。直观地理解如果每次规模为 n 的问题我们都划分成 1 和 n−1,每次递归的时候又向 n−1的集合中递归,这种情况是最坏的,时间代价是 O(n ^ 2)。

我们可以引入随机化 来加速这个过程,它的时间代价的期望是 O(n)。

顺序统计量:期望为线性时间的选择算法

(暂时没弄明白)
选择算法指的是:在一个长度为n的数列中找到第i 小或第i 大的元素。

一般选择问题看起来要比找最小值这样的简单问题更难。但令人惊奇的是,这两个问题的渐近运行时间却是相同的:Θ ( n )。参考文章将介绍一种解决选择问题的分治算法。
参考文章:https://blog.csdn.net/hy592070616/article/details/120470336
方案三:堆排序。

在这里插入图片描述

class HeapSort{
    
    
	public static int[] HeapSortMain(int[] arr) {
    
    
    	int len = arr.length;
    	buildMaxHeap(arr, len); //  将数组整理成堆
    	
		for (int i = len - 1; i > 0; i--) {
    
    
            swap(arr, 0, i);  // 把堆顶元素(当前最大)交换到数组末尾
            len--;			 // 逐步减少堆有序的部分
            heapify(arr, 0, len);		// 下标 0 位置下沉操作,使得区间 [0, i] 堆有序
        }
        
        return arr;
	}
	
	//将数组整理成堆(堆有序)
	private void buildMaxHeap(int[] arr, int len) {
    
    
        for (int i = (int) Math.floor(len / 2); i >= 0; i--) {
    
    
            heapify(arr, i, len);
        }
    }
    
    // i为当前下沉元素的下标
	private void heapify(int[] arr, int i, int len) {
    
    
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        int largest = i;

        if (left < len && arr[left] > arr[largest]) {
    
    
            largest = left;
        }

        if (right < len && arr[right] > arr[largest]) {
    
    
            largest = right;
        }

        if (largest != i) {
    
    
            swap(arr, i, largest);
            heapify(arr, largest, len);
        }
    }
    
	private void swap(int[] arr, int i, int j) {
    
    
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

7. 堆排序

LC 215. 数组中的第K个最大元素

LC 剑指Offer 40.最小的k个数

4种解法秒杀TopK(快排/堆/二叉搜索树/计数排序)

8. 计数排序

LC 912. 排序数组
LC 1122. 数组的相对排序

9. 桶排序

LC 908.最小差值I
LC 164.最大间距

10. 基数排序

LC 164.最大间距
LC 561.数组拆分I

猜你喜欢

转载自blog.csdn.net/JingYan_Chan/article/details/127732460