快速排序/快速选择算法

news2024/11/25 15:45:13

一.快速排序

1.基本介绍

快速排序(Quicksort〉是对冒泡排序的一种改进,都属于交换排序。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分(每次选择中轴值),中轴值左边的元素小于中轴值,中轴值右边的元素全部大于中轴值(但不要求有序),然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

2.基本思路

我们每次选择一个中轴值,将中轴值和最后一个元素交换位置,对left到right-1的元素进行交换,当r<=l,此时l停留到的位置和right位置上的元素(中轴值)进行交换,这个时候,l停留的位置上是中轴值,并且左边的元素比中轴值小,右边的元素比中轴值大,然后递归对左边和右边的元素进行快速排序,直到left>=right的时候停止,此时数组便是有序的了.

1.关于中轴值的选择

大部分的可能给出的是选择第一个元素作为中轴值,但是这种做法有一定的弊端,如果是数组的元素是随机排序的,是可以接受的,但是如果数组的元素是预排序或者反序的,这样所有的元素不是全被划入到中轴值左边,就是划入到中轴值右边,那么时间复杂度就会很高

一种有效的方法时是采用随机产生一个下标为[left,right]的下标,这种情况下虽然可能会产生上面描述的情况,但总的来说可能性还是很小的.

还有一种方法是:三数中值分割法,我们选取三个数,使得三个数的最大值作为中轴值进行分割,一般来说我们选取左端,中端,右端的三个元素的终止作为中轴值,这种选取的方法还是

2.小数组情形

对于很小的数组(数组元素个数小于20),快速排序不如插入排序,因为快速排序是递归的.我们通常的做法是对于小的数组采用插入排序,一种好的截止范围是n=10,同时这种做法也避免了三数中值分割法的错误情况,比如最终数组的元素只有一个元素或者两个元素,而无法三数分割

3.时间复杂度

快速排序的平均时间复杂度:O(nlogn) 

最好情况:O(nlogn) 

最坏情况:O(n^{2})

空间复杂度:O(logn)

同时快速排序不稳定,也就说元素相同的时候,原本在前边的元素,可能会最终被排序到后边

3.代码实现

1.随机值分割

    public void quickSort(int[] nums, int left, int right) {
        if (left >= right)
            return;
        int index = (int) (left + Math.random() * (right - left + 1));
        int pivot = nums[index];//随机值生成index
        swap(nums, index, right);
        int l = left, r = right - 1;
        while (true) {
            while (l < right && nums[l] < pivot) {//找到第一个比pivot大的数
                l++;
            }
            while (r > 0 && nums[r] > pivot) {//
                r--;
            }
            if (l < r)
                swap(nums, l++, r--);
            else
                break;
        }
        swap(nums, l, right);
        quickSort(nums, left, l - 1);
        quickSort(nums, l + 1, right);


    }

2.三数中值分割法

    public static final int CUTOFF = 10;

    public int median(int[] nums, int left, int right) {
        int center = (left + right) / 2;
        if (nums[left] > nums[center])
            swap(nums, left, center);//此时center大于left
        if (nums[left] > nums[right])
            swap(nums, left, right);//此时left为最小
        if (nums[center] > nums[right])
            swap(nums, center, right);
        //把center值放到right-1的位置
        swap(nums, center, right - 1);
        return nums[right - 1];


    }

    public void quickSort2(int[] nums, int left, int right) {
        //cutoff为截断值
        if (left + CUTOFF <= right) {
            int pivot = median(nums, left, right);//随机值生成index
            int l = left+1, r = right - 2;
            while (true) {
                while (nums[l] < pivot) {//找到第一个比pivot大的数
                    l++;
                }
                while (nums[r] > pivot) {//
                    r--;
                }
                if (l < r)
                    swap(nums, l++, r--);
                else
                    break;
            }
            swap(nums, l, right-1);
            quickSort2(nums, left, l - 1);
            quickSort2(nums, l + 1, right);
        } else {
            insertSort(nums, left, right);
        }


    }
    public void insertSort(int[] nums, int left, int right) {
        int j;
        for (int i = left; i <= right; ++i) {
            int temp = nums[i];
            //寻找插入的位置
            for (j = i; j > left && temp < nums[j - 1]; j--) {
                nums[j] = nums[j - 1];
            }
            nums[j] = temp;
        }
    }


    public void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

2.快速选择

1.基本介绍

快速选择算法其实是快速排序算法的变形,主要用于解决寻找第k大元素或者第k小元素,当我们需要寻找找到(排序后)第k个元素的时候,不要求数组的所有元素必须有序,这个时候我们可以选择使用快速选择算法,因为快速选择算法的时间复杂度比直接使用快速排序的时间复杂度低

快速选择的时间复杂度:O(n)

快速排序的时间复杂度:O(nlog(n))

①中轴值最终能交换到第k个位置,说明我们找到了第k个元素

②中轴值最终的位置大于第k个位置,此时我们只需要对中轴值左边的元素进行快速排序

③中轴值最终的位置小于第k个位置,此时我们只需要对中轴值右边的元素进行快速排序

2.基本思路

快速选择的基本思路和快速排序算法还是一样的,无非就是多加了一个判断,判断是对中轴值左边进行快速排序,还是右边进行.

3.代码实现

1.随机值分割

    public void quickSelect(int[] nums, int left, int right, int k) {
        if (left >= right)
            return;
        int index = (int) (left + Math.random() * (right - left + 1));
        int pivot = nums[index];//随机值生成index
        swap(nums, index, right);
        int l = left, r = right - 1;
        while (true) {
            while (l < right && nums[l] < pivot) {//找到第一个比pivot大的数
                l++;
            }
            while (r > 0 && nums[r] > pivot) {//
                r--;
            }
            if (l < r)
                swap(nums, l++, r--);
            else
                break;
        }
        swap(nums, l, right);
        if (l > k) {
            quickSelect(nums, left, l - 1, k);
        } else if (l < k) {
            quickSelect(nums, l + 1, right, k);
        }


    }

2.三数中值分割法

    public static final int CUTOFF = 10;

    public int median(int[] nums, int left, int right) {
        int center = (left + right) / 2;
        if (nums[left] > nums[center])
            swap(nums, left, center);//此时center大于left
        if (nums[left] > nums[right])
            swap(nums, left, right);//此时left为最小
        if (nums[center] > nums[right])
            swap(nums, center, right);
        //把center值放到right-1的位置
        swap(nums, center, right - 1);
        return nums[right - 1];


    }

    public void quickSelect2(int[] nums, int left, int right, int k) {
        if (left + CUTOFF <= right) {
            int pivot = median(nums, left, right);
            int l = left + 1, r = right - 2;
            while (true) {
                while (l < right && nums[l] < pivot) {//找到第一个比pivot大的数
                    l++;
                }
                while (r > 0 && nums[r] > pivot) {//
                    r--;
                }
                if (l < r)
                    swap(nums, l++, r--);
                else
                    break;
            }
            swap(nums, l, right - 1);
            if (l > k) {
                quickSelect2(nums, left, l - 1, k);
            } else if (l < k) {
                quickSelect2(nums, l + 1, right, k);
            }
        } else {
            insertSort(nums, left, right);
        }


    }

    public void insertSort(int[] nums, int left, int right) {
        int j;
        for (int i = left; i <= right; ++i) {
            int temp = nums[i];
            //寻找插入的位置
            for (j = i; j > left && temp < nums[j - 1]; j--) {
                nums[j] = nums[j - 1];
            }
            nums[j] = temp;
        }
    }


    public void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

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

1.题目描述

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

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

力扣:力扣

2.思路分析

看到题目我们就可以想到这一题可以使用快速选择算法肯定会更加高效,寻找第k个最大的元素,也就是寻找到nums.length-k个元素是什么即可,当然我们也可以直接快速排序,直接返回nums.length-k个元素

3.代码实现

1.直接使用快速排序

   public int findKthLargest(int[] nums, int k) {
        quickSort(nums, 0, nums.length - 1);
        return nums[nums.length - k];


    }

    public void quickSort(int[] nums, int left, int right) {
        if (left >= right)
            return;
        int index = (int) (left + Math.random() * (right - left + 1));
        int pivot = nums[index];//随机值生成index
        swap(nums, index, right);
        int l = left, r = right - 1;
        while (true) {
            while (l < right && nums[l] < pivot) {//找到第一个比pivot大的数
                l++;
            }
            while (r > 0 && nums[r] > pivot) {//
                r--;
            }
            if (l < r)
                swap(nums, l++, r--);
            else
                break;
        }
        swap(nums, l, right);
        quickSort(nums, left, l - 1);
        quickSort(nums, l + 1, right);

    }

    public  void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

2.快速选择

    public int findKthLargest(int[] nums, int k) {
        quickSelect(nums, 0, nums.length - 1,nums.length-k);
        return nums[nums.length - k];


    }

    public void quickSelect(int[] nums, int left, int right, int k) {
        if (left >= right)
            return;
        int index = (int) (left + Math.random() * (right - left + 1));
        int pivot = nums[index];//随机值生成index
        swap(nums, index, right);
        int l = left, r = right - 1;
        while (true) {
            while (l < right && nums[l] < pivot) {//找到第一个比pivot大的数
                l++;
            }
            while (r > 0 && nums[r] > pivot) {//
                r--;
            }
            if (l < r)
                swap(nums, l++, r--);
            else
                break;
        }
        swap(nums, l, right);
        if (l > k ) {
            quickSelect(nums, left, l - 1,k);
        } else if (l < k ) {
            quickSelect(nums, l + 1, right,k);
        }


    }

    public  void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

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

1.题目描述

给你一个二维矩阵 matrix 和一个整数 k ,矩阵大小为 m x n 由非负整数组成。

矩阵中坐标 (a, b) 可由对所有满足 0 <= i <= a < m0 <= j <= b < n 的元素 matrix[i][j]下标从 0 开始计数)执行异或运算得到。

请你找出 matrix 的所有坐标中第 k 大的值(k 的值从 1 开始计数)。

力扣:力扣

2.思路分析

这一题首先需要解决的就是将所有坐标的异或运算的结果表达出来,然后用一个ArrayList存起来,然后这个时候我们就可以使用快速选择求出来第k大的值了.

首先我们需要解决的就是各个位置的异或结果的值,一想到异或,并且题目中明确表达出 满足0 <= i <= a < m0 <= j <= b < n 的元素 matrix[i][j],这个时候我们可以想到使用异或前缀和进行解答,我们这个时候需要找到进行递推的异或表达式

借用力扣官方题解的一幅图片,我们可以看出来异或递推公式为

prefix[i][j] = prefix[i - 1][j] ^ prefix[i][j - 1] ^ prefix[i - 1][j - 1] ^ matrix[i - 1][j - 1];

但我们我们看出(i,j)需要上一层的元素进行递推得到,如果我们定义的前缀异或的表达式长度为二维数组的大小的话,这个时候我们需要对第一行和第一列进行初始化,这个时候是很麻烦的,这个时候我们不妨定义的长度为m+1和n+1,刚开始元素的值都为1,并且一个值异或0还是本身,正好符合本题的意思

接下来进行快速选择,和上一题一样

3.代码实现

1.直接使用快速排序

    public int kthLargestValue(int[][] matrix, int k) {
        int m = matrix.length;
        int n = matrix[0].length;
        int[][] prefix = new int[m + 1][n + 1];
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                prefix[i][j] = prefix[i - 1][j] ^ prefix[i][j - 1] ^ prefix[i - 1][j - 1] ^ matrix[i-1][j-1];
                list.add(prefix[i][j]);

            }
        }
        quickSort(list, 0, list.size() - 1);
        return list.get(list.size() - k);


    }

    public void quickSort(List<Integer> list, int left, int right) {
        if (left >= right)
            return;
        int index = (int) (left + Math.random() * (right - left + 1));
        int pivot = list.get(index);//随机值生成index
        swap(list, index, right);
        int l = left, r = right - 1;
        while (true) {
            while (l < right && list.get(l) < pivot) {//找到第一个比pivot大的数
                l++;
            }
            while (r > 0 && list.get(r) > pivot) {//
                r--;
            }
            if (l < r)
                swap(list, l++, r--);
            else
                break;
        }
        swap(list, l, right);
        quickSort(list, left, l - 1);
        quickSort(list, l + 1, right);
        



    }

    public void swap(List<Integer> list, int i, int j) {
        int temp = list.get(i);
        list.set(i, list.get(j));
        list.set(j, temp);
    }

2.快速选择

   public int kthLargestValue(int[][] matrix, int k) {
        int m = matrix.length;
        int n = matrix[0].length;
        int[][] prefix = new int[m + 1][n + 1];
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                prefix[i][j] = prefix[i - 1][j] ^ prefix[i][j - 1] ^ prefix[i - 1][j - 1] ^ matrix[i-1][j-1];
                list.add(prefix[i][j]);

            }
        }
        quickSelect(list, 0, list.size() - 1, list.size() - k);
        return list.get(list.size() - k);


    }

    public void quickSelect(List<Integer> list, int left, int right, int k) {
        if (left >= right)
            return;
        int index = (int) (left + Math.random() * (right - left + 1));
        int pivot = list.get(index);//随机值生成index
        swap(list, index, right);
        int l = left, r = right - 1;
        while (true) {
            while (l < right && list.get(l) < pivot) {//找到第一个比pivot大的数
                l++;
            }
            while (r > 0 && list.get(r) > pivot) {//
                r--;
            }
            if (l < r)
                swap(list, l++, r--);
            else
                break;
        }
        swap(list, l, right);
        if (l > k) {
            quickSelect(list, left, l - 1, k);
        } else if (l < k) {
            quickSelect(list, l + 1, right, k);
        }



    }

    public void swap(List<Integer> list, int i, int j) {
        int temp = list.get(i);
        list.set(i, list.get(j));
        list.set(j, temp);
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/403365.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Canvas详细使用方法(一)

Canvas Canvas的注意事项 < canvas > 和 < img > 元素很相像&#xff0c;唯一的不同就是它并没有 src 和 alt 属性。 -< canvas > 标签只有两个属性——width和height( 单位默认为px )。当没有设置宽度和高度时&#xff0c;canvas 会初始化宽为 300px 和高…

C#基础之面向对象编程(二)

总目录 文章目录总目录前言一、概述1. 定义2. 面向对象的三大特性二、封装1. 定义2. 属性三、继承1. 定义2. 继承的使用3. base 和this四、多态1. 定义2. 重写和重载3. 多态性的实现1、静态多态性2、动态多态性4. 向上转型和向下转型1、定义2、语法格式3、案例结语前言 本文主…

Docker常用项目实战演练

docker镜像源的修改 linux环境下编辑 /etc/docker/daemon.json vi /etc/docker/daemon.json #如添加如下网易镜像源 { "registry-mirrors": ["http://hub-mirror.c.163.com"] }docker run命令详细解释 日常工作中用的比较多的是docker run命令&#xff…

[ROC-RK3399-PC Pro] 手把手教你移植主线Buildroot(基于2023.02-rc3版本)

&#x1f347; 博主主页&#xff1a;Systemcall小酒屋&#x1f347; 博主简介&#xff1a;Neutionwei&#xff0c;C站嵌入式领域新星创作者之一&#xff0c;一枚热爱开源技术、喜欢分享技术心得的极客&#xff0c;注重简约风格&#xff0c;热衷于用简单的案例讲述复杂的技术&am…

机械学习 - 基础概念 - scikit-learn - 数据预处理 - 1

目录安装 scikit-learn术语理解1. 特征&#xff08;feature &#xff09;和样本&#xff08; sample / demo&#xff09;的区别&#xff1f;2. 关于模型的概念一、机械学习概念1. 监督学习总结&#xff1a;2. 非监督学习总结&#xff1a;3. 强化学习总结&#xff1a;三种学习的…

硬件基础常识【1】--如何让BJT工作在深度饱和区

引言BJT饱和的概念差不多的比喻特性曲线说明记忆NPN和PNP的小技巧- -保证一辈子不忘简单估算总结引言 学过模电或者做过一些电子作品的人都知道三极管这个器件&#xff0c;虽然是个小玩意&#xff0c;但在电路设计过程中承担了巨大的作用。BJT叫做双极结型三极管&#xff0c;可…

浏览器并发行为记录

使用nodejs koa起一个服务&#xff0c;使请求延时返回。 服务端代码 /** 延时 */ exports.timeoutTestData async function (ctx) {console.log(get query:, ctx.request.query);const query ctx.request.query;let timeout query.timeout || 2000;await new Promise(res…

vue专项练习

一、循环实现一个列表的展示及删除功能 1.1 列表展示 1、背景&#xff1a; 完成一个这样的列表展示。使用v-for 循环功能 id接口名称测试人员项目名项目ID描述信息创建时间用例数1首页喵酱发财项目a1case的描述信息2019/11/6 14:50:30102个人中心张三发财项目a1case的描述信…

Redis学习(13)之Lua脚本【环境准备】

文章目录一 Lua入门环境准备1.1 Lua简介1.2 Linux 系统安装Lua1.2.1 Lua 下载1.2.2 Lua 安装1.3 Hello World1.3.1 命令行模式1.3.2 脚本文件模式1.3.3 两种脚本运行方式1.4 Win安装Lua1.4.1 LuaForWindows的安装1.4.2 SciTE修改字体大小1.4.3 SciTE中文乱码1.4.4 SciTE快捷键工…

aws ecs 使用copilot快速创建ecs集群环境并部署服务

参考资料 https://github.com/aws/copilot-cli https://aws.github.io/copilot-cli/ https://github.com/aws-samples/amazon-ecs-cli-sample-app https://ecsworkshop.com/microservices/frontend/#deploy-frontend-0 ecs的服务部署从头开始需要进行以下操作 创建vpc等网…

JS中的事件、DOM操作

一、事件1.1 事件介绍事件: 就是发生在浏览器(页面)上一件事,键盘事件,鼠标事件,表单事件,加载事件等等1.2 事件绑定方式事件要想发生,就得将事件和标签先绑定一个完整的事件有三部分事件源(标签)什么事(事件)响应(动作效果)事件绑定,其实就是事件和标签绑定方式1: 事件源,事件…

IBM Semeru Windows 下的安装 JDK 17

要搞清楚下载那个版本&#xff0c;请参考文章&#xff1a;来聊聊 OpenJDK 和 JVM 虚拟机下载地址semeru 有认证版和非认证版&#xff0c;主要是因为和 OpenJ9 的关系和操作系统的关系而使用不同的许可证罢了&#xff0c;本质代码是一样的。在 Windows 下没有认证版&#xff0c;…

[计算机组成原理(唐朔飞 第2版)]第三章 系统总线(学习复习笔记)

3.1 总线的基本概念 计算机系统的五大部件之间的互连方式有两种 各部件之间使用单独的连线&#xff0c;称为分散连接将各部件连到一组公共信息传输线上&#xff0c;称为总线连接 总线是连接多个部件的信息传输线&#xff0c;是各部件共享的传输介质。 当多个部件与总线相连时&…

【java基础】LinkedList源码解析

文章目录基本介绍构造器基础方法linkFirstlinkLastlinkBeforeunlinkFirstunlinkLastunlinknodeindexOf方法分析总结基本介绍 在java中&#xff0c;LinkedList就是使用双向链表存储元素&#xff0c;既然是链表&#xff0c;那么也就知道了该数据结构擅长添加和删除。对于需要频繁…

线程等待/休眠/状态及 Runnable 和 Callable 的简单使用及原理

关于线程和进程的基本概念☛操作系统中线程和进程的概念理解 这篇文章已经有了很详细的解释, 接下来主要来讲讲线程等待与线程休眠 / 线程的几种状态 / Runnable 和 Callable 与 Thread 的概念和区别及 Executor 框架是什么样的. 关于线程1 线程等待与线程休眠2 线程一共有哪些…

[洛谷-P3698][CQOI2017]小Q的棋盘

一、问题 题目描述 小 Q 正在设计一种棋类游戏。 在小 Q 设计的游戏中&#xff0c;棋子可以放在棋盘上的格点中。某些格点之间有连线&#xff0c;棋子只能在有连线的格点之间移动。整个棋盘上共有 VVV 个格点&#xff0c;编号为 0,1,2,⋯,V−10,1,2,\cdots, V- 10,1,2,⋯,V−…

【C++知识点】C++11 常用新特性总结

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;C/C知识点 &#x1f4e3;专栏定位&#xff1a;整理一下 C 相关的知识点&#xff0c;供大家学习参考~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;…

EasyRcovery16免费的电脑照片数据恢复软件

电脑作为一种重要的数据储存设备&#xff0c;其中保存着大量的文档&#xff0c;邮件&#xff0c;视频&#xff0c;音频和照片。那么&#xff0c;如果电脑照片被删除了怎么办&#xff1f;今天小编给大家介绍&#xff0c;误删除的照片从哪里可以找回来&#xff0c;误删除的照片如…

win10打印机拒绝访问解决方法

一直以来,在安装使用共享打印机打印一些文件的时候&#xff0c;会遇到错误提示&#xff1a;“无法访问.你可能没有权限使用网络资源。请与这台服务器的管理员联系”的问题&#xff0c;那为什么共享打印机拒绝访问呢&#xff1f;别着急&#xff0c;下面为大家带来相关的解决方法…

mysql时区问题

设置mysql容器时间与服务器时间一致 问题背景&#xff1a; 今天测试发现一个问题&#xff0c;时间不一致&#xff0c;当工单入库时&#xff0c;其创建时间和更新时间应该是一样的&#xff0c;即使不一样最多只会错几秒的时间&#xff1b;实际上两个时间相差的大概8小时&#…