数据结构排序——详解快排及其优化和冒泡排序(c语言实现、附有图片与动图示意)

这篇具有很好参考价值的文章主要介绍了数据结构排序——详解快排及其优化和冒泡排序(c语言实现、附有图片与动图示意)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

上次讲了选择排序和堆排序:数据结构排序——选择排序与堆排序

今天就来快排和冒泡



1.快排

1.1基本介绍

快速排序(Quick Sort)是一种常用的排序算法,它是由英国计算机科学家Tony Hoare于1959年发明的。快速排序的基本思想是通过分治的策略将一个数组分成两个子数组,然后分别对这两个子数组进行排序。具体步骤如下:

  1. 选择一个基准元素(通常是数组的第一个元素,右边先行)。
  2. 将数组分割成两部分,使得左边的元素都小于等于基准元素,右边的元素都大于基准元素。这个过程叫做分区(Partition)
  3. 对分割后的两个子数组分别重复步骤1和2(利用递归),直到子数组的大小为1或0,此时数组已经有序

==优化:==如果本身就很接近有序,那效率就慢了(一个逆序变升序,keyi就一直在左边,递归也只有右侧,所以选择三个数来找中间大小,能让keyi尽量向数组中间靠近),所以设计了Getmid函数来取中间大小的数

1.2不同的分区方法及代码实现

1.2.1Hoare版

使用两个索引,一个从数组的左边开始向右移动,另一个从数组的右边开始向左移动,直到它们相遇。在这个过程中,如果左指针指向的元素大于基准元素且右指针指向的元素小于基准元素,则交换这两个元素。当两个指针相遇时,将基准元素(keyi指向的)与相遇位置的元素交换,这样基准元素就归位了

数据结构排序——详解快排及其优化和冒泡排序(c语言实现、附有图片与动图示意),数据结构,数据结构——排序,数据结构,算法,c++,java,机器学习,人工智能,c语言

void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

int GetMid(int* a,int left, int right)//找中间的
{
	// a[left]      a[mid]           a[right]
	int mid = (left + right) / 2;
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[left] > a[right])  // mid是最大值
		{
			return left;
		}
		else
		{
			return right;
		}
	}
	else // a[left] > a[mid]
	{
		if (a[left] < a[right])
		{
			return left;
		}
		else if (a[mid] < a[right])
		{
			return right;
		}
		else
		{
			return mid;
		}
	}
}
//一次排序
int OneSort1(int* a, int left, int right)//使keyi位置的元素处于正确的位置上
{
	int mid = GetMid(a, left, right);
	Swap(&a[mid], &a[left]);//现在left处是三者的中间值了
	//左边第一个为key,右边先走才能保证相遇处比啊a[keyi]小
	int keyi = left;
	while (left < right)
	{
		while (a[right] >= a[keyi] && left < right)//右边先走
		{
			right--;
		}
		while (a[left] <= a[keyi] && left < right)//左侧找大的
		{
			left++;
		}
		Swap(&a[left], &a[right]);//找到一个大和一个小的就交换
	}
	Swap(&a[keyi], &a[left]);//把keyi放相遇位置
	return left;//返回相遇的索引
}

void QuickSort(int* a, int begin, int end)//升序
{
	if (begin >= end)
	{
		return;
	}
	// [begin, keyi-1] keyi [keyi+1, end]
	int keyi = OneSort1(a, begin, end);//找到keyi索引,才能分左右
	QuickSort(a, begin, keyi - 1);//左侧
	QuickSort(a, keyi + 1, end);//右侧
}

int main()
{
	int a[] = { 6,1,2,7,9,3,4,5,10,8 };
	printf("排序前:");
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
	QuickSort(a, 0,sizeof(a) / sizeof(int)-1);
	printf("排序后:");
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
	return 0;
}

数据结构排序——详解快排及其优化和冒泡排序(c语言实现、附有图片与动图示意),数据结构,数据结构——排序,数据结构,算法,c++,java,机器学习,人工智能,c语言

1.2.2挖坑版

选择基准元素后,先将基准元素保存到临时变量中,然后使用左右索引的方式找到需要交换的元素,将这些元素填入基准元素的位置,最后将基准元素填入最后一个空出来的位置

数据结构排序——详解快排及其优化和冒泡排序(c语言实现、附有图片与动图示意),数据结构,数据结构——排序,数据结构,算法,c++,java,机器学习,人工智能,c语言

int OneSort2(int* a, int left, int right)//挖坑
{
	int mid = GetMid(a, left, right);
	Swap(&a[mid], &a[left]);//现在left处是三者的中间值了

	int key = a[left];//保存基准元素
	int hole = left;//储存坑下标,不能直接赋值为0
	while (left < right)
	{
		while (a[right] >= key && left < right)//右边先走,没有等号两侧出现相同值会死循环
		{
			right--;
		}//找到了就去赋值到第一个“坑”
		a[hole] = a[right];
		hole = right;//更新坑
		while (a[left] <= key && left < right)//左侧找大的
		{
			left++;
		}
		a[hole] = a[left];
		hole = left;
	}
	Swap(&key, &a[left]);//把keyi放相遇位置
	return left;//返回相遇的索引
}

1.2.3 前后指针版

pre指向第一个,cur指向下一个。cur找小后,pre++,然后交换(做到大的向后推),最后cur出数组结束

prev的情况有两种:

  1. 在cur还没遇到比key大的值时候,prev紧跟着cur
  2. 在cur还遇到比key大的值时候,prev在比key大的一组值的前面

本质是把一段大于key的区间,往后推,同时小的甩到左边去

数据结构排序——详解快排及其优化和冒泡排序(c语言实现、附有图片与动图示意),数据结构,数据结构——排序,数据结构,算法,c++,java,机器学习,人工智能,c语言
数据结构排序——详解快排及其优化和冒泡排序(c语言实现、附有图片与动图示意),数据结构,数据结构——排序,数据结构,算法,c++,java,机器学习,人工智能,c语言

int OneSort3(int* a, int left, int right)//挖坑
{
	int mid = GetMid(a, left, right);
	Swap(&a[mid], &a[left]);

	int keyi = left;
	int pre = left;
	int cur = left + 1;
	while (cur <= right)
	{
		if (a[cur] < a[keyi])
		{
			pre++;
			Swap(&a[cur], &a[pre]);
		}
		cur++;
	}
	Swap(&a[pre], &a[keyi]);
	return pre;
}

1.3快排的优化

1.3.1三数取中选key

从待排序数组的首、尾和中间位置各选取一个元素,然后取它们的中间值作为基准元素,确保选择的基准元素相对中间位置的元素更为接近

代码在Hoare版已经展示过了

int GetMid(int* a,int left, int right)//找中间的
{
	// a[left]      a[mid]           a[right]
	int mid = (left + right) / 2;
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[left] > a[right])  // mid是最大值
		{
			return left;
		}
		else
		{
			return right;
		}
	}
	else // a[left] > a[mid]
	{
		if (a[left] < a[right])
		{
			return left;
		}
		else if (a[mid] < a[right])
		{
			return right;
		}
		else
		{
			return mid;
		}
	}
}

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

当递归到小的子区间时,可以考虑使用插入排序来优化快速排序。因为插入排序在小规模数据上的排序效率通常比快速排序更高==(当数量比较少时,也没必要在递归好几层了)==

void InsertSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (a[end] > tmp)
			{
				a[end + 1] = a[end];
			}
			else
			{
				break;
			}
			end--;
		}
		a[end + 1] = tmp;
	}
}

int OneSort3(int* a, int left, int right)//挖坑
{
	int mid = GetMid(a, left, right);
	Swap(&a[mid], &a[left]);

	int keyi = left;
	int pre = left;
	int cur = left + 1;
	while (cur <= right)
	{
		if (a[cur] < a[keyi])
		{
			pre++;
			Swap(&a[cur], &a[pre]);
		}
		cur++;
	}
	Swap(&a[pre], &a[keyi]);
	return pre;
}

void QuickSort(int* a, int begin, int end)//升序
{
	if (begin >= end)
	{
		return;
	}

	if ((end - begin + 1) > 10)
	{
		// [begin, keyi-1] keyi [keyi+1, end]
		int keyi = OneSort3(a, begin, end);
		QuickSort(a, begin, keyi - 1);
		QuickSort(a, keyi + 1, end);
	}
	else
	{
		//用插入排序
		InsertSort(a + begin, end - begin + 1);
	}
}

1.3.3大量重复数据采用三路划分

快速排序的三路划分通过将数组分为小于等于=和大于基准元素的三个部分==,有效地处理了重复元素,提高了算法的效率

快速排序的三路划分算法的基本思想是使用三个指针/下标来维护三个区域:小于基准元素的区域、等于基准元素的区域和大于基准元素的区域。在每一次遍历数组的过程中,将数组分为这三个区域,并将指针移动到合适的位置。最终,数组会被划分成小于基准元素、等于基准元素和大于基准元素的三个部分

基本步骤:

数据结构排序——详解快排及其优化和冒泡排序(c语言实现、附有图片与动图示意),数据结构,数据结构——排序,数据结构,算法,c++,java,机器学习,人工智能,c语言

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

	int mid = GetMid(a, left, right);
	Swap(&a[mid], &a[left]);
	int cur = left + 1;
	int key = a[left];//储存一下,后面比较来用,用a[left]会被替代
	while (cur <= right)
	{
		if (a[cur] < key)
		{
			Swap(&a[cur], &a[left]);
			cur++;
			left++;
		}
		else if (a[cur] == key)
		{
			cur++;
		}
		else
		{
			Swap(&a[cur], &a[right]);
			right--;
		}
	}
	QuickSort(a, begin, left - 1);
	QuickSort(a, right + 1, end);
}

1.4快排非递归

快速排序的非递归实现通常使用栈来模拟递归调用的过程。在快速排序的递归实现中,每次递归调用都将函数参数压入栈中,然后在递归返回时再从栈中弹出参数(二者性质相同)。

非递归实现则需要手动维护一个栈,将需要处理的子序列的起始和结束位置压入栈中,然后循环处理栈中的元素,直到栈为空(递归一次就用一次)

void QuickSortNonR(int* a, int begin, int end)//利用栈,先想好先排左侧再排右侧
{
	ST st;
	STInit(&st);
	STPush(&st,end);//把各个区间的两侧的索引(整形)插入进Stack中
	STPush(&st,begin);//栈(后进先出),先排左侧所以后入左
	while (!STEmpty(&st))
	{
		int left = STTop(&st);
		STPop(&st);
		int right = STTop(&st);
		STPop(&st);

		int keyi = OneSort1(a, left, right);//得到基准元素下标
		// [begin, keyi-1] keyi [keyi+1, end]
		if (keyi + 1 < right)//等于说明就一个,没必要
		{
			STPush(&st, right);
			STPush(&st, keyi);
		}
		if (left < keyi-1)
		{
			STPush(&st, keyi-1);
			STPush(&st, left);
		}
	}
	STDestroy(&st);
}

2.冒泡排序

它重复地遍历要排序的列表,比较每对相邻的元素,并按照大小顺序交换它们。重复这个过程直到整个列表排序完成

步骤:

  1. 从列表的第一个元素开始,依次比较相邻的两个元素,如果它们的顺序不正确就交换它们。
  2. 继续遍历列表,重复上述比较和交换的过程,直到没有任何一对相邻元素需要交换为止。
  3. 列表排序完
void BobbleSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		for (int j = 0; j < n - 1 - i; j++)
		{
			if (a[j] > a[j + 1])
			{
				Swap(&a[j], &a[j + 1]);
			}
		}
	}
}

好啦,这次内容就到这里啦,下次带来归并排序,感谢大家支持!!!文章来源地址https://www.toymoban.com/news/detail-823921.html

到了这里,关于数据结构排序——详解快排及其优化和冒泡排序(c语言实现、附有图片与动图示意)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构】深入浅出理解快速排序背后的原理 以及 版本优化【万字详解】(C语言实现)

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

    2024年02月05日
    浏览(87)
  • Python篇——数据结构与算法(第四部分:希尔排序及其讨论、计数排序、桶排序、基数排序)

    希尔排序(shell sort)是一种分组插入排序算法 首先取一个整数d1=n/2,将元素分为d1个组,每组相邻两元素之间距离为d1,在各 组内 进行直接插入排序 取第二个整数d2=d1/2,重复上述分组排序过程,知道di=1,即所有元素在同一组内进行直接插入排序。 希尔排序每趟并不使某些

    2024年02月07日
    浏览(29)
  • 【数据结构】| 并查集及其优化实现

    以一个直观的问题来引入并查集的概念。 亲戚问题:有一群人,他们属于不同家族,同一个家族里的人互为亲戚,不同家族的人不是亲戚。随机指定两个人,问他们是否有亲戚关系。 以下图3个不相交的集合表示 3 个家族,当询问两个人是否有亲戚关系时,也就是问两个元素

    2024年02月09日
    浏览(26)
  • 【数据结构与算法】排序算法:冒泡排序,冒泡排序优化,选择排序、选择排序优化

    目录 一、冒泡排序 1、冒泡排序思想 2、冒泡排序算法的性能分析 代码实现: 二、选择排序 1、选择排序思想 2、选择排序算法的性能分析  代码实现: 1、冒泡排序思想 冒泡排序的基本思想是通过相邻元素之间的比较和交换来逐步将最大(或最小)的元素移到右边(或左边

    2024年01月19日
    浏览(34)
  • 深入理解数据结构第二弹——二叉树(2)——堆排序及其时间复杂度

    看这篇前请先把我上一篇了解一下:深入理解数据结构第一弹——二叉树(1)——堆-CSDN博客 前言: 相信很多学习数据结构的人,都会遇到一种情况,就是明明最一开始学习就学习了时间复杂度,但是在后期自己写的程序或者是做到哪个需要判断时间复杂度的题时,仍然判

    2024年04月09日
    浏览(29)
  • 数据结构之美:如何优化搜索和排序算法

    🎉欢迎来到数据结构学习专栏~数据结构之美:如何优化搜索和排序算法 ☆* o(≧▽≦)o *☆嗨~我是IT·陈寒🍹 ✨博客主页:IT·陈寒的博客 🎈该系列文章专栏:数据结构学习 📜其他专栏:Java学习路线 Java面试技巧 Java实战项目 AIGC人工智能 数据结构学习 🍹文章作者技术和水

    2024年02月08日
    浏览(35)
  • 算法 数据结构 递归插入排序 java插入排序 递归求解插入排序算法 如何用递归写插入排序 插入排序动图 插入排序优化 数据结构(十)

    1. 插入排序(insertion-sort):                                           是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入     算法稳定性:                  

    2024年02月09日
    浏览(39)
  • 数据结构:排序干货!(7大排序汇总+快速排序的优化+计数排序+基数排序+桶排序)

    目录 概念 插入排序 直接插入排序 希尔排序 选择排序 直接选择排序 双向选择排序 堆排序 交换排序 冒泡排序 快速排序 Hoare法 挖坑法 前后指针法 快排的优化 三数取中法 非递归快排 归并排序 分治算法+二路归并 非递归归并 应用 排序总结 其他排序 计数排序 简单版本 复杂

    2024年02月06日
    浏览(32)
  • <数据结构>NO11.归并排序|递归|非递归|优化

    思路: 如果给出两个有序数组,我们很容易可以将它们合并为一个有序数组。 因此当给出一个无序数组时,我们先将它们均分为两组有序数组,在将这两组有序数组合并为一个有序数组;而将原数组分成2组 有序数组的思路又是归并排序 。 递归的结束条件是什么? 当递归区间

    2024年02月16日
    浏览(32)
  • 【排序算法】数据结构排序详解

    前言: 今天我们将讲解我们数据结构初阶的最后一部分知识的学习,也是最为“炸裂”的知识---------排序算法的讲解!!!! 排序 :所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 稳定性 :假定在待排序的记录序列中,

    2023年04月08日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包