数据结构精讲——排序(二)

news2024/9/28 23:28:34

数据结构精讲——排序(二)

排序的分类

在这里插入图片描述
上节课我们已经了解过插入排序选择排序,这节课主要讲的是快排排序,冒泡排序应该都很熟悉,少提一下吧。

冒泡排序

冒泡排序是我们刚接触编程时就学习的排序方法(还有选择排序)。
其原理就是将相邻的两个数据节点节进行比较然后慢慢完后转移比较,很像水中的泡泡。
在这里插入图片描述
一共七趟(n-1趟)。

程序实现

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
using namespace std;

int main()
{
	int a[] = { 1,8,7,3,5,4,6,2 };
	int n = sizeof(a) / sizeof(a[0]);
	for (int i = 0; i < n; i++)
	{
		for (int j = 1; j < n - i; j++)
		{
			if (a[j - 1] > a[j])
			{
				swap(a[j - 1], a[j]);
			}
		}
	}
	for (int i = 0; i < n; i++)
	{
		cout << a[i] <<' ';
	}
	return 0;
}

时间复杂度为O(n^2),所以效率并不高,但是好理解所以新手好学一下。
只是简单说一下,并不做重点。

快排

在学C语言时我们了解过qsort这个函数,其内核就是快排。
在这里插入图片描述
base是数组的第一个节点的指针,num是排序数组的成员个数(要排序几个成员),size是每一个成员的类型大小(char=1;double=8;int=4),compar是比较函数(需要在运算后将void*改成int返回出来)。

compare函数:

在这里插入图片描述
如果小于返回-1,如果大于返回1,等于返回0。

快排原理

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

// 假设按照升序对array数组中[left, right)区间中的元素进行排序
void QuickSort(int array[], int left, int right)
{
 if(right - left <= 1)
 return;
 
 // 按照基准值对array数组的 [left, right)区间中的元素进行划分
 int div = partion(array, left, right);
 
 // 划分成功后以div为边界形成了左右两部分 [left, div) 和 [div+1, right)
 // 递归排[left, div),左闭右开
 QuickSort(array, left, div);
 
 // 递归排[div+1, right)
 QuickSort(array, div+1, right);
}

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

hoare版本

在这里插入图片描述
我们可以把L与R相遇的地方变成新keyi,新keyi左边都比keyi小,右边都比keyi大。
然后再进行递归。
在这里插入图片描述
再进行分批排序。

void QSort1(int* a, int begin, int end)//hoare
{
	int keyi = begin;
	int left = begin;
	int right = end;
	while (left < right)
	{
		while (left < right && a[right] >= a[keyi])//右边找小于a[keyi]的数
		{
			right--;
		}
		while (left < right && a[left] <= a[keyi])//左边找大于a[keyi]的数
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	//将a[keyi]与相遇点交换(要保证相遇点比a[keyi]小,需要让right先走)
	Swap(&a[keyi], &a[right]);
	QSort1(a, begin, keyi-1);
	QSort1(a, keyi+1,end);

}

挖坑法

先保存a[keyi]的值到int temp上,然后将keyi先作坑,int left=0、int right=n-1

先让right走,找到小于a[keyi]的数,就将它放在a[hole]处,更新hole=right。然后left再走,找到大于a[keyi]的数,然后将它放在a[hole]处,再次更新hole=left,再让right移动,left移动,直到left等于right。此时相遇点必然是一个坑,最后将temp放在a[hole]处。
在这里插入图片描述

int Q_PartSort2(int* a, int begin, int end)//挖坑法
{
	int key = a[begin];
	int hole = begin;
	int left = begin;
	int right = end;
	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 keyi = Q_PartSort2(a, begin, end);

	_QuickSort(a, begin, keyi - 1);
	_QuickSort(a, keyi + 1, end);
}

前后指针版本

取最左边的下标左keyi,prev=begin、next=begin+1,
next找小,如果找到小于a[ keyi ]的数,就让prev++,
然后将a[prev]和a[next]交换。直到next大于n,结束。
最后再让a[keyi]和a[prev]交换。
在这里插入图片描述

int Q_PartSort3(int* a, int begin, int end)//前后指针法
{
	int keyi = begin;
	int prev = begin;
	int next = begin + 1;
	while (next <= end)
	{
		if (a[next] < a[keyi] && ++prev != next)
		{
			Swap(&a[prev], &a[next]);
		}
		next++;
	}
	Swap(&a[keyi], &a[prev]);
	return prev;
}
void _QuickSort(int* a, int begin, int end)//递归
{
	if (begin >= end)
	{
		return;
	}
	int keyi = Q_PartSort3(a, begin, end);

	_QuickSort(a, begin, keyi - 1);
	_QuickSort(a, keyi + 1, end);
}

快速排序优化

  1. 三数取中法选key
  2. 递归到小的子区间时,可以考虑使用插入排序
    在这里插入图片描述

快速排序非递归

void QuickSortNonR(int* a, int left, int right)
{
Stack st;
StackInit(&st);
StackPush(&st, left);
StackPush(&st, right);
while (StackEmpty(&st) != 0)
{
 right = StackTop(&st);
 StackPop(&st);
 left = StackTop(&st);
 StackPop(&st);
 
 if(right - left <= 1)
 continue;
 int div = PartSort1(a, left, right);
 // 以基准值为分割点,形成左右两部分:[left, div) 和 [div+1, right)
 StackPush(&st, div+1);
 StackPush(&st, right);
 
 StackPush(&st, left);
 StackPush(&st, div);
}
 
 StackDestroy(&s);
}

排序的代码

我的全部代码,拿走不谢!

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

#define MAX 10

void display(int arr[], int num) {//遍历数组
    int i;
    for (i = 0; i < num; i++) {
        printf("%-5d", arr[i]);
    }
    printf("\n");
    return;
}

int Q_PartSort1(int* a, int begin, int end)//hoare
{
	int keyi = begin;
	int left = begin;
	int right = end;
	while (left < right)
	{
		while (left < right && a[right] >= a[keyi])//右边找小于a[keyi]的数
		{
			right--;
		}
		while (left < right && a[left] <= a[keyi])//左边找大于a[keyi]的数
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	//将a[keyi]与相遇点交换(要保证相遇点比a[keyi]小,需要让right先走)
	Swap(&a[keyi], &a[right]);
	/*Q_PartSort1(a, begin, keyi-1);
	Q_PartSort1(a, keyi+1,end);*/
	return right;
}
int Q_PartSort2(int* a, int begin, int end)//挖坑法
{
	int key = a[begin];
	int hole = begin;
	int left = begin;
	int right = end;
	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;
}
int Q_PartSort3(int* a, int begin, int end)//前后指针法
{
	int keyi = begin;
	int prev = begin;
	int next = begin + 1;
	while (next <= end)
	{
		if (a[next] < a[keyi] && ++prev != next)
		{
			Swap(&a[prev], &a[next]);
		}
		next++;
	}
	Swap(&a[keyi], &a[prev]);
	return prev;
}
void _QuickSort(int* a, int begin, int end)//递归
{
	if (begin >= end)
	{
		return;
	}
	int keyi = Q_PartSort3(a, begin, end);

	_QuickSort(a, begin, keyi - 1);
	_QuickSort(a, keyi + 1, end);
}
int main()
{

	return 0;
}

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

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

相关文章

达梦数据库实施、运维与使用经验(一)

一、使用Xshell输入Insert语句&#xff0c;回车执行系统没反应 问题解决思路 查下数据库的活动会话数并登录数据库服务器看数据库的资源负载 select * from v$sessions where stateACTIVE;在数据库Xshell中输入语句查询数据库磁盘是否满了 df -lh上图中的第一个命令是用来查…

C++的IO流

目录 1. 流是什么 2. CIO流 2.1 C标准IO流 2.2 C文件IO流 2.2.1 简单读取文件流 2.2.2 C读取文件巧妙之处 3. stringstream的简单介绍 1. 流是什么 “流”即是流动的意思&#xff0c;是物质从一处向另一处流动的过程&#xff0c;是对一种有序连续且具有方向性的数据&am…

【我的渲染技术进阶之旅】关于OpenGL纹理压缩的相关资料

文章目录一、为啥要了解压缩纹理1.1 为啥要使用压缩纹理1.2 如何自定义压缩纹理以及使用压缩纹理的效果1.2.1 使用压缩纹理节省显存1.2.2 自定义压缩纹理&#xff1a;将压缩好的纹理数据保存在本地1.2.3 使用自定义的压缩纹理1.2.3 示例原理二、纹理压缩相关知识2.0 什么是压缩…

FOLOLane论文阅读

论文地址&#xff1a;https://arxiv.org/pdf/2105.13680.pdf 一、论文创新点 主流的车道线检测方法使用语义分割和聚类实现&#xff0c;但像素级的输出存在信息冗余&#xff0c;同时会带来大量噪声。而该论文使用两个分支&#xff0c;一个输出heatmap用于判断像素是否是关键点…

干货 | 初窥 Pytest 测试框架,基础薄弱也能轻松 hold 住

pytest 是一个成熟的全功能Python测试工具&#xff0c;可以帮助您编写更好的程序。它与 python 自带的 unittest 测试框架类似&#xff0c;但 pytest 使用起来更简洁和高效&#xff0c;并且兼容 unittest 框架。pytest 能够支持简单的单元测试和复杂的功能测试&#xff0c;pyte…

java中修改List的对象元素时碰到的坑

坑 case1 case1:Data class Person {Integer age;String name;public Person(Integer age, String name) {this.age age;this.name name;} } List<Person> v1List new ArrayList<>(); v1List.add(new Person(11, "小刚")); v1List.add(new Person(1…

前端基础(五)_运算符(算术运算符、赋值运算符、比较运算符、逻辑运算符、三目运算符、运算符优先级和结合性、数据类型的隐式转换)

一、算术运算符 算术运算符即&#xff1a;加&#xff08;&#xff09;减&#xff08;-&#xff09;乘&#xff08;*&#xff09;除&#xff08;/&#xff09;取余&#xff08;%&#xff09;加加&#xff08;&#xff09;减减&#xff08;–&#xff09; 算术运算符里比较特殊的…

CleanMyMacX软件有哪些优缺点?值不值得下载

CleanMyMac X 2023是一款可靠且功能强大的Mac清洁工具工具&#xff0c;他可以让你随时检查Mac电脑的健康情况&#xff0c;并删除电脑中的垃圾文件&#xff0c;来腾出存储空间&#xff0c;保持Mac系统的整洁。至问世以来&#xff0c;CleanMyMac 系统倍受国内外用户推崇&#xff…

【4】SCI易中期刊推荐——神经科学研究(中科院4区)

🚀🚀🚀NEW!!!SCI易中期刊推荐栏目来啦 ~ 📚🍀 SCI即《科学引文索引》(Science Citation Index, SCI),是1961年由美国科学信息研究所(Institute for Scientific Information, ISI)创办的文献检索工具,创始人是美国著名情报专家尤金加菲尔德(Eugene Garfield…

C#使用迷宫地图来模拟新冠疫情的传播速度(一)

国家开始发布疫情放开政策&#xff0c;本人于2022-12-21开始感染并发高烧。 最近才康复。有感于此 我们用初始感染源来影响九宫网格来查看新冠的传播速度 小游戏规则如下&#xff1a; 一个感染源 可以传播附近相邻的8个网格【类似于扫雷】&#xff0c;假如每个感染源一天只…

Vue--》setup、ref、reactive函数使用讲解

目录 setup ref函数 reactive函数 Vue3中的响应式原理 setup Vue3中的一个新的配置项&#xff0c;值为一个函数。组件中所用到的数据、方法等等&#xff0c;均要配置在setup中。setup函数的两种返回值&#xff0c;如下&#xff1a; 若返回一个对象&#xff0c;则对象中的…

使用Docker快速搭建Hfish蜜罐

HFish简介 HFish是一款社区型免费蜜罐&#xff0c;侧重企业安全场景&#xff0c;从内网失陷检测、外网威胁感知、威胁情报生产三个场景出发&#xff0c;为用户提供可独立操作且实用的功能&#xff0c;通过安全、敏捷、可靠的中低交互蜜罐增加用户在失陷感知和威胁情报领域的能…

源码讲解ThreadLocal父子线程通信问题(图+文+源码)

1 缘起 在复习ThreadLocal相关应用的知识&#xff0c; 有一个老生常谈的问题&#xff1a;父子线程通信&#xff0c; 起初&#xff0c;对于父子线程通信&#xff0c;仅了解ThreadLocal无法通过子线程获取线程数据&#xff0c; 并不了解为什么会这样&#xff1f;以及为什么Inher…

15.4 宏任务和微任务

宏任务和微任务 start 如果彻底理解了事件循环&#xff0c;其实大多数 JS 执行的逻辑都能理解了但是在 ES6 中引入了 Promise, 就引出了两个新概念&#xff0c;宏任务和微任务。 1.宏任务和微任务 1.1 名词 宏任务&#xff1a;macrotask 微任务&#xff1a;microtask 在 E…

异构操作系统的“融合计算”

这些年&#xff0c;由随着应用场景日益丰富和多样化&#xff0c;计算工作越来越复杂&#xff0c;传统的计算方式&#xff08;单机计算/分布式计算&#xff09;已经不能满足&#xff0c;需要一种新的更强大的计算模式来解决这些问题&#xff0c;这是融合计算产生的背景。 …

117.(leaflet之家)leaflet空间判断-点与geojson面图层的空间关系(turf实现)

听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 效果如下所示: 下面献上完整代码,代码重要位置会做相应解释 <!DOCTYPE html> <html>

Linux系统下的服务管理

文章目录Linux系统下的服务管理1.基本介绍2.service管理指令3.chkconfig指令4.systemctl管理指今4.1.基本语法4.2.systemctl设置服务的自启动状态4.3.防火墙实验案例4.4.防火墙Linux系统下的服务管理 1.基本介绍 服务(service) 本质就是进程&#xff0c;但是是运行在后台的&a…

软考高项(信息系统项目管理师)经验分享

文章目录前言考试过程第一步&#xff1a;日常刷选择题第二步&#xff1a;考前一个月之前刷完精讲课第三步&#xff1a;计算题统一学习第四步&#xff1a;论文早准备第五步&#xff1a;反复刷冲刺视频第六步&#xff1a;刷近几年真题第七步&#xff1a;准备几份考试当天复习资料…

vue组件之间的数据传递和组件的生命周期

一、组件之间的通信1、组件之间的关系&#xff1a;父子关系、兄弟关系、跨级关系2、父子组件之间的通信&#xff08;数据传递&#xff09;&#xff1a;&#xff08;1&#xff09;父组件 ——-> 子组件&#xff1a;使用propsA、第一步&#xff1a;在父组件中使用子组件时&…

【K哥爬虫普法】大数据风控第一案:从魔蝎科技案件判决,看爬虫技术刑事边界

我国目前并未出台专门针对网络爬虫技术的法律规范&#xff0c;但在司法实践中&#xff0c;相关判决已屡见不鲜&#xff0c;K 哥特设了“K哥爬虫普法”专栏&#xff0c;本栏目通过对真实案例的分析&#xff0c;旨在提高广大爬虫工程师的法律意识&#xff0c;知晓如何合法合规利用…