【数据结构之八大内排序】交换排序(冒泡排序,快速排序)

本文深入解析了两种经典的排序算法——冒泡排序与快速排序。冒泡排序通过相邻元素间的交换实现升序排列,其时间复杂度为O(n^2),但在最优情况下,只需进行n-1次比较。快速排序采用分治策略,通过选择一个主元,将序列分为三部分,并递归地排序左右子序列,时间复杂度为O(nlogn)。改进的快速排序通过优化主元选择和避免最坏情况来提升效率。

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

冒泡排序

稳定

最差时间:O(n2)(反向有序)

平均时间:O(n2)

最好时间:O(n)(正向有序,作n-1次比较即可)

空间:O(1) 

冒泡排序的线性实现,包括传统冒泡排序(MyBubble)以及参考书的冒泡排序方法(BookBubble),已经改进的冒泡排序():


#include <stdio.h>
#define MaxSize 100
#define Swap(x,y,t)((t)=(x),(x)=(y),(y)=(t))
typedef int T;
typedef struct list {
	int Size,MaxList;
	T Elements[MaxSize];
}List;

List CreateList(int n , int maxSize) {
	List list;
	int i ; 
	list.Size=n; 
	list.MaxList = maxSize;
	for(i=0;i<n;i++) {
		scanf("%d",&(list.Elements[i]));
	}
	return list;
	
}
void MyBubble(List *list) {
	int i , j,temp; 
	for(i=1;i<=list->Size-1;i++) {
		for(j=0;j<list->Size-i;j++) {
			if(list->Elements[j]>list->Elements[j+1])Swap(list->Elements[j],list->Elements[j+1],temp);
		}
	}
}

void BookBubble(List *list) {
	int j,i=list->Size-1;
	T temp;
	int sorted=0;
	while(i>0&&!sorted) {
		sorted=1;
		for(j=0;j<i;j++) {
			if(list->Elements[j+1]<list->Elements[j]) {
				Swap(list->Elements[j],list->Elements[j+1],temp);
				sorted=0;
			}

		}
		i--;
	}
}
/*
	记录该趟排序最后一次被交换的两记录的位置,例如在某趟排序中最后发生交换的记录为Ks,Ks+1,
	下趟排序的序列范围应为K0~Ki-1,改进后的排序将下趟排序范围定位K0~Ks
*/
void ModifyBubble(List *list) {
	int i , j ,last;
	T temp;
	i = list->Size-1;
	while(i>0) {
		last=0;
		for(j=0;j<i;j++) {
			if(list->Elements[j+1]<list->Elements[j]) {
				Swap(list->Elements[j],list->Elements[j+1],temp);
				last=j;
			}
		}
		i=last;
	}

}

void PrintList(List list) {
	int i;
	for(i=0;i<list.Size-1;i++) {
		printf("%d ",list.Elements[i]);
	}
	printf("%d\n",list.Elements[list.Size-1]);
}
void main() {
	int n ,maxList;
	List list;
	while(scanf("%d%d",&n,&maxList)) {
		list = CreateList(n,maxList);
	//	MyBubble(&list);
	//	BookBubble(&list);
		ModifyBubble(&list);
		PrintList(list);
	}
}

快速排序

不稳定
最好时间:O(nlogn)[经过每一趟排序后,若被分割成两个大小相近的子序列时,效率最好]

平均时间:O(nlogn)
最差时间:O(n2) [反之,若每次分割的两个子序列中有一个为空,即初始序列有序(顺序或逆序),效率最低]
空间:O(logn)


基本思想:首先在待排序的序列(K0, K1, …, Kn-1)中选择一个记录作为分划元素,被称为主元(pivot)。
例如可以取序列的第一个记录为主元。
接着,以主元为轴心,经过一趟特殊的排序处理,实现对原序列的重新排列。
设在新序列中,主元处于位置k处。则新序列应满足条件:从位置0到位置k-1的所有记录的关键字值都小于或等于主元的值;

从位置k+1到位置n-1的所有记录的关键字值都大于或等于主元的值。这样,序列被分成三部分:主元和左、右两个子序列。


改进一、改进主元的选择方法为了避免发生最坏情况:
(1)将A[(left+right)/2]作为分割元素,与A[left]交换;
(2)选left到right间的随机整数k,将A[k]与A[left]交换;

(3)取A[left]、A[(left+right)/2]和A[right]之中间值与A[left]交换。


改进二、

(1)为提高快速排序的效率,将递归程序改为非递归的,具体做法:使用一个堆栈,在一次分划操作后,将其中一个序列的上下界进栈保存,而对另一个子序列继续进行分划排序。当处理这个子序列时,仍将分划得到的一个子序列的上下界进栈保存,对另一个子序列进行排序,直到分划所得的子序列中至多只有一个元素为止,再从栈中取出保存的某个尚未排序的子序列的上、下界对其进行快速排序。

(2)为减小栈空间,先对较小的子序列排序,将较大的子序列进栈,这样可使所需的栈空间的大小降为O(logn);


附上快排完整实现代码:

#include <stdio.h>
#define MaxSize 100
#define Swap(x,y,t)((t)=(x),(x)=(y),(y)=(t))
#define MaxNum 10000000
typedef int T;
typedef struct list {
	int Size,MaxList;
	T Elements[MaxSize];
}List;

List CreateList(int n , int maxSize) {
	List list;
	int i ; 
	list.Size=n+1; 
	list.MaxList = maxSize;
	for(i=0;i<n;i++) {
		scanf("%d",&(list.Elements[i]));
	}
	return list;
	
}

int Partition(List *list,int left,int right) {
	int i = left,j=right+1;//确定分划序列的指针i,j
	T temp;
	T pivot = list->Elements[left];
	do {
		do i++ ; while(list->Elements[i]<pivot);//i从左向右找第一个不小于分割元素pivot的元素
		do j-- ; while(list->Elements[j]>pivot);//j从又向左找第一个不大于分割元素pivot的元素
		if(i<j)Swap(list->Elements[i],list->Elements[j],temp); //若i<j,则交换两个元素
	}while(i<j);
	//交换分割元素Elements[left]和Elements[j]
	Swap(list->Elements[left],list->Elements[j],temp);
	return j ;
}

void QSort(List *list,int left,int right) {
	int k ; 
	if(left<right) {
		k=Partition(list,left,right);//对left和right之间的序列进行分割
		QSort(list,left,k-1);//对左子序列进行快速排序
		QSort(list,k+1,right);//对右序列进行快速排序
	}
}

void QuickSort(List *list) {
	list->Elements[list->Size]=MaxNum;
	QSort(list,0,list->Size-1);
}

void PrintList(List list) {
	int i;
	for(i=0;i<list.Size-1;i++) {
		printf("%d ",list.Elements[i]);
	}
	printf("%d\n",list.Elements[list.Size-1]);
}

void main() {
	int n ,maxList;
	List list;
	while(scanf("%d%d",&n,&maxList)) {
		list = CreateList(n,maxList);
		QuickSort(&list);
		PrintList(list);

	}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值