分治算法-快排

算法原理

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,从后边开始找到比基准小的停下来,从前面开始找到比基准大的停下来直至两个相遇,相遇的即为新基准,找到基准后再分两边找,类似二叉树

时间复杂度:当给定1,2,3,4......有序情况下0(n^2)

当每次都能均匀分开时0(logN)

空间复杂度:最坏单分支树0(N) 最好情况:0(logN) 完全二叉树

稳定性:不稳定的排序

挖坑法(做题优先考虑的思想):先把left(0)拿出,0坐标空出即为坑,从后边找比基准小的直接填坑,再从前边找比基准大的挖坑

前后指针法:两个指针prev和cur

prev=legft,cur=left+1

一前一后走,找到比基准小的进行交换

优化思路:减少他的递归次数

1.三数取中法(left,right,mid找到这三个数据中)

2.直接插入法(针对一定范围) 二叉树越深,递归次数越多,可以在最后几层直接插入排序(因为数据少且趋于有序)

题目解析

1.颜色分类

https://leetcode.cn/problems/sort-colors/description/

题目描述

给定一个包含红色 白色和蓝色,共n个元素的数组nums,原地对他们进行排序,使得相同颜色的元素相邻,并且按照红色,白色,蓝色顺序进行排列,我们使用0,1,2分别表示红色,白色和蓝色(在不使用内置sort的情况下解决这个问题)

算法原理

上图就是我们最终要使数组达到的状态

利用三指针来解决问题,一个指针i来遍历数组,left用来记录左边界,right用来记录右边界

0,left\]的值都为0,\[left+1,i-1\]值为1,\[i,right-1\]未知(仍然需要遍历),\[right,n-1\]的值为2 遇到0,要交换nums\[++left\]和nums\[i++

遇到1,i++

遇到2,交换nums[i],nums[--right](为什么此处i不用加加?因为此处交换过来的元素是谁并不知道)

以一个具体的例子来说明:

代码实现

java 复制代码
class Solution {
    public void sortColors(int[] nums) {
        int left=-1,right=nums.length;
        int i=0;
        while(i<right){
            if(nums[i]==0){
                swap(nums,i++,++left);
            }else if(nums[i]==1){
                i++;
            }else{
                swap(nums,i,--right);
            }
        }
    }
    public void swap(int[]nums,int i,int j){
        int tmp=nums[i];
        nums[i]=nums[j];
        nums[j]=tmp;
    }
}

2.快速排序

https://leetcode.cn/problems/sort-an-array/

题目描述

给定一个整数数组nums,请将该数组升序排列

算法原理

用数组分三块的思想来实现快速排序

快速排序,选择基准元素,然后以基准元素为主,先排左边,再排右边

然后再定义两个指针,将左右的元素比较按照大小填入到临时数组中(因为我们会经常使用这个数组,所以我们可以将其设为全局变量),然后再将这个临时数组对应的填到原数组中

(这是个不断递归的过程)

当left>=right返回此时已经排完序了

优化用随机的方式选择基准

代码实现

java 复制代码
    int[] tmp;
    public int[] sortArray(int[] nums) {
        tmp=new int[nums.length];
        mergeSort(nums,0,nums.length-1);
        return nums;
    }
    public void mergeSort(int[]nums,int left,int right){
        if(left>=right){
            return;
        }
        int mid=(left+right)/2;
        mergeSort(nums,left,mid);
        mergeSort(nums,mid+1,right);
        int cur1=left,cur2=mid+1,i=0;
        while(cur1<=mid&&cur2<=right){
            tmp[i++]=nums[cur1]<=nums[cur2]?nums[cur1++]:nums[cur2++];
        }
        while(cur1<=mid){
            tmp[i++]=nums[cur1++];
        }
        while(cur2<=right){
            tmp[i++]=nums[cur2++];
        }
        for(int j=left;j<=right;j++){
            nums[j]=tmp[j-left];
        }
    }

3.数组中的第k个最大元素

https://leetcode.cn/problems/kth-largest-element-in-an-array/description/

题目描述

给定整数数组nums和整数k,请返回数组中第k个最大的元素

算法原理

数组分三块+随机选择基准元素

当c>=k时,那么我们可以确定最终元素在[right,r]区间内,只需在[right,r]内寻找即可

当c+b>=k时,返回key

当上述条件都不成立的时候,在[l,left]内寻找k-b-c大元素

代码实现

java 复制代码
    public int findKthLargest(int[] nums, int k) {
        int ret=findKey(nums,0,nums.length-1,k);
        return ret;
    }
    public int findKey(int[]nums,int l,int r,int k){
        if(l==r){
            return nums[l];
        }
        int key=nums[new Random().nextInt(r-l+1)+l];
        int left=l-1,right=r+1,i=l;
        while(i<right){
            if(nums[i]<key){
                swap(nums,++left,i++);
            }else if(nums[i]>key){
                swap(nums,--right,i);
            }else{
                i++;
            }
        }
        int c=r-right+1,b=right-left-1;
        if(c>=k){
            return findKey(nums,right,r,k);
        }else if(b+c>=k){
            return key;
        }else{
            return findKey(nums,l,left,k-b-c);
        }
    }
    public void swap(int[] nums,int i,int j){
        int tmp=nums[i];
        nums[i]=nums[j];
        nums[j]=tmp;
    }

4.最小k个数

https://leetcode.cn/problems/zui-xiao-de-kge-shu-lcof/description/

题目描述

输入整数数组arr,找出其中最小的k个数

算法原理

解法一:排序logN

解法二:堆(大根堆) NlogK

解法三:快速选择 O(N)(这里我们着重讲解这一方法)

随机选择+数组分三块

当a>k,在[l,left]内寻找最小k

当a+b>=k,返回

当上述都不成立,[right-r]内寻找k-b-a

代码实现

java 复制代码
    public int[] inventoryManagement(int[] s, int k) {
        sqort(s,0,s.length-1,k);
        int[] ret=new int[k];
        for(int i=0;i<k;i++){
            ret[i]=s[i];
        }
        return ret;
    }
    public void sqort(int[] nums,int l,int r,int k){
        int key=nums[new Random().nextInt(r-l+1)+l];
        int left=l-1,right=r+1,i=l;
        while(i<right){
            if(nums[i]<key){
                swap(nums,++left,i++);
            }else if(nums[i]>key){
                swap(nums,--right,i);
            }else{
                i++;
            }
        }
        int a=left-l+1,b=right-left-1;
        if(a>k){
         sqort(nums,l,left,k);
        }else if(a+b>=k){
            return ;
        }else{
            sqort(nums,right,r,k-a-b);
        }
    }
    public void swap(int[] nums,int i,int j){
        int tmp=nums[i];
        nums[i]=nums[j];
        nums[j]=tmp;
    }
相关推荐
cpp_250113 小时前
P8377 [PFOI Round1] 暴龙的火锅
数据结构·c++·算法·题解·洛谷
uesowys13 小时前
Apache Spark算法开发指导-Factorization machines classifier
人工智能·算法
季明洵13 小时前
C语言实现单链表
c语言·开发语言·数据结构·算法·链表
shandianchengzi14 小时前
【小白向】错位排列|图文解释公考常见题目错位排列的递推式Dn=(n-1)(Dn-2+Dn-1)推导方式
笔记·算法·公考·递推·排列·考公
I_LPL14 小时前
day26 代码随想录算法训练营 回溯专题5
算法·回溯·hot100·求职面试·n皇后·解数独
Yeats_Liao14 小时前
评估体系构建:基于自动化指标与人工打分的双重验证
运维·人工智能·深度学习·算法·机器学习·自动化
only-qi14 小时前
leetcode19. 删除链表的倒数第N个节点
数据结构·链表
cpp_250114 小时前
P9586 「MXOI Round 2」游戏
数据结构·c++·算法·题解·洛谷
浅念-14 小时前
C语言编译与链接全流程:从源码到可执行程序的幕后之旅
c语言·开发语言·数据结构·经验分享·笔记·学习·算法
爱吃生蚝的于勒14 小时前
【Linux】进程信号之捕捉(三)
linux·运维·服务器·c语言·数据结构·c++·学习