从LeetCode215看排序算法

目录

LeetCode215 数组的第K个最大元素

 ① 第一反应:java的内置排序Arrays.sort()

② 冒泡排序

③归并排序(先分解再合并)

④快速排序(边分解边排序)

⑤堆排序


LeetCode215 数组的第K个最大元素

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

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

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

示例 1:

输入: [3,2,1,5,6,4], k = 2
输出: 5

示例 2:

输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4

 ① 第一反应:java的内置排序Arrays.sort()

arrays.sort的复杂度取决于所使用的排序算法。在java中,Arays类中的sort方法使用的是一种优化的快速排序算法或归并排序算法
对于快速排席算法,其平均时间复杂度为0(nl0gn),其中n是数组的大小。然而,在最坏情况下,快速排序的时间复杂度可以达到O(n^2),这发生在数组已经有序的情况下。
对于归并排序算法,其时间复杂度始终为0(nlog n),不论输入数据的情况如何。
总结起来,Arrays.sort方法的平均时间复杂度为0(nlog n),最坏情况下可能为O(n^2)。


② 冒泡排序

思路:总结来说就是两个for循环。多次遍历数组,每次遍历通过不断比较相邻元素的大小,如果左边大于右边就交换元素顺序,最终会将一个最大(或最小的)元素冒泡到数组的末尾或开头。直到最终没有任何元素需要交换(end一直--)。

举个例子,假设我们有一组数字:3, 38, 5, 44, 15, 47, 36, 26, 27, 2, 46, 4, 19, 50, 48。

下面是冒泡排序的执行过程:
1.第一轮比较后,最大的数字 50 被冒泡到了数组末尾,数组变为:3, 5, 38, 15, 44, 36, 26, 27, 2, 46, 4, 19, 47, 48, 50

2.第二轮比较后,第二大的数字 48 被冒泡到了倒数第二的位置,数组变为:3, 5, 15, 38, 36, 26, 27, 2, 44, 4, 19, 46, 47, 48, 50

3.经过多轮比较和交换后,所有数字按照从小到大的顺序排列完成。

第k大只要找到第k个泡泡即可,无需向后比较对整个数组进行排序

class Solution {public int findKthLargest(int[] nums, int k) {return bubbleSort(nums, k);}int bubbleSort(int[] nums, int k){int n = nums.length;int count = 0;for(int i=0;i<n-1;i++){for(int j=0;j<n-i-1;j++){if(nums[j] > nums[j+1]){int tmp =nums[j];nums[j] = nums[j+1];nums[j+1] = tmp;}}count++;if(count == k){break;}}return nums[n-k];}
}

时间复杂度:最坏情况:O(N^2)
      最好情况:O(N)
空间复杂度:O(1)
 


③归并排序(先分解再合并)

思路:通过分治法将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。与快速排序相比,快速排序是将序列划分成两个子序列,再递归使子序列有序;而归并排序是先使子序列有序,再进行合并。

运算符小妙用:

偶数&1 结果为0;奇数&1 结果为1;

左移相当于乘以2^n;右移相当于除以2^n,所以通常会用>>1来代替除以2。

不用第三个变量交换两个整数:

    void swap(int x , int y){x ^= y;y ^= x;x ^= y;}

不断从中间划分子数组,递归合并

public class MergeSort {   public static int[] mergeSort(int[] nums, int l, int h) {if (l == h)return new int[] { nums[l] };int mid = l + (h - l) / 2;int[] leftArr = mergeSort(nums, l, mid); //左有序数组int[] rightArr = mergeSort(nums, mid + 1, h); //右有序数组int[] newNum = new int[leftArr.length + rightArr.length]; //新有序数组int m = 0, i = 0, j = 0; while (i < leftArr.length && j < rightArr.length) {newNum[m++] = leftArr[i] < rightArr[j] ? leftArr[i++] : rightArr[j++];}while (i < leftArr.length)newNum[m++] = leftArr[i++];while (j < rightArr.length)newNum[m++] = rightArr[j++];return newNum;}

 LeetCode 4:寻找两个正序数组的中位数

这道题就用到了归并排序

class Solution {public double findMedianSortedArrays(int[] nums1, int[] nums2) {int totalLength = nums1.length + nums2.length;int[] nums = new int[totalLength];int i = 0, j = 0;int index = 0;while (index <= totalLength /2) {if (i < nums1.length && (j >= nums2.length || nums1[i] < nums2[j])) {nums[index] = nums1[i++];} else{nums[index] = nums2[j++];}if (index == totalLength / 2) {//nums数组长度为偶数if ((totalLength & 1) == 0) {return (nums[index] + nums[index - 1]) / 2.0;} else {//nums数组长度是奇数return 1.0 * nums[index];}}index++;}return 0.0;}
}

时间复杂度:O(nlogn)(稳定,因为每次都是从中间划分)

空间复杂度:递归造成栈空间的使用,最好O(nlogn),最坏O(n)


④快速排序(边分解边排序)

思路:通过分治法将一个数组分成较小的子数组,然后递归地对每个子数组进行排序。

移动左右指针,左指针向右移动,直到找到一个大于等于分界值的元素;右指针向左移动,直到找到一个小于等于分界值的元素。交换这两个元素的位置,然后再次移动左右指针,直到左指针和右指针指向同一个元素,再递归左右子数组。

快速排序找到第k个最大的,就是排序后递增子数组的倒数第k个(序号为n-k)

class Solution {public int findKthLargest(int[] nums, int k) {int n = nums.length;return quickSelect(nums, 0, n-1, n-k);}public int quickSelect(int[] nums, int l, int r, int k){if(l == r)  return nums[k];int x = nums[l], i = l-1, j = r+1;while(i < j){do i++;  while(nums[i] < x);do j--;  while(nums[j] > x);if(i < j){int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}}//这个时候i==j==x,要寻找的坐标<=j则说明第k大元素在分界元素左边if(k <= j)  return quickSelect(nums, l, j, k);else return quickSelect(nums, j+1, r, k);}
}

快排的时间复杂度我们可以认为是O(nlogn),但当遇到原数组本身有序的情况下,其时间复杂度就会退化至O(n^2),这个其实很好理解,举个例子就明白了:

最优情况下,即每趟选择key时都恰好选择到数组的中间值时(第n层可以确定2^{n-1}个数字位置),快排的时间复杂度如下图完全(满)二叉树:


在最坏的情况下,这个树是一个完全的斜树,只有左半边或者右半边,即每趟选择key时都恰好选择到数组最大或最小的值时(即每一层都只能确定一个数字位置)。这时候我们的比较次数就变为∑n−1i=1(n−i)=(n−1)+(n−2)+⋯+1=n∗(n−1)2=O(n^2)

时间复杂度:最好O(nlogn),最坏O(n^2)

空间复杂度:递归造成栈空间的使用,最好O(nlogn),最坏O(n)


⑤堆排序

java提供了PriorityQueue,可以构建小顶堆

class Solution {public int findKthLargest(int[] nums, int k) {PriorityQueue<Integer> queue = new PriorityQueue<>(); //小顶堆for(int num : nums){queue.add(num);if(queue.size() > k){queue.poll();}}return queue.peek();}
}

自己构建堆 

class Solution {public int findKthLargest(int[] nums, int k) {//-------------1.首先,构建最大堆[6,5,4,3,2,1]----------------//建大堆:要从堆的最后一个非叶子节点开始向下调整for (int i = nums.length/2-1; i >= 0; i--) {adjustHeap(nums,i,nums.length);}//---2.每次把最大的换到最后一个位置,调整堆,交换后不把最后一个数看作堆里的数据-------for(int i = nums.length-1;i >= nums.length-k ;i--){swap(nums,0,i);adjustHeap(nums,0,i);    //选出次小的数}return nums[nums.length-k];}//向下调整算法public static void adjustHeap(int[] nums,int node,int tail){int left = node*2+1;int right = node*2+2;//-------------大的往上浮,小的往下浮--------------------int max = node;if(left<tail&&nums[left]>nums[max]){max=left;}if(right<tail&&nums[right]>nums[max]){max=right;}//---------max是父节点和左右子节点中最大的数-------------------//max!=node证明左右子节点有比父节点大的数,需要进行交换if(max!=node){swap(nums,max,node);adjustHeap(nums,max,tail);}}public static void swap(int[] nums, int index1, int index2){int tmp = nums[index1];nums[index1]=nums[index2];nums[index2]=tmp;}
}

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

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

相关文章

Spring Boot有哪些优点和缺点

Spring Boot作为一个基于Spring框架的开源项目&#xff0c;旨在简化Spring应用的初始搭建以及开发过程。以下是Spring Boot的主要优点和缺点&#xff1a; 优点 简化项目配置&#xff1a; Spring Boot采用“约定优于配置”的原则&#xff0c;通过自动配置提供默认配置&#xff…

ubuntu基于cmakelist的Qt工程,如何将图片打包进二进制程序

qt界面使用的图片打包进入二进制可执行程序&#xff0c;可以避免发布的软件&#xff0c;因为路径问题无法加载图片的问题。 以下步骤参考自百度AI. 步骤如下&#xff1a; 1.创建一个新的Qt资源文件&#xff08;.qrc文件&#xff09; 2.在*.qrc文件中添加图片路径 qrc文件使用…

【ceph】ceph集群-添加/删除mon

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…

无障碍开发的艺术:Xcode打造可访问性应用全指南

无障碍开发的艺术&#xff1a;Xcode打造可访问性应用全指南 在软件开发中&#xff0c;可访问性&#xff08;Accessibility&#xff09;开发是确保应用能够被更广泛用户群体使用的关键。Xcode作为苹果公司提供的集成开发环境&#xff08;IDE&#xff09;&#xff0c;提供了一系…

LeetCode HOT100(四)字串

和为 K 的子数组&#xff08;mid&#xff09; 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列。 输入&#xff1a;nums [1,1,1], k 2 输出&#xff1a;2 解法1&#xff1a;前缀和Map 这…

(Vue+SpringBoot+elementUi+WangEditer)仿论坛项目

项目使用到的技术与库 1.前端 Vue2 elementUi Cookie WangEditer 2.后端 SpringBoot Mybatis-Plus 3.数据库 MySql 一、效果展示 1.1主页效果&#xff1a; 1.2 文章编辑页面&#xff1a; 1.3 成功发布文章 1.4 文章关键字搜索提示 1.5 文章查询结果展示 1.6 文章内容及交互展示…

期货量化交易客户端开源教学第一节——交易服务器的安装

上传“交易服务端”到服务器把“交易服务器版本2”解压到dht unzip 交易服务端.ziprpm -Uvh mysql-community-release-el6-5.noarch.rpm (tab键)yum install mysql-community-server //y yvi /etc/selinux/config 将SELINUX=enforcing改为SELINUX=disabled(需要重启)//阿里云已…

linux怎么查看系统重启原因?LINUX系统不明原因重启解决步骤,在Linux中如何排查系统启动问题?

linux怎么查看系统重启原因&#xff1f;LINUX系统不明原因重启解决步骤&#xff0c;在Linux中如何排查系统启动问题&#xff1f; linux怎么查看系统重启原因&#xff0c;网上大部分总结的步骤如下&#xff1a; 查看系统日志&#xff1a;系统日志中存储了系统重启异常的情况及其…

TransT: 基于类型的多重嵌入表示用于知识图谱补全

1 引言 1.1 问题 仅仅依赖于三元组的结构化信息有其局限性&#xff0c;因为它们往往忽略了知识图谱中丰富的语义信息以及由这些语义信息所代表的先验知识。语义信息是指实体和关系的含义&#xff0c;比如“北京”是“中国”的首都&#xff0c;“苹果”是一种水果。先验知识则…

ESP32CAM物联网教学12

ESP32CAM物联网教学12 MicroPython 视频服务 小智希望能在MicroPython中实现摄像头的视频服务&#xff0c;就像官方示例程序CameraWebServer那样。 下载视频服务驱动库 小智通过上网搜索&#xff0c;发现相关的教学材料还不少&#xff0c;并且知道有人已经写出了视频服务的驱…

Redis的哨兵和集群实现高可用

一个典型的高可用Redis集群示例配置 1个主服务器 2-3个从服务器 3-5个哨兵 哨兵和集群就是为了高可用 哨兵 哨兵的功能&#xff1a;监听和故障转移 &#xff08;1&#xff09;客户端可以从哨兵获得集群的状态。 &#xff08;2&#xff09;当主服务器断开&#xff0c;哨兵可…

UI图标库推荐网站

在推荐图标库时&#xff0c;我会考虑图标的多样性、质量、易用性以及是否免费或开源等因素。以下是一些值得推荐的图标库&#xff1a; 1. Font Awesome 特点&#xff1a;Font Awesome是一套绝佳的图标字体库和CSS框架&#xff0c;提供可缩放的矢量图标。您可以使用CSS所提供的…

24/7/12总结

axios Axios 是一个基于 promise 网络请求库&#xff0c;作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http 模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。 get请求: <script>function…

【Linux网络】数据链路层【下】{MAC/MTU/ARP/ICMP/NAT/PING/代理服务器原理}

文章目录 1.逐步深入数据链路层1.1MAC帧1.2由集线器到交换机1.3认识MTU 2.ARP 地址解析协议/RARP逆地址解析协议3.DNS(Domain Name System)域名从输入url后到能看到网页 发生了什么【典中典】 4.ICMP协议&#xff1a;一个网络层协议有了TCP&#xff0c;为什么还要用ICMPICMP协议…

面向对象程序设计之常用集合类

容器集合类&#xff0c;是后端的基础&#xff0c;本文以面向对象语言c#与java为例&#xff0c;了解常用的集合类。 c# List<T>: System.Collections.Generic.List<T> 是动态数组的泛型集合类。它提供了类型安全性和高效的元素访问。 List<string> list ne…

uboot学习:(一)基础认知

目录 uboot是一个裸机程序&#xff08;bootloader&#xff09; 作用 要运行linux系统时&#xff0c;如何从外置的flash拷贝到DDR中&#xff0c;才能启动 uboot使用步骤 步骤1中的命令例子 注意 uboot源码获取方法 uboot是一个裸机程序&#xff08;bootloader&#xff09…

1、预处理

C语言预处理 预处理是C语言编译过程中的第一个阶段&#xff0c;它主要处理以 # 开头的预处理指令。预处理器会在实际编译之前对源代码进行一些文本操作。 1. 宏定义 (#define) 宏定义用于定义常量或者简单的函数。 1.1 简单的宏定义 // 定义圆周率常量 #define PI 3.14159…

解决RedisTemplate配置JSON序列化后@Cacheable序列化仍然是JDK序列化的问题

问题现象 在参考网上的Redis集成后&#xff0c;配置了RedisTemplate的序列化&#xff0c;配置成功后Cacheable注解的缓存仍然是jdk的序列化&#xff0c;配置无效。 参考配置的类似代码&#xff1a; Bean("redisTemplate") public RedisTemplate<Object, Objec…

[Python学习篇] Python Socket网络编程

Python中的socket编程是通过内置的socket模块实现的&#xff0c;可以方便地创建网络应用程序&#xff0c;包括客户端和服务器。 服务端 1. 创建套接字 使用socket.socket()函数创建一个TCP套接字。套接字可以是流式&#xff08;TCP&#xff09;或数据报式&#xff08;UDP&…