八大排序——快速排序(霍尔 | 挖空 | 前后指针 | 非递归)

news2024/10/5 14:17:52

我们今天来讲讲八大排序中的快速排序,快速排序最明显的特点就是排序快,时间复杂度是O(N* logN),但是坏处就是如果排序的是一个逆序的数组的时候,时间复杂度是O(N^2),还不用我们的插入排序好,所以特点明显,但是缺点也是很明显的,那我们开始今天的学习吧。

首先就是我们霍尔大佬的排序方法,思想就是一遍排序让大的在右边,小的都在左边,我们来看看下面的动图.

我们可以看到霍尔大佬的排序方法有很多坑的,首先我们是右边开始先找,右边是找小,找到小的时候,停下来,然后就是我们左边开始动,左边是找到到,找到大的时候就开始交换左边和右边,然后再开始我们右边开始找大,左边开始找小,我们这里还是需要注意的就是我们这个排序什么时候才是结束的时候,我们就是条件通过动图可以看到就是right和left相遇的时候,然后这个时候我们需要做的就是交换key和left和rigth相遇地方的值。

这里的唯一难处就是我们为什么他们相遇的时候这个值一定是比key小的???

我们left和right相遇有两种情况,一种是right与left相遇,一种是left和right相遇,这两种相遇都是能够确保我们遇到的值是比key小的,我们可以这样来看,第一种情况,right动,left不动,我们的right是找小, 当right遇到left的时候,left的位置肯定是小于key的,我们上一次交换的时候,就是把left变成小的,所以这样也就确保right和left相遇的时候,是比key小的,我们再来看第二种情况就是left去遇到right,因为right是找到小的值,然后我们left去找大,一直没找到大的时候就是到right这个时候条件也不满足了,所以这样left和right碰到时候,条件也是比key小。

但是这个都是因为我们是右边开始先找小的,然后左边开始找大的,如果没有这个条件的话,我们是无法成立left和right相遇的时候值是比key小的。

代码如下

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
int SortPart1(int* a, int left, int right)
{
	int key = a[left];
	int begin = left;
	while (left < right)
	{
		while (left < right && a[right] >= key)
		{
			right--;
		}
		while (left < right && a[left] <= key)
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[left], &a[begin]);
	return left;
}



void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int midi = Midi(a, begin, end);
	Swap(&a[begin], &a[midi]);

	int keyi = SortPart3(a, begin, end);
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi+1, end);

}

这样就是我们霍尔大佬的思路,但是霍尔大佬的写法坑是太对了,大家可以看我们的代码是很容易写错的,那我们来看看其他的版本,再看其他版本的时候我们可以优化我们的代码就是我们的三数取中,因为我们的代码逆序的时候是最慢的,所以我们每次取值的时候如果我们的值每次不是最大和最小就很好的解决了我们的这个问题,下面是三数取中的代码。

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

我们来看看挖空法的动图。

我们先给出代码,然后对着代码和图来看,让大家更好的理解挖空法。

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

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}


int SortPart2(int* a, int left, int right)
{
	int hole = left;
	int key = a[hole];
	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;
}


void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int midi = Midi(a, begin, end);
	Swap(&a[begin], &a[midi]);

	int keyi = SortPart3(a, begin, end);
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi+1, end);

}

 挖空法的思路其实和霍尔大佬的思想很相似,我们首先一定要保存坑位的数据,因为等等这个数据想当于要被挖空,是个空位,我们记住这个hole是坑位的下标,然后就是key是这个坑位的值,我们先是右边开始找小的,找到小的把这个位置填到到hole那个坑位里,然后就是当前right位置就是新的坑位,左边开始找大,找到大的时候就是再和右边一样,我们把右边的坑位拿左边的值填上,再来左边挖坑,最后left和right遇到的时候就是结束的时候。结束的时候就是最后的坑位,最后的坑位补上我们刚开始的时候的值。

下一个就是我们的前后指针法

我们还是先来看看我们的动图。

前后指针法的我觉得看图就是能够写出代码的,就是cur再找小,找到小的时候,pre要先++然后再次进行交换就行了,cur到最后结束,所以结束条件就是cur <= end我们的代码可以优化成下面的这个代码。 

int Midi(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	if (a[mid] > a[left])
	{
		if (a[right] > a[mid])
		{
			return mid;
		}
		else if (a[right] < a[left])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
	else//left > mid
	{
		if (a[right] > a[left])
		{
			return left;
		}
		else if (a[right] < a[mid])
		{
			return mid;
		}
		else
		{
			return right;
		}
	}
}
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}


int SortPart3(int* a, int left, int right)
{
	int pre = left;
	int cur = left + 1;
	int begin = left;
	int end = right;
	while (cur <= end)
	{
		if (a[cur] < a[begin] && ++pre != cur)
		{
			Swap(&a[cur], &a[pre]);
		}
		cur++;
	}
	Swap(&a[pre], &a[begin]);
	return pre;
}
void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int midi = Midi(a, begin, end);
	Swap(&a[begin], &a[midi]);

	int keyi = SortPart3(a, begin, end);
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi+1, end);

}

那这样我们的前后指针法也是完成了,这三种方法都是递归的方法吗,现在下面来讲讲我们的非递归的方法,非递归的方法首先是要有个栈的,我们先得有个栈,以前的文章有·栈吗,大家也可以用我这个现场简略版的栈。


#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

typedef int STDateType;
typedef struct Stack
{
	STDateType* a;
	int top;
	int capacity;
}ST;


void Init(ST* ps);

void Push(ST* ps, STDateType x);

void Pop(ST* ps);

STDateType Top(ST* ps);

void Dstory(ST* ps);

bool Empty(ST* ps);

int Size(ST* ps);


#include"stack.h"


void Init(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = -1;
}

void Push(ST* ps, STDateType x)
{
	assert(ps);
	if (ps->top + 1 == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		STDateType* tmp =(STDateType*) realloc(ps->a, sizeof(STDateType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->capacity = newcapacity;
		ps->a = tmp;
	}
	ps->top++;

	ps->a[ps->top] = x;
}


void Pop(ST* ps)
{
	assert(ps);
	assert(ps->top >= 0);
	ps->top--;
}


void Dstory(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->capacity = 0;
}

STDateType Top(ST* ps)
{
	assert(ps);
	return ps->a[ps->top];
}


bool Empty(ST* ps)
{
	assert(ps);
	return ps->top == -1;
}




int Size(ST* ps)
{
	assert(ps);
	return ps->top + 1;
}

有了这个栈之后我们的非递归的思路其实就是边界问题,我们的栈存储的不是我们数组的值,而是我们每次的begin和end这些下标的值,我们一开始push的值肯定是0和end,我们也一定要注意push的顺序,然后再取出来left和right。我们来看看这个图,例子是这个数组。

我们的代码就是下面的这个。

//非递归
void QuickSortNonR(int* a, int n)
{
	int begin = 0;
	int end = n - 1;
	ST st;
	Init(&st);
	Push(&st, end);
	Push(&st, begin);
	while (!Empty(&st))
	{

		int left = Top(&st);
		Pop(&st);
		int right = Top(&st);
		Pop(&st);
		int keyi = SortPart3(a, left, right);

		if (left < keyi - 1)
		{
			Push(&st, keyi-1);
			Push(&st, left);
		}
		if (right > keyi + 1)
		{
			Push(&st, right);
			Push(&st, keyi+1);
		}
		
	}
	Dstory(&st);
}

 今天分享的就是我们八大排序的快速排序,快速排序是很重要的一个排序,还有一个快速排序还是可以针对我们的很多重复值的排序,比如一堆222222这些种,我们放在后面的OJ题里面来讲他,下一个分享的就是归并排序。我们下次再见。

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

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

相关文章

【Hive_03】单行函数、聚合函数、窗口函数、自定义函数、炸裂函数

1、函数简介2、单行函数2.1 算术运算函数2.2 数值函数2.3 字符串函数&#xff08;1&#xff09;substring 截取字符串&#xff08;2&#xff09;replace 替换&#xff08;3&#xff09;regexp_replace 正则替换&#xff08;4&#xff09;regexp 正则匹配&#xff08;5&#xff…

操作系统期末复习-内存管理

一、内存管理 分页存储管理&#xff0c;是将一个进程的逻辑地址空间分成若干个大小相等的片&#xff0c;称为页面或页&#xff0c;并为各页加以编号&#xff0c;从0开始&#xff0c;如第0页、第1页等。相应地&#xff0c;也把内存空间分成与页面相同大小的若干个存储块&#xf…

直线追踪

由于项目的需要&#xff0c;最近在做一个直线追踪的东西&#xff0c;但是网上的代码关于车道线或者别的什么之类的直线追踪的代码只是提了一下&#xff0c;相关的代码并不是公开的&#xff0c;所以自己写了一些直线追踪的代码。 代码使用的是kalman滤波进行直线追踪&#xff0…

完美解决labelimg xml转可视化中文乱码问题,不用matplotlib

背景简述 我们有一批标注项目要转可视化&#xff0c;因为之前没有做过&#xff0c;然后网上随意找了一段代码测试完美&#xff08;并没有&#xff09;搞定&#xff0c;开始疯狂标注&#xff0c;当真正要转的时候傻眼了&#xff0c;因为测试的时候用的是英文标签&#xff0c;实…

Sci. Rep. | 一个对任意分子体系实现准确且高效几何深度学习的通用框架

这篇工作是来自纽约城市大学/康奈尔医学院谢磊团队的一篇论文。作者提出了一个通用框架&#xff0c;PAMNet&#xff0c;可以对任意分子体系实现准确且高效的几何深度学习。在小分子性质、RNA三维结构以及蛋白质-配体结合亲和力的预测任务上&#xff0c;PAMNet在准确性和效率方面…

网络编程-认识套接字socket

文章目录 套接字概念端口号网络字节序 套接字类型流套接字数据报套接字 socket常见APIsocket函数bind函数listen函数accept函数connect函数sockaddr结构 套接字概念 socket套接字是进程之间一种通信机制&#xff0c;通过套接字可以在不同进程之间进行数据交流。在TCP/UDP中&…

将html的radio单选框自定义样式为正方形和对号

将html的radio单选框自定义样式为正方形和对号 背景&#xff1a; 如何能把html的<input type"radio" name"option">改成自定义的样式呢&#xff1f;比如想要把他变成正方形&#xff0c;选中的时候是对号。默认的样式太丑了 默认样式&#xff1a; 自…

[数学]三角形的五心之内心

三角形内心的性质 三角形内切圆的圆心称为三角形的内心。内心也是三角形三个角的角平分线的交点 性质1 1.1 设 I I I为 △ A B C △ABC △ABC内一点&#xff0c;则 I I I为 △ A B C △ABC △ABC内心的充要条件是下列条件之一&#xff1a; 1.1.1 I I I到 △ A B C △ABC △…

【C语言】RDMACM、Verbs API与epoll一起使用的示例

一、epoll介绍 epoll是Linux内核为处理大批量文件描述符而作了改进的poll&#xff0c;是Linux下多路复用IO接口select/poll的增强版本&#xff0c;它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。 以下是epoll的主要使用方法和优点&#xff1a; epo…

代码随想录刷题题Day15

刷题的第十五天&#xff0c;希望自己能够不断坚持下去&#xff0c;迎来蜕变。&#x1f600;&#x1f600;&#x1f600; 刷题语言&#xff1a;C Day15 任务 ● 513.找树左下角的值 ● 112. 路径总和 113.路径总和ii ● 106.从中序与后序遍历序列构造二叉树 105.从前序与中序遍历…

整理了上百个开源中文大语言模型,涵盖模型、应用、数据集、微调、部署、评测

自ChatGPT为代表的大语言模型&#xff08;Large Language Model, LLM&#xff09;出现以后&#xff0c;由于其惊人的类通用人工智能&#xff08;AGI&#xff09;的能力&#xff0c;掀起了新一轮自然语言处理领域的研究和应用的浪潮。 尤其是以ChatGLM、LLaMA等平民玩家都能跑起…

广州华锐互动:汽车电子线束加工VR仿真培训与实际生产场景相结合,提高培训效果

随着科技的不断发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术已经逐渐渗透到各个领域&#xff0c;为企业和个人带来了前所未有的便利。在汽车制造行业中&#xff0c;线束加工作为一项关键的生产工艺&#xff0c;其质量直接影响到汽车的性能和安全。因此&#xff0c;…

LCR 181. 字符串中的单词反转

解题思路&#xff1a; class Solution {public String reverseMessage(String message) {message message.trim(); // 删除首尾空格int j message.length() - 1, i j;StringBuilder res new StringBuilder();while (i > 0) {while (i >…

局域网环境下的ntp对时

服务端&#xff1a; 此处为v4-sp4服务器 安装ntp&#xff0c;apt-get install ntp -y ,若为离线环境&#xff0c;则安装ntp和libopts25两个包。 配置&#xff1a; 在/etc/ntp.conf的配置文件里 加入 restrict default nomodify notrap noquery restrict 127.0.0.1 rest…

时序预测 | Python实现GRU-XGBoost组合模型电力需求预测

时序预测 | Python实现GRU-XGBoost组合模型电力需求预测 目录 时序预测 | Python实现GRU-XGBoost组合模型电力需求预测预测效果基本描述程序设计参考资料预测效果 基本描述 该数据集因其每小时的用电量数据以及 TSO 对消耗和定价的相应预测而值得注意,从而可以将预期预测与当前…

【IDEA】关于快捷键行注释时会从行首开始注释

前言 每次在使用IDEA写代码时&#xff0c;用快捷键进行行注释的时候&#xff0c;//会处于行首位置 但是我们想要的是这种注释效果 修改 IDEA中打开settings 找到Editor - Code Style- Java - 找到Code Genneration&#xff0c;取消勾选Line comment at first column&#x…

如何批量获取CSDN文章数据并进行持久化

自己去看文章数据的话&#xff0c;比较慢&#xff0c;所以一直想通过程序来批量获取CSDN的文章数据&#xff0c;最近研究了一下&#xff0c;发现还是挺简单的&#xff0c;能够直接通过解析json来获取文章数据&#xff0c;跟大家分享一下。 文章目录 一、步骤1、首先我们到自己的…

VUE学习三、前端项目部署

1.前端项目打包 执行命令 npm run build:prod正常命令结束 , 会在前端项目里面出现dist文件夹 2.nginx下载安装 nginx下载 : http://nginx.org/en/download.html Windows 下载版本 Mainline version&#xff1a;Mainline 是 Nginx 目前主力在做的版本&#xff0c;可以说…

【手撕算法系列】BN

BN的计算公式 BN中均值与方差的计算 所以对于输入x: b,c,h,w 则 mean: 1,c,1,1var: 1,c,1,1代码 class BatchNorm(nn.Module):def __init__(self, num_features, num_dims):# num_features&#xff1a;完全连接层的输出数量或卷积层的输出通道数。# num_dims&#xff1a;2表示…