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

本文分析了冒泡排序和快速排序两种经典排序算法的特性,包括最好、最坏和平均情况的时间复杂度,并讨论了它们的稳定性。通过C语言实现代码,展示了这两种排序算法的过程,同时记录了比较和移动次数,以直观对比其效率。实验中,冒泡排序适合小规模数据,而快速排序在效率上更胜一筹,但不具备稳定性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、实验配置

二、实验分析:

冒泡排序算法特点:

快速排序算法特点:

三、比较思路 

四、代码实现 

五、程序分析  


一、实验配置

实验环境:Visual Studio 2022

程序语言:C

二、实验分析:

排序方法最好情况最坏情况平均情况额外占用空间稳定性
冒泡排序O(n)O(n^2)O(n^2)O(1)稳定
快速排序O(n)O(n^2)O(nlogn)O(nlogn)不稳定

冒泡排序算法特点:

  1. 稳定排序;
  2. 算法简单;
  3. 可以用于链式存储结构;
  4. 移动次数较多,算法性能较差,当排序的数据量大时不建议使用。

快速排序算法特点:

  1. 不稳定排序是因为快速排序在选择参考元素时是随机选择(一般选择序列首元素)的,且移动元素时是非顺次移动;
  2. 排序过程时需要确定上界high和下界low,所以适用于顺序存储结构;
  3. 算法效率较高,但较难理解。

三、比较思路 

通过比较两种算法的交换次数和比较次数来反映出算法的特点。

四、代码实现 

#define _CRT_SECURE_NO_DEPRECATE

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>

typedef int KeyType;		// 关键字的数据类型
typedef struct {
	KeyType* key;
	int length;
}SqList;	// 定义数字列表
int MoveCount = 0;	// 统计数据移动次数
int CompCount = 0;	// 统计数据比较次数

// 快速排序的子排序
int Partition(SqList* L, int low, int high) 
{
	L->key[0] = L->key[low];
	MoveCount += 1;
	while (low < high)
	{
		CompCount += 1;
		while (low < high && L->key[high] >= L->key[0]) {
			CompCount += 2;
			high--;
		}
		L->key[low] = L->key[high];
		MoveCount += 1;
		while (low < high && L->key[low] <= L->key[0]) {
			CompCount += 2;
			low++;
		}
		L->key[high] = L->key[low];
		MoveCount += 1;
	}
	L->key[low] = L->key[0];
	MoveCount += 1;

	return low;
}

// 快排函数
void QSort(SqList* L, int low, int high)
{
	if (low < high) {
		int mid = Partition(L, low, high);	// 确定中间临界值
		QSort(L, low, mid - 1);	// 前半段排序
		QSort(L, mid + 1, high);	// 后半段排序
	}
}

// 初始化快速排序参数
void QuickSort(SqList* L)
{
	QSort(L, 1, L->length);
}

// 冒泡排序
void BubbleSort(SqList* L) 
{
	int flag = 1, m = L->length-1;
	while ((m > 0) && (flag == 1)) {
		flag = 0;
		for (int j = 1; j <= m; j++) {
			if (L->key[j] > L->key[j + 1]) {
				flag = 1;
				L->key[0] = L->key[j];
				L->key[j] = L->key[j + 1];
				L->key[j + 1] = L->key[0];
				CompCount += 1;
				MoveCount += 3;
			}
		}
		m--;
	}
}
// 手动初始化无序列表
void ManRand(SqList* L)
{
	printf("\n请输入有%d个元素的随机序列(间隔符使用空格):\n", L->length);
	for (int i = 1; i <= L->length; i++) {
		scanf(" %d", &(L->key[i]));
	}
	setbuf(stdin, NULL);	// 对用户的额外输入进行处理
}

// 自动产生无序列表
void AutoRand(SqList *L)
{
	time_t t;

	srand((unsigned)time(&t));	// 随机种子使用时间产生

	for (int i = 1; i <= L->length; i++) {
		L->key[i] = rand() % 100;		// 产生100以内的随机数
	}
}

int main()
{
	SqList L;
	char choice;

	// 获取数据
	printf("请输入数据元素总个数:");
	scanf(" %d", &L.length);
	L.key = (KeyType*)malloc((L.length + 1) * sizeof(KeyType));
	printf("\n请选择序列生成方式(1.自动;2.手动):");
	while (1) {
		scanf(" %c", &choice);
		if (choice == '1') {
			AutoRand(&L);
			break;
		}
		else if (choice == '2') {
			ManRand(&L);
			break;
		}
		else {
			printf("\n选择有误,请重新选择:");
		}
	}
	printf("\n待排序序列为:\n");
	for (int i = 1; i <= L.length; i++) {
		printf("%d  ", L.key[i]);
	}

	// 选择排序方法
	printf("\n请选择排序方法(1.冒泡排序;2.快速排序):");
	while (1) {
		scanf(" %c", &choice);
		if (choice == '1') {
			CompCount = 0;	// 初始化
			MoveCount = 0;
			BubbleSort(&L);
			printf("\n排序好的序列为:\n");
			for (int i = 1; i <= L.length; i++) {
				printf("%d  ", L.key[i]);
			}
			printf("\n比较次数:%d\n移动次数:%d", CompCount, MoveCount);
			break;
		}
		else if (choice == '2') {
			CompCount = 0;	// 初始化
			MoveCount = 0;
			QuickSort(&L);
			for (int i = 1; i <= L.length; i++) {
				printf("%d  ", L.key[i]);
			}
			printf("\n比较次数:%d\n移动次数:%d", CompCount, MoveCount);
			break;
		}
		else {
			printf("\n选择有误,请重新选择:");
		}
	}
	_getch();

	return 0;
}

五、程序分析  

  1. 对于无序序列的产生,使用了两种方法(自动与手动),自动产生的随机序列的随机种子依赖于程序运行时间,手动产生依赖于I/O设备,可以对用户额外输入的数据作以舍弃;
  2. 程序使用MoveCount和CompCount来记录两种算法的元素移动次数和比较次数,可以明显的对比出两种算法的效率;

扩展知识:清空缓冲区使用了setbuf()函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值