【交换排序】冒泡排序 与 快速排序

news2024/9/30 19:32:03

交换排序基本思想:

所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

目录

1.冒泡排序

2.快速排序

2.1 递归实现 

2.2 快速排序优化

2.3 非递归实现


1.冒泡排序

假设升序。每次遍历,两两比较,将大的元素向后交换,直到选出最大的元素放在最后,这时已经确定了升序中最后一个元素,然后多次遍历前面无序的元素,每次可以确定一个最大的数,直到排序完成。

动态图解:

代码实现:

//交换函数
void Swap(int* p1, int* p2)
{
	int t = *p1;
	*p1 = *p2;
	*p2 = t;
}

void BubbleSort(int* arr, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		for (int j = 0; j < n - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				Swap(&arr[j], &arr[j + 1]);
			}
		}
	}
}

如果在排序有序数据时,我们还可以优化一下,提高一下效率,代码如下

void BubbleSort(int* arr, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
        int flag = 1;//假设已经有序
		for (int j = 0; j < n - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				Swap(&arr[j], &arr[j + 1]);
                flag = 0;//发生交换,说明无序
			}
		}
        if(flag == 1)//已经有序,不在继续排序
        {
            break;
        }
	}
}

冒泡排序的特性总结:

  1. 冒泡排序是─种非常容易理解的排序
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:稳定
     

2.快速排序

2.1 递归实现 

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值key,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值key,右子序列中所有元素均大于基准值key,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

图示:

代码实现:

假设按照升序对array数组中【left, right】左右都是闭区间中的元素进行排序。

void QuickSort(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}

    //按照基准值(中间位置)对array数组的[left, right]区间中的元素进行划分
	int keyi = PartSort(a, left, right);

    //划分成功后以div为边界形成了左右两部分[left, keyi-1]和[key+1,right]
    //递归排 [left, keyi-1]
	QuickSort(a, left, keyi - 1);

    //递归排[key+1,right]
	QuickSort(a, keyi + 1, right);
}

上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像,大家在写递归框架时可想想二叉树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。

将区间按照基准值划分为左右两半部分(PartSort)的常见方式有:

1. hoare 版本

我们先看一下动图,方便理解 

巧妙之处: 

  1. 左边做key,右边先走;保障了相遇位置的值比key小,或者就是key的位置
  2. 右边做key,左边先走;保障了相遇位置的值比key大,或者就是key的位置

我们这里使用第一种方法:

L和R相遇无非就是两种情况,L遇R和R遇L:

  1. 情况一:L遇R,R是停下来,L在走,R先走,R停下来的位置一定比key小,相遇的位置就是R停下的位置,就一定比key要小
  2. 情况二:R遇L,在相遇这一轮,L就没动,R在移动,跟L相遇,相遇位置就是L的位置,L的位置就是key的位置或者这个位置交换过,换成了比key小的,相遇L位置一定比key小

代码实现:

int PartSort1(int* a, int left, int right)
{
	int keyi = left;
	while (left < right)
	{
		//右边找小 与key相等的数据放在左边右边都可以
		while (left < right && a[right] >= a[keyi])
		{
			right--;
		}
		//左边找大 与key相等的数据放在左边右边都可以
		while (left < right && a[left] <= a[keyi])
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[keyi], &a[right]);
	return right;
}

2. 挖坑法

 left 和 right 中有一个一定是坑位,右边找小,左边找大,找到就将值放入原坑位,该位置成为新坑位。

代码实现:

int PartSort2(int* a, int left, int right)
{
	int hole = left;
	int key = a[left];
	while (left < right)
	{
		//右边找小
		while (left < right && a[right] >= key)
		{
			right--;
		}
		a[hole] = a[right];
		hole = right;
		//左边找大
		while (left < right && a[left] <= key)
		{
			left++;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;
	return hole;
}

3. 前后指针版本

  1. 最开始prev和cur相邻的
  2. 当cur遇到比key的大的值以后,他们之间的值都是比key大的值
  3. cur找小,找到小的以后,跟++prev位置的值交换,相当于把大翻滚式往右边推同时把小的换到左边
     

 代码实现:

int PartSort3(int* a, int left, int right)
{
	int keyi = left;
	int prev = left;
	int cur = left + 1;
	while (cur <= right)
	{
		if (a[cur] <= a[keyi]&&++prev!=cur)//自己不与自己交换
		{
			Swap(&a[cur], &a[prev]);
		}
		cur++;
		
	}
	Swap(&a[prev], &a[keyi]);
	return prev;
}

2.2 快速排序优化

当我们遇到有序数据时,由于我们的key是选的第一个元素,时间复杂度会变成O(N^2)。有两种优化方法:

  1. 三数取中法选key
  2. 随机数选key

 我们这里讲一下第一种方法:让三个数作比较,取中间值

三数取中,代码实现:

int GetMidIndex(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if(a[left]>a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
	else//a[left]>a[mid]
	{
		if (a[mid] > a[right])
		{
			return mid;
		}
		else if (a[left] > a[right])
		{
			return right;
		}
		else
		{
			return left;
		}
	}
}

然后我们需要在上面3种划分方式开头都加上下面代码,就可以达到优化的目的

int midi = GetMidIndex(a, left, right);
Swap(&a[midi], &a[left]);

2.3 非递归实现

我们这里使用栈来实现,栈内存放需要划分的区间端点值,利用栈先入后出的特点模拟实现递归

void QuickSortNonR(int* a, int left, int right)
{
	Stack st;
	StackInit(&st);
	//入栈
	StackPush(&st, right);
	StackPush(&st, left);

	while (!StackEmpty(&st))
	{
		left = StackTop(&st);
		StackPop(&st);
		right = StackTop(&st);
		StackPop(&st);

		int keyi = PartSort1(a, left, right);
		//想先处理左边,就先右边区间先入栈

        //以基准值为分割点,形成左右两部分:[left, keyi-1]和[keyi+1,right)
		if(right > keyi + 1)
		{
			StackPush(&st, right);
			StackPush(&st, keyi + 1);
		}
		if (left < keyi - 1)
		{
			StackPush(&st, keyi - 1);
			StackPush(&st, left);
		}
	}

	StackDestroy(&st);
}

当然也可以使用队列来模拟。队列相当于广度优先,二叉树中的层序遍历,栈是深度优先,二叉树的先序遍历。

快速排序的特性总结:

  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(logN)
  4. 稳定性:不稳定

本篇结束!我们下一篇文章来学习排序第四课【归并排序】。

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

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

相关文章

Day 76:通用BP神经网络 (3. 综合测试)

1 代码&#xff1a; package dl;import java.util.Arrays;/*** Full ANN with a number of layers.** author Fan Min minfanphd163.com.*/ public class FullAnn extends GeneralAnn {/*** The layers.*/AnnLayer[] layers;/*********************** The first constructor.*…

Python爬虫——selenium的安装和基本使用

1.什么是selenium&#xff1f; selenium是一个用于web应用程序测试的工具selenium测试直接运行在浏览器中&#xff0c;就像真正的用户在操作一样支持通过各种driver&#xff08;FrifoxDriver&#xff0c;ItenrentExploreDriver&#xff0c;OperaDriver&#xff0c;ChromeDrive…

【linux开发基础知识】

基础--图形界面 基础--终端 基础-用户/组 基础-目录 基础-文件 Shell-基础 Shell-参数 Shell-if Shell-while/for Shell-until/case Shell-函数 Shell-正则表达式 Shell-常用命令 开发-svn 开发-编辑(vim) 开发-编译 开发-调试 开发-部署 建议 ● http://linux.vbir…

海外应用商店关键词优化之如何提高应用可见度

应用商店关键词优化是确保用户可以在Google Play和Apple应用商店中找到我们应用的重要一步。我们需要选择正确的关键词&#xff0c;将它们放在正确的位置&#xff0c;并合并一系列不同的关键词&#xff0c;同时确保拥有良好的转化率。 1、了解当前的元数据是关键字选择的第一步…

selenium环境搭建

文章目录 1、下载谷歌浏览器2、下载谷歌驱动 1、下载谷歌浏览器 浏览器下载完成后&#xff0c;在任务管理器中禁止浏览器的自动更新。因为驱动版本必须和浏览器一致&#xff0c;如果浏览器更新了&#xff0c;驱动就用不起了。 2、下载谷歌驱动 谷歌驱动需要和谷歌浏览器版本…

Eigen在QT中的配置

Eigen简介 Eigen支持包括固定大小、任意大小的所有矩阵操作&#xff0c;甚至是稀疏矩阵&#xff1b;支持所有标准的数值类型&#xff0c;并且可以扩展为自定义的数值类型&#xff1b;支持多种矩阵分解及其几何特征的求解&#xff1b;它不支持的模块生态系统提供了许多专门的功能…

浏览器自动访问打开网址的软件小工具模拟测试

用微软框架写了个浏览器自动访问和打开网址的工具&#xff0c;进行测试模拟&#xff1a; 1、获取链接方式&#xff0c;可通过API接口返回JSON链接格式&#xff0c;也可以集成到文档手动录入链接由软件进行循环运行。 2、配置一些参数&#xff1a;数量、次数、时间间隔等 看下演…

常用dbGet命令

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f;拾陆楼知识星球入口 Examples of dbGet Command 1. Find the top name of the design dbGet top.name 2. Get all the attributes of a selected object dbGet selected.?? If you press tab key…

华为OD机试真题 Java 实现【判断字符串子序列】【2023 B卷 100分】,倒序遍历

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…

智汇云舟入选IDC《中国智慧城市数字孪生技术评估,2023》报告

8月7日&#xff0c;国际数据公司&#xff08;IDC&#xff09;发布了《中国智慧城市数字孪生技术评估&#xff0c;2023》报告。智汇云舟凭借在数字孪生领域的创新技术与产品&#xff0c;入选《2023中国数字孪生城市技术提供商图谱》。 报告通过公开征集的形式进行申报&am…

PCkit3如何刷固件

PCkit3如何刷固件 一般在MAPLAB安装时&#xff0c;在安装路径下面都会自带所有烧写器的固件包&#xff0c;找到对应的固件包利用MPLAB进行刷新就行了&#xff0c;具体步骤如下&#xff1a; 首先打开MPLAB软件&#xff0c;然后Programmer->Settings…然后点击configuration …

线程控制+线程tid+线程局部存储+线程私有栈

线程控制函数 今天学习的都是linux线程库中的函数。<pthread.h> pthread_creat()创建线程 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); 参数 thread:返回线程IDattr:设置线程的属性&…

eclipse怎么导入web项目?看这篇为你解决!

引言【注意】 在导入别人写好或者自己写的web项目要确定以下几点&#xff0c;防止导入的时候出现错误。 确认eclipse的版本&#xff1b;如果web项目是21版的eclipse写的&#xff0c;而你要导入的的eclipse是17版的会出现错误&#xff0c;导入了也没用&#xff0c;运行不出结果。…

Huggingface使用

文章目录 前置安装Huggingface介绍NLP模块分类transformer流程模块使用详细讲解tokennizermodeldatasetsTrainer Huggingface使用网页直接体验API调用本地调用(pipline)本地调用&#xff08;非pipline&#xff09; 前置安装 anaconda安装 使用conda创建一个新环境并安装pytorc…

Qt应用开发(基础篇)——LCD数值类 QLCDNumber

一、前言 QLCDNumber类继承于QFrame&#xff0c;QFrame继承于QWidget&#xff0c;是Qt的一个基础小部件。 QLCDNumber用来显示一个带有类似lcd数字的数字&#xff0c;适用于信号灯、跑步机、体温计、时钟、电表、水表、血压计等仪器类产品的数值显示。 QLCDNumber可以显示十进制…

关于将预留单中增强字段带入物料凭证和会计凭证中

1、业务需求 预留中自定义文本字段“大项修”。根据预留创建物料凭证时&#xff0c;将该字段带入到物料凭证中&#xff0c;类似标准字段“项目文本”。并在物料凭证自动产生会计凭证后&#xff0c;将该字段带入到会计凭证行项目中。 其中需要解决以上三张凭证对该字段的界面显…

c/c++函数可变参数的实现

c语言&#xff0c;利用<stdarg.h> 里面的 typedef char* va_list; void va_start ( va_list ap, prev_param ); /* ANSI version */ type va_arg ( va_list ap, type ); void va_end ( va_list ap );#include <stdio.h> #include <stdarg.h> double Sum(int…

火爆全球的AI艺术二维码到底是怎么做的?

如今&#xff0c;二维码扫描已经成为一种与呼吸一样自然的本能动作。支付、购物、点餐、订票、浏览网页&#xff0c;几乎所有事情都可以通过扫描二维码来完成。你是否可以想象到下面是二维码&#xff1f;AI生成的艺术二维码使二维码瞬间变得高逼格。这些艺术二维码极具吸引力&a…

如何制定项目计划?甘特图告诉你

最近被领导指派负责一个新的项目&#xff0c;我想把项目做成功&#xff0c;给老板留下深刻的印象&#xff0c;同时也给自己的职业生涯添上浓墨重彩的一笔。 但是&#xff0c;项目管理流程很复杂&#xff0c;项目本身也不好做。在收集了与该项目有关的所有信息&#xff0c;并将…

亚马逊搜索关键词下单有哪些好处

在亚马逊上使用搜索关键词进行下单有许多好处&#xff0c;以下是其中一些主要的优点&#xff1a; 1、准确的产品匹配&#xff1a; 通过输入相关的搜索关键词&#xff0c;您可以更准确地找到您所需的产品&#xff0c;从而避免了浏览大量无关的商品页面。 2、节省时间&#xff…