算法通关村第十关——快速排序算法

news2024/9/21 22:02:52

1 快速排序基本过程

快速排序的是将分治法运用到排序问题的典型例子。力扣912题,给你一个整数数组 nums,请你将该数组升序排列。

基本思想:是通过随机标记一个pivot元素将含有n个元素的序列划分为左右两个子序列leftright,其中left的元素都比pivot小,right的元素都比pivot大,然后再次对leftright分别执行快速排序,这样将左右两个子序列排列完后,整个序列也就是有序的了。这里以序列[28,36,35,38,34,25]为例,示意一下一轮快速排序的过程:

在这里插入图片描述

代码如下:

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function(nums) {
    quickSort(nums, 0, nums.length - 1);
    return nums;
};

function quickSort(nums, start, end) {
    if (start >= end) {
        return;
    }
    const mid = partition(nums, start, end);
    quickSort(nums, start, mid - 1);
    quickSort(nums, mid + 1, end);
}

function partition(nums, start, end) {
    let pivot = nums[start];
    // 选择第一个数作为pivot,那么left就从第二个数开始进行比较
    let left = start + 1;
    let right = end;
    while (left <= right) {
        // 如果nums[left]一直小于pivot,left就向右移动
        while (left <= right && nums[left] <= pivot) {
            left++;
        }
        // 如果nums[right]一直大于pivot,right就向左移动
        while (left <= right && nums[right] >= pivot) {
            right--;
        }
        // 如果出现 nums[left] > pivot 或者 nums[right] < pivot 的情况,
        // 就交换 nums[left] 和 nums[right]的位置
        if (left < right) {
            [nums[left], nums[right]] = [nums[right], nums[left]];
            left++;
            right--;
        }    
    }
    // 把pivot交换到合适位置
    [nums[start], nums[right]] = [nums[right], nums[start]];
    return right;
}

但是当数组中包含大量重复元素时,会出现性能下降的现象,此时我们可以使用三路快速排序算法。

三路快速排序的基本思想是将数组划分为三个区域:小于、等于和大于 pivot 的区域。这样可以将相同元素聚集在一起,从而减少交换的次数,提高性能。

代码如下:

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function(nums) {
    threeWayQuickSort(nums, 0, nums.length - 1);
    return nums;
};

function threeWayQuickSort(nums, start, end) {
    if (start >= end) {
        return;
    }
    // 缩写解释 lt: less than(小于)  gt: greater than(大于)
    // 使用partitionThree函数进行三路划分
    const [lt, gt] = partitionThree(nums, start, end);
    threeWayQuickSort(nums, start, lt - 1);
    threeWayQuickSort(nums, gt + 1, end);
}

function partitionThree(nums, start, end) {
    let pivot = nums[start];
    // lt指向小于pivot的区域的末尾
    let lt = start;
    // gt指向大于pivot的区域的起始
    let gt = end;
    // 用于遍历数组的指针
    let i = start;
    
    while (i <= gt) {
        if (nums[i] < pivot) {
            // 将当前元素交换到小于pivot区域末尾,并扩展小于区域
            [nums[lt], nums[i]] = [nums[i], nums[lt]];
            lt++;
            i++;
        } else if (nums[i] > pivot) {
            // 将当前元素交换到大于pivot区域起始,并扩展大于区域
            [nums[gt], nums[i]] = [nums[i], nums[gt]];
            gt--;
        } else {
            // 相等情况,直接扩展等于区域
            i++;
        }
    }
    // 将pivot元素交换到适当位置
    [nums[start], nums[lt]] = [nums[lt], nums[start]];
    // 返回等于区域的起始和末尾
    return [lt, gt];
}

这段代码中的 partitionThree 函数实现了三路划分,将数组划分为小于、等于和大于 pivot 的三个区域,从而有效处理重复元素。在每次递归中,通过 ltgt 两个指针来维护三个区域的边界。相同元素会被直接放入等于区域,从而避免了不必要的交换操作。这种方法可以显著提高处理包含大量重复元素的数组的性能。

2 数组中第K大的数字

力扣215 题,数组中的第K个最大元素。给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

基于快速排序来解决这个问题,当我们完成快速排序后,序列是一个升序序列,我们只需要在排好序的序列中找到nums.length - k个元素即为最大的第k个元素。同样如果数组里包含海量重复元素那么就可以使用三路快速排序思想,将数组分为小于、等于和大于 pivot 的三个区域,以减少不必要的交换操作和递归。

代码如下:

var findKthLargest = function(nums, k) {
	// 使用快速选择算法,在索引范围 [0, nums.length - 1] 中寻找第 k 个最大元素
    return quickSelect(nums, 0, nums.length - 1, nums.length - k);
};

/**
 * 快速选择函数
 * */
function quickSelect(nums, start, end, targetIndex) {
	// 如果区间中只有一个元素,则该元素就是第 k 个最大元素
    if (start === end) {
        return nums[start];
    }
    // 使用三路快速排序的方式进行划分
    const { left, right } = threeWayPartition(nums, start, end);
    // 如果 targetIndex 在 [left, right] 区间内,则说明第 k 个最大元素就在该区间内
    if (targetIndex >= left && targetIndex <= right) {
        return nums[targetIndex];
    } else if (targetIndex < left) {
    	// 否则,在左侧区域寻找第 k 个最大元素
        return quickSelect(nums, start, left - 1, targetIndex);
    } else {
    	// 在右侧区域寻找第 k 个最大元素
        return quickSelect(nums, right + 1, end, targetIndex);
    }
}

/**
 * 三路快速排序划分函数
 * 
 * */
function threeWayPartition(nums, start, end) {
	// 选取最后一个元素作为 pivot
    const pivot = nums[end];
    let i = start;  // 当前元素索引
    let lt = start; // lt指向小于pivot的区域的末尾
    let gt = end;   // gt指向大于pivot的区域的起始
    
    while (i <= gt) {
        if (nums[i] < pivot) {
        	// 将当前元素交换到小于 pivot 区域
            [nums[i], nums[lt]] = [nums[lt], nums[i]];
            i++;
            lt++;
        } else if (nums[i] > pivot) {
        	// 将当前元素交换到大于 pivot 区域
            [nums[i], nums[gt]] = [nums[gt], nums[i]];
            gt--;
        } else {
        	// 相等的情况,直接跳过
            i++;
        }
    }
    // 返回小于 pivot 区域的右边界和大于 pivot 区域的左边界
    return { left: lt, right: gt };
}

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

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

相关文章

验证码服务(使用提供好的项目)

1、先生成一个指定位数的验证码&#xff0c;根据需要可能是数字、数字字母组合或文字。 2、根据生成的验证码生成一个图片并返回给页面 3、给生成的验证码分配一个key&#xff0c;将key和验证码一同存入缓存。这个key和图片一同返回给页面。 4、用户输入验证码&#xff0c;连…

什么是 DALI 协议?

在照明行业&#xff0c;我们常常听到 DALI 的名号&#xff01;那么&#xff0c;到底什么是 DALI 呢&#xff1f;那么今天&#xff0c;我们就来一起入门&#xff0c;揭开 DALI 的神秘面纱~什么是 DALI 协议&#xff1f; DALI &#xff0c;实际上是一个简称&#xff01;它的全程如…

向阳而生的智慧光伏设施

光伏发电太阳花装配双轴自动追踪器&#xff0c;会根据当前的经纬度和时间&#xff0c;实时计算太阳的方位角和高度角&#xff0c;计算出光伏板应当运行的角度&#xff0c;于倾角传感器的当前角度 比较&#xff0c;当二者的误差超过 1时&#xff0c;发出电机运转指令&#xff0c…

程序填空技巧1.0

程序填空要先知道这个程序要干什么&#xff0c;然后找到标准模板后对照模板填写&#xff0c;但当然不是让你做题的时候对照模板写&#xff0c;而是要把每种算法的标准模板背下来&#xff0c;但你肯定要问&#xff1a;邹邹&#xff0c;我哪里来的模板呢&#xff1f;&#xff1f;…

租赁小程序开发|免押租赁系统包含哪些功能?

租赁小程序是一种基于现代技术的创新解决方案&#xff0c;为租赁业务提供了全面的管理功能。通过这个小程序&#xff0c;您可以方便地组织和跟踪您的库存情况&#xff0c;轻松管理租赁合同以及处理订单。这一切都在您的指尖之间&#xff0c;让您节省时间和精力&#xff0c;专注…

PHP敬老院管理系统Dreamweaver开发mysql数据库web结构php编程计算机网页

一、源码特点 PHP 敬老院管理系统&#xff08;养老&#xff09;是一套完善的web设计系统&#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 论文 https://download.csdn.net/download/qq_41221322/…

【OCR识别】tess4j图片识别文字

什么是OCR? OCR &#xff08;Optical Character Recognition&#xff0c;光学字符识别&#xff09;是指电子设备&#xff08;例如扫描仪或数码相机&#xff09;检查纸上打印的字符&#xff0c;通过检测暗、亮的模式确定其形状&#xff0c;然后用字符识别方法将形状翻译成计算机…

探秘工业设计的魅力:引领时尚潮流,打造个性空间

工业风格源自于上世纪初的工人阶级世界&#xff0c;几十年来一直充满诱惑力。它们由金属集合物&#xff0c;焊接、铆钉这些暴露在外的结构组建&#xff0c;融进了更多装饰性的曲线&#xff0c;再与素雅的色彩搭配形成&#xff1a;让我们来看看这种历史悠久的&#xff0c;在室内…

创作2周年纪念日-特别篇

创作2周年纪念日-特别篇 1. 与CSDN的机缘2. 收获3. 憧憬 1. 与CSDN的机缘 很荣幸&#xff0c;在大学时候&#xff0c;能够接触到CSDN这样一个平台&#xff0c;当时对嵌入式开发、编程、计算机视觉等内容比较感兴趣。后面一个很偶然的联培实习机会&#xff0c;让我接触到了Pych…

06:TIM定时器功能------编码器接口功能

目录 1:简历 2: 正交编码器 3:编码器接口基本结构 4:编码器的工作模式 5:极性反转 A:编码器接口测速 1:连接图 2:函数介绍 3:步骤 4:代码 B:编码器接口计次 1:连接图 2:代码 1:简历 Encoder Interface 编码器接口 编码器接口可接收增量&#xff08;正交&#xff09;编…

无涯教程-分类算法 - 简介

分类可以定义为根据观测值或给定数据点预测类别的过程。分类的输出可以采用"黑色"或"白色"或"垃圾邮件"或"非垃圾邮件"的形式。 在数学上&#xff0c;分类是从输入变量(X)到输出变量(Y)近似映射函数(f)的任务&#xff0c;它属于有监督…

Bito----一款Idea智能化代码辅助插件,让你的开发效率飞起来!

ChatGPT&#xff0c;想必大家都比较熟悉了&#xff0c;一款高情商对话AI&#xff0c;可以用来进行文本对话、问答等多种人机交互场景&#xff0c;也可以用来辅助编写代码&#xff0c;大大提高程序员的开发效率。而今天的主角Bito&#xff0c;是一款比ChatGPT更快&#xff0c;无…

error LNK2019: 无法解析的外部符号 __imp__glClear@4,函数 _main 中引用了该符号

自己犯这个错误有些搞笑了&#xff0c;找着教程一步一步来还出错&#xff0c;复制GLFW示例代码 运行&#xff0c;报的第一个错误&#xff0c;这是一个链接错误&#xff0c;解决方案&#xff1a;

Unity Meta Quest MR 开发教程:(二)自定义透视 Passthrough【透视功能进阶】

文章目录 &#x1f4d5;教程说明&#x1f4d5;动态开启和关闭透视⭐方法一&#xff1a;OVRManager.instance.isInsightPassthroughEnabled⭐方法二&#xff1a;OVRPassthroughLayer 脚本中的 hidden 变量 &#x1f4d5;透视风格 Passthrough Styling⭐Inspector 面板控制⭐代码…

JVM7:垃圾回收是什么?从运行时数据区看垃圾回收到底回收哪块区域?垃圾回收如何去回收?垃圾回收策略,引用计数算法及循环引用问题,可达性分析算法

垃圾回收是什么&#xff1f;从运行时数据区看垃圾回收到底回收哪块区域&#xff1f; 垃圾回收如何去回收&#xff1f; 垃圾回收策略 引用计数算法及循环引用问题 可达性分析算法 垃圾回收是什么&#xff1f;从运行时数据区看垃圾回收到底回收哪块区域&#xff1f;垃圾回收如何去…

使用Linux部署Kafka教程

目录 一、部署Zookeeper 1 拉取Zookeeper镜像 2 运行Zookeeper 二、部署Kafka 1 拉取Kafka镜像 2 运行Kafka 三、验证是否部署成功 1 进入到kafka容器中 2 创建topic 生产者 3 生产者发送消息 4 消费者消费消息 四、搭建kafka管理平台 五、SpringBoot整合Kafka 1…

求生之路2私人服务器开服搭建教程centos

求生之路2私人服务器开服搭建教程centos 大家好我是艾西&#xff0c;朋友想玩求生之路2(left4dead2)重回经典。Steam玩起来有时候没有那么得劲&#xff0c;于是问我有没有可能自己搭建一个玩玩。今天跟大家分享的就是求生之路2的自己用服务器搭建的一个心路历程。 &#xff0…

【LeetCode-面试经典150题-day15】

目录 104.二叉树的最大深度 100.相同的树 226.翻转二叉树 101.对称二叉树 105.从前序与中序遍历序列构造二叉树 106.从中序与后序遍历序列构造二叉树 117.填充每个节点的下一个右侧节点指针Ⅱ 104.二叉树的最大深度 题意&#xff1a; 给定一个二叉树 root &#xff0c;返回其…

登录校验-JWT令牌-登陆后下发令牌

目录 思路 接口文档 令牌生成和下发 步骤 具体代码如下 工具类 控制类 测试 前后端联调 思路 令牌生成&#xff1a;登陆成功后&#xff0c;生成JWT令牌&#xff0c;并返回给前端令牌校验&#xff1a;在请求到达服务端后&#xff0c;对令牌进行统一拦截、校验 接口文档…

c语言练习题31:字符转换

scanf(“%[^\n]“, str)正则用法 1 ^表示"非"&#xff0c;[^\n]表示读入换行字符就结束读入。这个是scanf的正则用法&#xff0c;我们都知道scanf不能接收空格符&#xff0c;一接受到空格就结束读入&#xff0c;所以不能像gets()等函数一样接受一行字符串&#xff0…