数据结构(下)--快速排序(三种优化),归并排序,堆排序,基数排序思想及代码实现

本文详细介绍了快速排序的三种优化方法,包括固定位置选取、随机选取和三分选取基准法,以及归并排序、堆排序和基数排序的思想与实现。每种排序算法的时间复杂度、空间复杂度和稳定性特点也被阐述,并提供了应用场景的说明。

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

快速排序

思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序

时间复杂度:O(NlogN)

空间复杂度:O(1)
稳定性:不稳定

快速排序一共有三种:固定位置选取基准法(过程见下图)

随机选取基准法(基准选取不是arr[0],而是随机选取)

三分选取基准法(过程见下)

1.左中右三个数排序,并将中间数作为基准

2.将基准放到数组末尾(将基准与右端点前一个数交换)

3.从low向后查找第一个比tmp大的数,同时从high向前查找第一个比tmp小的数,两数交换(只找到9,此时9  6交换)

第一轮结束,此时6左边的数都小于它,右面的数都大于它,将数组分成两部分

接下来递归处理,对每一个子序列进行三位取中

算法优化:

1.如果排序的数据过少,直接插入法

2.聚集相同基准元素法 (一次划分后,把与par相等的元素聚在一起)   
画图文字表述过程:

一趟快排过程:

tmp中存放arr[0],用low指针指向头,high指针指向尾 

1.从high位置向前找第一个比tmp小的数,并与low所指位置交换,此时low向后走

2.low向后查找第一个比tmp大的数,并与之交换,此时high向前走

3.high向前查找第一个比tmp小的数,并与之交换,此时low向前走

4.low向后查找第一个比tmp大的数,并与之交换,此时high向前走

5.high向前查找第一个比tmp小的数,当low与high相遇时,一趟排序结束

一趟快排结束后,46左边的数都比46小,46右边的数都比它大,将这个数组以46为基准分成两部分,这两部分分别重复上述步骤排序

应用场景:大数据

​
int  Partion(int *arr, int low, int high)  //一趟快排,找基准
{
	int tmp;
	tmp = arr[low];
	while (low <high)
	{
		while (low < high&&arr[high] >= tmp)
		{
			--high;
		}
		if (low>high)
		{
			break;
		}
		else
		{
			arr[low] = arr[high];
		}
		while (low < high&&arr[low] <= tmp)
		{
			low++;
		}
		if (low>high)
		{
			break;
		}
		else
		{
			arr[high] = arr[low];
		}
		arr[low] = tmp;
	}
	return low;
}
void  QuickSort1(int *arr, int start, int end)  //递归排序
{
	int par;
	if (start< end)
	{
		par = Partion(arr, start, end);
		QuickSort1(arr, start, par - 1);
		QuickSort1(arr, par + 1, end);
	}
}

void QuickSort2(int *arr, int len)   //非递归写法
{
	int *stack = (int *)malloc(sizeof(int)*len);
	assert(&stack != NULL);
	int top = 0;
	int low = 0;
	int high = len - 1;
	int par = Partion(arr, low, high);
	stack[top++] = low;
	stack[top++] = high;
	while (top > 0)
	{
		high = stack[--top];
		low = stack[--top];
		if (low < high)
		{
			par = Partion(arr, low, high);
			if (par - low > high - par)
			{
				stack[top++] = low;
				stack[top++] = par - 1;
				if (high > par)
				{
					stack[top++] = par + 1;
					stack[top++] = high;
				}
			}
			else
			{
				stack[top++] = par + 1;
				stack[top++] = high;
				if (par > low)
				{
					stack[top++] = low;
					stack[top++] = par - 1;
				}
			}
		}
	}
}

void Swap(int *arr, int start, int end)  //交换函数
{
	int tmp = arr[start];
	arr[start] = arr[end];
	arr[end] = tmp;
}
void Focus_Same_Num(int *arr, int low, int par, int high, int *left, int *right) //聚集相同元素法
{
	if (low < high)
	{
		int ParLeftIndex  = par - 1;
		int ParRightIndex = par + 1;
		for (int i = par - 1; i >= low; i--)
		{
			if (arr[i] == arr[par])//如果两个值相等,那么交换,如果不是紧挨着的
			{
				if (i != ParLeftIndex)
				{
					Swap(arr, i, ParLeftIndex);//把相等的拉过来,聚集在一起
					ParLeftIndex--;
				}
				else
				{
					ParLeftIndex--;
				}

			}
		}
		*left = ParLeftIndex;

		for (int i = par + 1; i <= high; i++)
		{
			if (arr[i] == arr[par])
			{
				if (i != ParRightIndex)
				{
					Swap(arr, i, ParRightIndex);
					ParRightIndex++;
				}
				else
				{
					ParRightIndex++;
				}
			}
		}
		*right = ParRightIndex;
	}
}
void Quick(int *arr, int low, int high)  
{
	if (low < high)
	{
		int par = Partion(arr, low, high);
		int left = par - 1;
		int right= par + 1;
		//优化方式2:
		Focus_Same_Num(arr, low, par, high, &left, &right);
		//Qsort(arr,low,left);//分治左边序列
		//Qsort(arr,right,high);//分治
		printf("par==%d\n", par);
		printf("left==%d\n", left);
		printf("right==%d\n", right);
		if (par > low + 1)
		{
			Quick(arr, low, left);//par-1
		}
		if (par < high - 1)
		{
			Quick(arr, right, high);par+1
		}
	}
}
void QuickSort(int *arr, int len)
{
	Quick(arr, 0, len - 1);
}

​

 

归并排序

思想:分而治之

时间复杂度:O(NlogN)
空间复杂度:O(N)
稳定性:稳定
画图文字表述过程:不断的将原序列划分,直至两两一组,此时进行排序后再将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

应用场景:适用于总体无序,但各子项相对有序的数列

//归并排序:针对于外排(s1,e1;s2,e2,e1和e2不动,s1和s2走)
void Mearge(int *arr, int len, int gap)
{
	int *brr = (int *)malloc(sizeof(int)*len);
	assert(brr != NULL);
	int i = 0;
	int start1 = 0;
	int end1 = start1 + gap - 1;
	int start2 = end1 + 1;
	int end2 = start2 + gap - 1<len - 1 ? start2 + gap - 1 : len - 1;
	while (start2<len)//当前有两个归并段
	{
		while (start1 <= end1 && start2 <= end2)
		{
			if (arr[start1]<arr[start2])
			{
				brr[i++] = arr[start1++];
			}
			else
			{
				brr[i++] = arr[start2++];
			}
		}
		while (start1 <= end1)
		{
			brr[i++] = arr[start1++];
		}
		while (start2 <= end2)
		{
			brr[i++] = arr[start2++];
		}
		start1 = end2 + 1;
		end1 = start1 + gap - 1;
		start2 = end1 + 1;
		end2 = start2 + gap - 1>len - 1 ? start2 + gap - 1 : len - 1;
	}
	while (start1<len)
	{
		brr[i++] = arr[start1++];
	}
	for (int i = 0; i<len; i++)
	{
		arr[i] = brr[i];
	}
	free(brr);
	brr = NULL;
}

void Mearge_Sort(int *arr, int len)
{
	for (int i = 1; i<len; i *= 2)//2,4,8分段
	{
		Mearge(arr, len, i);
	}
}

堆排序

思想:用大根堆排序

时间复杂度:O(NlogN)
空间复杂度:O(1)             稳定性:不稳定
画图文字表述过程:

12 34 45 21 4 6 33 54 15

从最后一颗枝丫从上往下调整

    

                

                    大根堆

最后一个和第一个交换,此时54有序

   

调整第一颗枝丫

第一个与倒数第二个交换,此时45有序

调整第1  3颗枝丫,并使第一个与倒数第三个交换,此时34有序

 

应用场景:优先队列

void HeapAdjust(int *arr, int i, int len)
{
	int Child = 2 * i + 1;
	int tmp;
	for (; 2 * i + 1 < len; i = Child)
	{
		if (Child < len - 1 && arr[Child + 1] > arr[Child])
		{
			++Child;
		}
		if (arr[i] < arr[Child])
		{
			tmp = arr[i];
			arr[i] = arr[Child];
			arr[Child] = tmp;
		}
		else
		{
			break;
		}
	}
}
void HeapSort(int *arr, int len)
{
	int i, tmp = 0;
	for (i = len / 2 - 1; i >= 0; --i)
	{
		HeapAdjust(arr, i, len);
	}
	for (i = len - 1; i > 0; --i)
	{
		tmp = arr[0];
		arr[0] = arr[i];
		arr[i] = tmp;
		HeapAdjust(arr, 0, i);
	}
}

基数排序

思想:先对每个数的最低位排序,分别入0-9号桶,再出桶,一直重复到对最高位排序

时间复杂度:O(N)
空间复杂度:O(K+N)
稳定性:稳定
画图文字表述过程:

310 100 222 4410 20 3 952 774 18

出桶:310 100 4410 20 222 952 3 774 18

出桶:100 3 310 4410 18 20 222 952 774

出桶:3 18 20 100 222 310 4410 774 952

出桶:3 18 20 100 222 310 774 952 4410

应用场景:适用于位数少的数列

typedef struct Node
{
	int data;
	struct Node*next;
}Node, *List;


void InitList(List plist)
{
	assert(plist != NULL);
	plist->next = NULL;
}

static Node *GetNode(int val)
{
	Node *pGet = (Node *)malloc(sizeof(Node));
	assert(pGet != NULL);
	pGet->data = val;
	pGet->next = NULL;
	return pGet;
}
bool Insert_tail(List plist, int val)
{
	assert(plist != NULL);
	Node *p;
	for (p = plist; p->next != NULL; p = p->next)
	{
		;
	}
	Node *pGet = GetNode(val);
	p->next = pGet;
	return true;
}
bool DelFirst(List plist, int*rtv)
{
	assert(plist != NULL);
	Node*p = plist->next;
	if (plist->next == NULL)
	{
		return false;
	}
	if (rtv != NULL)
	{
		*rtv = p->data;
	}
	plist->next = p->next;
	free(p);
	p = NULL;
	return true;
}
int  GetMaxBit(int *arr, int len)//得到数组里数的最高位
{
	assert(arr != NULL);
	int max = 0;
	for (int i = 0; i < len; i++)
	{
		if (arr[i] > arr[max])
		{
			arr[max] = arr[i];
		}
	}
	int count = 0;
	while (arr[max] != 0)
	{
		count++;
		arr[max] /= 10;
	}
	return count;
}
int GetNum(int num, int figure)//得到num第figure位数字
{
	for (int i = 0; i < figure; i++)
	{
		num /= 10;
	}
	return num % 10;
}
void Radix(int *arr, int len, int figure)//入桶出桶操作
{
	assert(arr != NULL);
	Node head[10];
	int i;
	for (i = 0; i < 10; i++)
	{
		InitList(&head[i]);
	}
	//入桶
	int tmp;
	for (i = 0; i < len; i++)
	{
		tmp = GetNum(arr[i], figure);
		Insert_tail(&head[tmp], arr[i]);
	}
	//出桶
	i = 0;
	for (int j = 0; j < 10; )
	{
		if (DelFirst(&head[j], &arr[i]))//桶内有数据,则出成功
		{
			i++;
		}
		else
		{
			j++;//桶内没数据,就便历下一个桶
		}
	}
}
void Radix_Sort(int *arr, int len)//基数排序
{
	assert(arr != NULL);
	int count = GetMaxBit(arr, len);
	for (int i = 0; i < count; i++)
	{
		Radix(arr, len, i);
	}
}
void show(int *arr, int len)
{
	for (int i = 0; i < len; ++i)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值