排序——交换排序(冒泡排序与快速排序)

本专栏和大家分享关于排序的算法,其中有插入排(直接插入排序和希尔排序)、选择排序(直接选择排序和堆排)、交换排序(冒泡排序和快速排序)、归并排序以及其他非基于比较的排序

本文与大家分享交换排序

目录

1.冒泡排序

时空复杂度:

稳定性:

 2.快速排序

我们通过代码来呈现大体框架

对于找基准pivot,有多种方法

Hoare法

挖坑法

双指针法

优化

三数取中法选key

递归到小的子区间时,可以考虑使用插入排序

快速排序法非递归实现

时空复杂度:

稳定性:不稳定

特点:

感谢您的访问!!期待您的关注!!!


1.冒泡排序

实则就是每次把最大值往后面放,比较简单,我们直接通过代码体现:

public class BubbleSort {
     public static void bubbleSort(int[] array){
         for(int i = 0; i < array.length - 1; i++){
             boolean flg = false;
             for(int j = 0; j < array.length - i - 1; j++){
                 if(array[j] > array[j+1]){
                     flg = true;
                     int tmp = array[j+1];
                     array[j+1] = array[j];
                     array[j] = tmp;
                 }
             }
             if(!flg){
                 break;
             }
         }
     }
 }

时空复杂度:

时间复杂度为O(N^2),最快情况下为O(1),空间复杂度为O(1)

稳定性:

是稳定的,与选择排序同理


 2.快速排序

任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

假设我们的数组是这样的,我们需要先找一个基准(这里以最左边的元素作为基准),那么我们就要想办法把数组变成左边都比6小,右边都比6大的情况

我们采用这种方法:定义变量left,让left 往右走直到遇见一个比6大的数;再定义一个遍历right,让right往左走,直到遇见一个比6小的数,此时交换left和right的元素

然后left++,right--继续按照上面的方式寻找

此时left++和right--后,二者相遇,那么就交换一开始的基准与此时left的值

这样,就能做到左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准

接着,我们开始将区间划分为两部分,两部分再分别进行上述基准的操作...

当我们某一段区间的left和right在一开始就相遇的时候,说明此时就只有一个元素,那么就是有序的了,此时把所有的区间都进行合并就会发现是有序的了

我们先忽略利用基准组织数组的方法(partition),就能把快速排序的框架用代码体现出来,我们可以利用递归来完成,即将将每次划分的区间进行基准组织操作,

那么递归的结束标志是什么呢??

我们看到上面的实例,结束标志是left == right,但是实际上还会出现一种情况:

那么在进行划分操作的时候,right = p-1,此时这种情况下是right < left,左边的递归才结束,因此,我们递归的结束条件是(left >= right)

我们通过代码来呈现大体框架

    public static void quickSort(int[] array){
        quick(array,0,array.length-1);
    }
    private static void quick(int[] array,int left,int right){
        if(left >= right){
            return;
        }
        int pivot = partition(array,start,right);
        quick(array,left,pivot-1);
        quick(array,pivot+1,right);
    }

对于找基准pivot,有多种方法

Hoare法

我们上面的示例用的方法就是Hoare法

即最左边的数作为基准,利用两个变量将数组组织成 左边的数全部小于基准,右边的数全部大于基准的情况,再把此时基准的位置返回,让quick利用基准划分数组

private static int partition(int[] array,int left,int right){
         int tmp = array[left];
         int i = left;//记录下最左边元素的下标
         while(left < right){
             while(left < right && array[right] >= tmp){
                 right--;
             }
             while(left < right && array[left] <= tmp){
                 left++;
             }
             swap(array,left,right);
         }
         swap(array,i,left);
         return left;
     }

有一个很重要的细节,就是我们一定要让right先找,因为我们最后二者相遇的点一定是小于基准的

如图所示,如果我们让left先找,那么最后left和right都会停在9的位置,就会出问题了

挖坑法

实际上与Hoare相似,但是元素处理方法不同

我们还是以最左边的数作为基准,放到tmp里面,此时与上面不同的是,放到tmp里面后,相当于left这个位置就是"空"了;right先向左走,直到找到一个比6小的,即5,那么就把5放到前面的坑里面,即left的位置:

在left++找到比6大的数,即7,就把7填到right的坑里面,依次类推...直到left和right相遇

  private static int partitionHole(int[] array,int left,int right){
         int tmp = array[left];
         while(left < right){
             while(left < right && array[right] >= tmp){
                 right--;
             }
             array[left] = array[right];
             while (left < right && array[left] <= tmp){
                 left++;
             }
             array[right] = array[left];
         }
         array[left] = tmp;
         return left;
     }

双指针法

实际上用的比较少,重在了解即可,本质就是把所有大于基准值的卷到prev和cur之间,再慢慢往后面卷

               

 

private static int partition(int[] array, int left, int right) {
int prev = left ;
int cur = left+1;
while (cur <= right) {
if(array[cur] < array[left] && array[++prev] != array[cur]) {
swap(array,cur,prev);
}
cur++;
}
swap(array,prev,left);
return prev;
}

优化

实际上快排的时间复杂度在于能否将数组尽可能均等分,可以的话就时间复杂度就小

如果我们的序列是1 2 3 4 5类似这样的序列,那么按照我们上面的方法来,以最左边的数作为基准,就会出现单分支的情况,那么时间复杂度就很高了,体现不出快排的优势

三数取中法选key

取一段区间内的开头、结尾、中间值,找出三者的中间值作为基准放在首位,这样在进行基准组织就能将每次接近均等划分

private static void quick(int[] array,int start,int end){
         if(start >= end){
             return;
         }
         int index = middleNum(array,start,end);
         swap(array,index,start);
         int pivot = partitionHole(array,start,end);
         quick(array,start,pivot - 1);
         quick(array,pivot+1,end);
     }
 ​
     private static int middleNum(int[] array,int left,int right){
         int mid = left + ((right - left) >> 1);//防止溢出
         if(array[left] < array[right]){
             if(array[mid] < array[left]) {
                 return left;
             }else if(array[mid] > array[right]){
                 return right;
             }else {
                 return mid;
             }
         }else{
             if(array[mid] < array[right]) {
                 return right;
             }else if(array[mid] > array[left]){
                 return left;
             }else {
                 return mid;
             }
         }
     }

递归到小的子区间时,可以考虑使用插入排序

 private static void quick(int[] array,int start,int end){
         if(start >= end){
             return;
         }
     //三数取中
         int index = middleNum(array,start,end);
         swap(array,index,start);
         if(end - start < 5){
             insertSort1(array,start,end);
         }
         int pivot = partitionHole(array,start,end);
         quick(array,start,pivot - 1);
         quick(array,pivot+1,end);
     }
 ​
 ​
 public static void insertSort1(int[] array,int start,int end){
         if(array.length == 0){
             return;
         }
         for (int i = start + 1; i <= end; i++) {
             int tmp = array[i];
             int j = i - 1;
             for (; j >= start ; j--) {
                 if(array[j] < tmp){
                     array[j + 1] = array[j];
                 }else{
                     break;
                 }
             }
             array[j + 1] = tmp;
         }
     }

快速排序法非递归实现

实际上就是用栈来模拟递归的过程

public static void quickSortNor(int[] array){
         int start = 0;
         int end = array.length - 1;
         int index = middleNum(array,start,end);
         swap(array,index,start);
         int pivot = partitionHole(array,start,end);
         Stack<Integer> stack = new Stack<>();
         if(pivot - 1 > start){
             stack.push(pivot - 1 );
             stack.push(start);
         }
         if(pivot + 1 < end){
             stack.push(end);
             stack.push(pivot + 1);
         }
         while(!stack.isEmpty()){
             start = stack.pop();
             end = stack.pop();
             pivot = partitionHole(array,start,end);
             if(pivot - 1 > start){
                 stack.push(pivot - 1 );
                 stack.push(start);
             }
             if(pivot + 1 < end){
                 stack.push(end);
                 stack.push(pivot + 1);
             }
         }
     }

时空复杂度:

在平均情况下,快速排序的时间复杂度为O(n log n),因为每一次递归过程中,将基准元素与其他元素进行比较,将数组分成两部分的时间复杂度为O(n),而最坏的情况下,每次划分只能减少一个元素,需要进行n次划分,所以最坏情况下的时间复杂度为O(n^2)。

空间复杂度为O(n)

稳定性:不稳定

特点:

快速排序整体的综合性能和使用场景都是比较好的

感谢您的访问!!期待您的关注!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SAP FICO 银企直联

银企直联是指企业通过互联网或专线连接的方式&#xff0c;使企业的SAP系统与商业银行的业务系统通过特定的数据接口实现连接&#xff0c;在SAP系统中可以直接查询银行账户的余额和明细&#xff0c;实现付款、银企对账、自动出具余额调节表等功能。 在这主要介绍SAP相关CALLSS配…

C++—vector的介绍及使用 vector的模拟实现

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 文章目录 前言 一、vector的介绍及使用 1.1 vector的介绍 1.2 vector的使用 1.2.1 vector的定义 1.2.2 vector iterator 的使用 1.2.3 vector 空间增长问题 1.2.4 vecto…

Python绘制线图之plt.plot()的介绍以及使用

在Python中plt.plot是matplotlib库中的一个函数,用于绘制点和线,并对其样式进行控制,下面这篇文章主要给大家介绍了关于Python绘制线图之plt.plot()的介绍以及使用的相关资料,需要的朋友可以参考下 plt.plot() 是Matplotlib库中用于绘制线图&#xff08;折线图&#xff09;的主…

【递归】有序分数(SBT)

给定一个整数 N&#xff0c;请你求出所有分母小于或等于 N&#xff0c;大小在 [0,1][0,1] 范围内的最简分数&#xff0c;并按从小到大顺序依次输出。 例如&#xff0c;当 N5时&#xff0c;所有满足条件的分数按顺序依次为&#xff1a; 0/1,1/5,1/4,1/3,2/5,1/2,3/5,2/3,3/4,4…

Python | Leetcode Python题解之第1题两数之和

题目&#xff1a; 题解&#xff1a; class Solution:def twoSum(self, nums: List[int], target: int) -> List[int]:n len(nums)for i in range(n):for j in range(i 1, n):if nums[i] nums[j] target:return [i, j]return []

GT收发器PHY层设计(4)上板验证

文章目录 前言一、实验平台二、通道1收发数据三、通道2收发数据 前言 在前面三篇内容当中详细介绍了基于GT的PHY设计&#xff0c;本篇内容进行上板测试&#xff0c;主要查看接收数据是否能正确对齐 一、实验平台 俩个光口相互通信&#xff0c;即1发2收&#xff0c;2发1收 发…

C++要学到什么程度才能找到实习?

在考虑 C 学习到何种程度可以找到实习时&#xff0c;以下是一些具体的方向和建议。我这里有一套编程入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习编程&#xff0c;不妨点个关注&#xff0c;给个评论222&#xff0c;私信22&#xff0c;我…

Win11 家庭版/专业版开启Hyper-V

​ 目录 收起 一、安装Hyper-V 二、启用Hyper-V Hyper-V是Windows专业版专属功能&#xff0c;但大多数&#xff08;除商业本&#xff09;品牌机内置的Windows都是家庭版。只能通过命令开启&#xff0c;方法如下&#xff1a; Windows专业版请直接阅读启用Hyper-V部分 一、安装Hy…

云服务器4核8G配置优惠价格表,买一年送3个月,12M公网带宽

腾讯云轻量4核8G12M服务器优惠价格646元15个月&#xff0c;买一年送3个月&#xff0c;配置为轻量4核8G12M、180GB SSD盘、2000GB月流量、12M带宽&#xff0c;腾讯云优惠活动页面 yunfuwuqiba.com/go/txy 活动链接打开如下图&#xff1a; 腾讯云4核8G服务器租用价格 腾讯云&…

中间件安全(apache、tomcat)

靶场&#xff1a; vulfocus Apache Apache HTTP Server 是美国阿帕奇&#xff08; Apache &#xff09;基金会的一款开源网页服务器。该服务器具有快速、可靠且可通过简单的API进行扩充的特点&#xff0c;发现 Apache HTTP Server 2.4.50 中针对 CVE - 2021 - 41773 的修复…

【c++】类和对象(六)深入了解隐式类型转换

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章我们来到初始化列表&#xff0c;隐式类型转换以及explicit的内容 目录 1.初始化列表1.1构造函数体赋值1.2初始化列表1.2.1隐式类型转换与复制初始化 1.3e…

R2GenCMN中的Encoder_Decoder结构

R2GenCMN中的 Encoder_Decoder 结构 Encoder_Decoder 结构直接关系到文本的生成&#xff0c;它结构参考的transformer的结构 我们这里主要看代码的实现&#xff0c;从视觉编码器的输出开始 1. 模型结构 首先介绍一下整体结构&#xff0c;这里的baseCMN其实就是一个包装了的T…

Learning from Multiple Annotator Noisy Labels via Sample-wise Label Fusion

confusion matrix P n ( r ) _n^{(r)} n(r)​ pillow8.3.1和python3.7.11的环境不好满足&#xff0c;不建议复现

前端学习<二>CSS基础——12-CSS3属性详解:动画详解

前言 本文主要内容&#xff1a; 过渡&#xff1a;transition 2D 转换 transform 3D 转换 transform 动画&#xff1a;animation 过渡&#xff1a;transition transition的中文含义是过渡。过渡是CSS3中具有颠覆性的一个特征&#xff0c;可以实现元素不同状态间的平滑过渡…

LeetCode226:反转二叉树

题目描述 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 解题思想 使用前序遍历和后序遍历比较方便 代码 class Solution { public:TreeNode* invertTree(TreeNode* root) {if (root nullptr) return root;swap(root->left, root…

查生意在SFE连锁加盟展会展示口碑力量,助力创业者精准抉择

在这个信息爆炸的时代&#xff0c;对于广大创业者而言&#xff0c;选择正确的连锁加盟项目犹如在繁星中寻找璀璨北斗。为了更好地服务于这个需求日益增长的市场&#xff0c;查生意一站式连锁经营口碑评分查询服务平台应运而生&#xff0c;并已在上海连锁加盟展会&#xff08;SF…

<PaddlePaddle学习使用P1>——《PaddlePaddle教程》

一、PaddlePaddle概述 1.什么是PaddlePaddle PaddlePaddle官网地址链接&#xff1a;https://www.paddlepaddle.org.cn/ 为什么学习PaddlePaddle&#xff1a; 2.PaddlePaddle特点 PaddlePaddle优点&#xff08;目前&#xff09;&#xff1a; PaddlePaddle缺点&#xff08;目…

c语言例题,实现一个整型有序数组的二分查找

c语言中&#xff0c;有很多可以实现效果的方法&#xff0c;而在一个整型有序的数组中&#xff0c;我们可以使用二分查找的方法来实现对于一个数组中的元素查找。二分查找的优点在于本身需要的计算是比较少的&#xff0c;一次计算查找排除掉数组中一半的元素&#xff0c;尤其对于…

C语言自定义类型

本篇文章主要介绍三种自定义类型&#xff0c;分别是&#xff1a;结构体、联合体、枚举。 一.结构体 1.结构体类型的声明 直接举一个例子&#xff1a; //一本书 struct s {char name[10]; //名称char a; //作者int p; //价格 }; 2.特殊的声明 结构体也可以不写结构体标…

7_springboot_shiro_jwt_多端认证鉴权_自定义AuthenticationToken

1. 目标 ​ 本小节会先对Shiro的核心流程进行一次回顾&#xff0c;并进行梳理。然后会介绍如果应用是以API接口的方式提供给它方进行调用&#xff0c;那么在这种情况下如何使用Shiro框架来完成接口调用的认证和授权。 2. 核心架构 引用官方的架构图&#xff1a; 2.1 Subje…