912. Sort an Array排序复习 快速排序,选择排序,插入排序,归并排序,堆排序

本文主要是介绍912. Sort an Array排序复习 快速排序,选择排序,插入排序,归并排序,堆排序,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

快速排序

选择排序 

插入排序

归并排序

堆排序

总结


Given an array of integers nums, sort the array in ascending order.

Example 1:

Input: nums = [5,2,3,1]
Output: [1,2,3,5]

Example 2:

Input: nums = [5,1,1,2,0,0]
Output: [0,0,1,1,2,5]

Constraints:

  • 1 <= nums.length <= 50000
  • -50000 <= nums[i] <= 50000

题目链接: Loading...

快速排序

class Solution {
public:void quicksort(vector<int>&nums,int left,int right){if(left>=right)//此处应该是大于等于,等于的时候就可以停止了{return ;}int i=left,j=right,key=nums[left];while(i<j){while(i<j&&nums[j]>=key){//选出来的nums[j] 小于 keyj--;}nums[i]=nums[j];while(i<j&&nums[i]<=key)//选出来的nums[i] 大于 key{i++;}nums[j]=nums[i];}nums[i]=key;quicksort(nums,left,i-1);quicksort(nums,i+1,right);}vector<int> sortArray(vector<int>& nums) {quicksort(nums,0,nums.size()-1);return nums;}
};

快速排序是不稳定的,为什么呢?

经过第一次sort后,对于j,j=5就停止了,对于i,i一直++到5。

这样赋值后,红色的9跑到黑色的9后面了。 

有的例子就刚好不会变,下面这种就不会变化。

选择排序 

是想复习一下,但是选择排序复杂度比较高,过不了。这些排序的复杂度表

void selectSort(vector<int>&nums){int len=nums.size();int index=0;for(int i=0;i<len;i++){index=i;for(int j=i+1;j<len;j++){if(nums[j]<nums[index]){index=j;}}int tmp=nums[i];nums[i]=nums[index];nums[index]=tmp;}}

插入排序

也是超时,原理很简单,在从小到大的排序方法中,就是  左边有序序列,新来一个值newvalue,就和左边有序序列比较,只要比我,我就往右边挪位置,nums[j+1]=nums[j]。

void insertSort(vector<int>& nums){int len=nums.size(),tmp=0,i=0,j=0;for(i=1;i<len;i++){tmp=nums[i];for(j=i-1;j>=0&&tmp<nums[j];j--){nums[j+1]=nums[j];//nums[j]小于 tmp,往后挪位置}nums[j+1]=tmp;//j退出时候,要么j=-1,或者 nums[j]< tmp,那你tmp只能放在 j+1的位置上}}

归并排序

这道题在归并排序上真是给我好好上了一课,之前写的归并排序没有问题,但是有一些额外的空间浪费和时间开销。就是我全行注释那行,如果每次都是按照nums的真实长度来初始化tmp,会浪费时间和空间,这题LeetCode会超时。刚开始我百思不得其解,后来发现是这个问题,真是学习了。我之前写的归并代码:随手写写之归并排序

    void mergeArray(vector<int>& nums,int start,int mid,int last){vector<int> tmp(last-start+1,0);// vector<int> tmp(nums.size(),0);这一点真的很棒,考虑数组元素多的时候,这样初始化很浪费时间,会超时,这点leetcode给我上了一课。int i=start,j=mid+1,k=0;//k记录start位置,tmp从哪个下标开始while(i<=mid&&j<=last){if(nums[i]<nums[j]){tmp[k++]=nums[i++];}else{tmp[k++]=nums[j++];}}while(i<=mid){tmp[k++]=nums[i++];}while(j<=last){tmp[k++]=nums[j++];}for(int t=start;t<=last;t++){nums[t]=tmp[t-start];}}void mergeSort(vector<int>& nums,int start,int last){if(start<last){int mid=(start+last)/2;mergeSort(nums,start,mid);mergeSort(nums,mid+1,last);mergeArray(nums,start,mid,last);}}

  

堆排序

堆排序(这篇博客说的挺好的排序六 堆排序)是使用大根堆(父节点不小于子节点,为啥从下往上调整呢,看个例子

把3和1调完之后, 根节点还要调整(因为4是最大的,4还要和根节点调整,导致根节点的值换了2次,从下往上调整,根节点只需要调整一次就行了,这样从下往上调整,所有非叶子节点调整一次,绝对就是对的了,不用考虑有的节点是不是还要调整一次),一个节点就可能重复调整,不止一次。从下往上调整,省去了父节点二次调整,简单省事了,不用考虑二次调整了。

这个例子第一次的调整构造初始堆过程如下图:

上面这个例子层数有点少,2种遍历都是调整3次,但是对于一个节点,既是父节点又是子节点,它的子节点还有子节点 ,步骤就上来了,比如下图的数字4

void heapAdjust(vector <int> & arr, int parent,int N){int largest = parent;int child=2*parent+1;while(child<N)//N代表整个数组的元素个数,下标从0开始,child必须小于N{if(arr[child]>arr[largest]){largest=child;}if(child+1<N&&arr[child+1]>arr[largest]){largest=child+1;child++;//更新child的值}swap(arr[parent],arr[largest]);//交换parent=largest;// parent=child;在没有child大于parent的时候,largest还是parent,child是2*parent+1,所以不能这么写child=2*child+1;//child根据child值翻倍,这样如果么有需要更新的,parent一直不变,child一直增大会退出}}void heapSort(vector<int>& nums){for(int i = nums.size()/2; i >= 0; i--){//从最后一个非叶子节点开始调整heapAdjust(nums,i,nums.size());}for(int i = nums.size()-1; i >= 0; i--){swap(nums[0] , nums[i]);//下标0存放的是调整后最大值,交换后放到下标 i(当前未排序的最后位置)heapAdjust(nums,0,i);//i表示调整的元素个数,i从 size()-1  开始}}

堆排序是不稳定的,看个图,原先的数组顺序是 1 2 2,调整之后 1 2 2,黄框是排序一次后的堆结构。

 

总结

对于这道题,我把所有排序方法都汇总在一起了

class Solution {
public:
void quickSort(vector<int>&nums,int left,int right){if(left>right){return ;}int i=left,j=right,key=nums[left];while(i<j){while(i<j&&nums[j]>=key){//选出来的nums[j] 小于 keyj--;}nums[i]=nums[j];while(i<j&&nums[i]<=key)//选出来的nums[i] 大于 key{i++;}nums[j]=nums[i];}nums[i]=key;quickSort(nums,left,i-1);quickSort(nums,i+1,right);}void selectSort(vector<int>&nums){int len=nums.size();int index=0;for(int i=0;i<len;i++){index=i;for(int j=i+1;j<len;j++){if(nums[j]<nums[index]){index=j;}}int tmp=nums[i];nums[i]=nums[index];nums[index]=tmp;}}void insertSort(vector<int>& nums){int len=nums.size(),tmp=0,i=0,j=0;for(i=1;i<len;i++){tmp=nums[i];for(j=i-1;j>=0&&tmp<nums[j];j--){nums[j+1]=nums[j];//nums[j]小于 tmp,往后挪位置}nums[j+1]=tmp;//j退出时候,要么j=-1,或者 nums[j]< tmp,那你tmp只能放在 j+1的位置上}}void mergeArray(vector<int>& nums,int start,int mid,int last){vector<int> tmp(last-start+1,0);// vector<int> tmp(nums.size(),0);这一点真的很棒,考虑数组元素多的时候,这样初始化很浪费时间,会超时,这点leetcode给我上了一课。int i=start,j=mid+1,k=0;//k记录start位置,tmp从哪个下标开始while(i<=mid&&j<=last){if(nums[i]<nums[j]){tmp[k++]=nums[i++];}else{tmp[k++]=nums[j++];}}while(i<=mid){tmp[k++]=nums[i++];}while(j<=last){tmp[k++]=nums[j++];}for(int t=start;t<=last;t++){nums[t]=tmp[t-start];}}void mergeSort(vector<int>& nums,int start,int last){if(start<last){int mid=(start+last)/2;mergeSort(nums,start,mid);mergeSort(nums,mid+1,last);mergeArray(nums,start,mid,last);}}void heapAdjust(vector <int> & arr, int parent,int N){int largest = parent;int child=2*parent+1;while(child<N)//N代表整个数组的元素个数,下标从0开始,child必须小于N{if(arr[child]>arr[largest]){largest=child;}if(child+1<N&&arr[child+1]>arr[largest]){largest=child+1;child++;//更新child的值}swap(arr[parent],arr[largest]);//交换parent=largest;// parent=child;在没有child大于parent的时候,largest还是parent,child是2*parent+1,所以不能这么写child=2*child+1;}// int left = 2*parent+1;// int right = 2*parent+2;// if(left < N && arr[left] > arr[largest])//     largest = left;// if(right < N && arr[right] > arr[largest])//     largest = right;// if(largest != parent){//     swap(arr[parent], arr[largest]);//     heapAdjust(arr,largest,N);// }}void heapSort(vector<int>& nums){for(int i = nums.size()/2; i >= 0; i--){//从最后一个非叶子节点开始调整heapAdjust(nums,i,nums.size());}for(auto it:nums){cout<<it<<endl;}for(int i = nums.size()-1; i >= 0; i--){swap(nums[0] , nums[i]);//下标0存放的是调整后最大值,交换后放到下标 i(当前未排序的最后位置)heapAdjust(nums,0,i);//i表示调整的元素个数,i从 size()-1  开始}}vector<int> sortArray(vector<int>& nums) {// quickSort(nums,0,nums.size()-1);// selectSort(nums);// insertSort(nums);// mergeSort(nums,0,nums.size()-1);heapSort(nums);return nums;}};

复杂度汇总 

 

这篇关于912. Sort an Array排序复习 快速排序,选择排序,插入排序,归并排序,堆排序的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/289036

相关文章

Python多线程实现大文件快速下载的代码实现

《Python多线程实现大文件快速下载的代码实现》在互联网时代,文件下载是日常操作之一,尤其是大文件,然而,网络条件不稳定或带宽有限时,下载速度会变得很慢,本文将介绍如何使用Python实现多线程下载... 目录引言一、多线程下载原理二、python实现多线程下载代码说明:三、实战案例四、注意事项五、总结引

C#使用Spire.XLS快速生成多表格Excel文件

《C#使用Spire.XLS快速生成多表格Excel文件》在日常开发中,我们经常需要将业务数据导出为结构清晰的Excel文件,本文将手把手教你使用Spire.XLS这个强大的.NET组件,只需几行C#... 目录一、Spire.XLS核心优势清单1.1 性能碾压:从3秒到0.5秒的质变1.2 批量操作的优雅

Python中的sort()和sorted()用法示例解析

《Python中的sort()和sorted()用法示例解析》本文给大家介绍Python中list.sort()和sorted()的使用区别,详细介绍其参数功能及Timsort排序算法特性,涵盖自适应... 目录一、list.sort()参数说明常用内置函数基本用法示例自定义函数示例lambda表达式示例o

Mybatis-Plus 3.5.12 分页拦截器消失的问题及快速解决方法

《Mybatis-Plus3.5.12分页拦截器消失的问题及快速解决方法》作为Java开发者,我们都爱用Mybatis-Plus简化CRUD操作,尤其是它的分页功能,几行代码就能搞定复杂的分页查询... 目录一、问题场景:分页拦截器突然 “失踪”二、问题根源:依赖拆分惹的祸三、解决办法:添加扩展依赖四、分页

c++日志库log4cplus快速入门小结

《c++日志库log4cplus快速入门小结》文章浏览阅读1.1w次,点赞9次,收藏44次。本文介绍Log4cplus,一种适用于C++的线程安全日志记录API,提供灵活的日志管理和配置控制。文章涵盖... 目录简介日志等级配置文件使用关于初始化使用示例总结参考资料简介log4j 用于Java,log4c

C++归并排序代码实现示例代码

《C++归并排序代码实现示例代码》归并排序将待排序数组分成两个子数组,分别对这两个子数组进行排序,然后将排序好的子数组合并,得到排序后的数组,:本文主要介绍C++归并排序代码实现的相关资料,需要的... 目录1 算法核心思想2 代码实现3 算法时间复杂度1 算法核心思想归并排序是一种高效的排序方式,需要用

使用Redis快速实现共享Session登录的详细步骤

《使用Redis快速实现共享Session登录的详细步骤》在Web开发中,Session通常用于存储用户的会话信息,允许用户在多个页面之间保持登录状态,Redis是一个开源的高性能键值数据库,广泛用于... 目录前言实现原理:步骤:使用Redis实现共享Session登录1. 引入Redis依赖2. 配置R

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

一文详解Java Stream的sorted自定义排序

《一文详解JavaStream的sorted自定义排序》Javastream中的sorted方法是用于对流中的元素进行排序的方法,它可以接受一个comparator参数,用于指定排序规则,sorte... 目录一、sorted 操作的基础原理二、自定义排序的实现方式1. Comparator 接口的 Lam

Linux如何快速检查服务器的硬件配置和性能指标

《Linux如何快速检查服务器的硬件配置和性能指标》在运维和开发工作中,我们经常需要快速检查Linux服务器的硬件配置和性能指标,本文将以CentOS为例,介绍如何通过命令行快速获取这些关键信息,... 目录引言一、查询CPU核心数编程(几C?)1. 使用 nproc(最简单)2. 使用 lscpu(详细信