【C语言】快速排序(递归与非递归)

快速排序是一种交换排序,时间复杂度为O(N*logN),且在各种场景具有良好的适应性,但是是一种不稳定的排序方式,排序之后相同值数据的相对位置会变化。

单趟排序

找大小交换

霍尔版本的快速排序,也是最经典的版本

  • 定义key为序列第一个值的下标
  • 左右指针分别找到比key小或者大的位置,并交换
  • 重复寻找、交换的过程直到左右指针相遇
  • 将相遇处值与key交换,一定要让右指针先走,这样key与相遇处交换时,才能保证key大于相遇处

返回值为下一趟排序key的位置,begin和end是序列起止下标

int PartSort1(int* arr, int begin, int end)// Hoare
{
	int key = begin;
	int left = begin, right = end;

	while (left < right)
	{
		while (arr[right] >= arr[key] && left < right)// 一定要右边先走,找比key小的
		{
			right--;
		}
		while (arr[left] <= arr[key] && left < right)// 左边找比key大的
		{
			left++;
		}
		Change(&arr[left], &arr[right]);// 分别找到之后交换
	}
	Change(&arr[left], &arr[key]);
	key = left;

	return key;
}
挖坑法

和霍尔版本差不多,但是在思路上进行了优化

  • 定义key为序列第一个值
  • 把key的位置拿出来形成一个hole
  • 右指针先走,找到比key小的,把该位置的值拿出来填到hole处,该处成为新的hole
  • 左指针找到比key大的,把该位置的值拿出来填到hole处,该处成为新的hole
  • 不断重复直到左右指针相遇
  • 相遇处用最开始的key填充

返回值为下一趟排序key的下标

int PartSort2(int* arr, int begin, int end)// 挖坑法
{
	int key = arr[begin];
	int hole = begin;
	int left = begin, right = end;
	while (left < right)
	{
		while (arr[right] >= key && left < right)
		{
			right--;
		}
		arr[hole] = arr[right];
		hole = right;
		while (arr[left] <= key && left < right)
		{
			left++;
		}
		arr[hole] = arr[left];
		hole = left;
	}
	arr[left] = key;
	return left;
}
前后指针法

利用两个指针,与以上排序方式有本质上的区别

  • 定义key和prev为序列第一个值的下标,cur为第二个值的下标
  • cur先走,遇到比key小的值的时候停下,prev++并且二者的值交换,cur++
  • 直到cur越界,交换prev和key的值

返回值为下一趟排序key的位置

int PartSort3(int* arr, int begin, int end)// 前后指针法
{
	int key = begin;
	int prev = begin;
	int cur = prev + 1;
	while (cur <= end)
	{
		while (arr[cur] >= arr[key] && cur <= end)
		{
			cur++;
		}
		if (cur <= end)
		{
			prev++;
			Change(&arr[cur], &arr[prev]);
			cur++;
		}
	}
	Change(&arr[prev], &arr[key]);
	return prev;
}

递归实现

递归方式与二叉树前序遍历类似

void QuickSort(int* arr, int begin, int end)
{
	if (begin >= end)// 只有一个数或者非法区间就不用再排序了
		return;

	int mid = Div(arr, begin, end); //优化方式:三数取中
	Change(&arr[mid], &arr[begin]);
	int key = begin;// 选取key

	key = PartSort3(arr, begin, end);// 单趟排序,划分区间为[begin, key - 1], key, [key + 1, end]
	QuickSort(arr, begin, key - 1);// 递归左区间
	QuickSort(arr, key + 1, end);// 递归右区间
}

非递归实现

非递归需要利用数据结构中的栈来模拟这个递归子区间的过程(栈先进后出,模拟二叉树的前序遍历)

void QuickSortNonR(int* arr, int left, int right)
{
	Stack s;
	StackInit(&s);
	int mid = Div(arr, left, right);
	Change(&arr[mid], &arr[left]);
	int key = left;
	
	StackPush(&s, left);
	StackPush(&s, right);
	while (StackSize(&s))
	{
		if (left >= right)
			break;
		int right = StackTop(&s);
		StackPop(&s);
		int left = StackTop(&s);
		StackPop(&s);

		key = PartSort1(arr, left, right);// 单趟排序划分区间
		if (left < key - 1)
		{
			StackPush(&s, left);   // 区间[left, key - 1]
			StackPush(&s, key - 1);
		}
		if (key + 1 < right)
		{
			StackPush(&s, key + 1);   // 区间[key + 1, right]
			StackPush(&s, right);
		}
	}
	StackDestroy(&s);
}
用到的栈操作函数定义补全
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
	STDataType* _a;
	int _top;		// 栈顶
	int _capacity;  // 容量 
}Stack;

Stack ST;

void StackInit(Stack* ps)
{
	assert(ps);
	ps->_a = NULL;
	ps->_capacity = 0;
	ps->_top = -1;//top指向栈顶元素
}

void StackPush(Stack* ps, STDataType data)
{
	assert(ps);
	if (ps->_capacity == ps->_top + 1)
	{
		int newcapacity = ps->_capacity == 0 ? sizeof(STDataType) : ps->_capacity * 2;
		STDataType* tmp = (STDataType*)realloc(ps->_a, newcapacity * sizeof(STDataType));
		ps->_capacity = newcapacity;
		if (tmp == NULL)
		{
			perror("malloc fail");
			return;
		}
		ps->_a = tmp;
	}
	ps->_a[ps->_top + 1] = data;	
	ps->_top++;
}

void StackPop(Stack* ps)
{
	assert(ps);
	assert(ps->_a);
	ps->_top--;
	ps->_capacity--;
}

STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(ps->_a);
	STDataType top = ps->_a[ps->_top];
	return top;
}

int StackSize(Stack* ps)
{
	assert(ps);
	return ps->_top + 1;
}

void StackDestroy(Stack* ps)
{
	free(ps->_a);
	ps->_a = NULL;
	ps->_top = -1;
	ps->_capacity = 0;
}

优化方式:三数取中

begin, (begin + end) / 2, end处的值比较,返回的mid就是中间大小的下标。

把mid处的数与begin对调,使得key为中间数,能避免极端情况出现(key为序列中的最值),提高程序效率。

int Div(int* arr, int begin, int end)
{
	int mid = (begin + end) / 2;
	if (arr[begin] >= arr[mid])
	{
		if (arr[mid] >= arr[end])
		{
			return mid;
		}
		else if(arr[begin] >= arr[end])
		{
			return end;
		}
		else
		{
			return begin;
		}
	}
	else//arr[begin] < arr[mid]
	{
		if (arr[mid] <= arr[end])
		{
			return mid;
		}
		else if (arr[begin] >= arr[end])
		{
			return begin;
		}
		else
		{
			return end;
		}
	}
}
  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
快速排序是一种常用的排序算法,它通过选择一个基准元素,将数组分割成两个子数组,然后对子数组进行递归排序,最终得到一个有序数组。 下面是使用非递归方式实现的快速排序算法C语言代码示例: ```c #include <stdio.h> #define MAX_STACK_SIZE 100 // 交换两个元素的值 void swap(int* a, int* b) { int temp = *a; *a = *b; *b = temp; } // 快速排序非递归实现 void quickSort(int arr[], int left, int right) { // 创建一个栈用于存储待排序子数组的起始和结束下标 int stack[MAX_STACK_SIZE]; int top = -1; // 初始时将整个数组的起始和结束下标入栈 stack[++top] = left; stack[++top] = right; while (top >= 0) { // 出栈获取当前待排序子数组的起始和结束下标 right = stack[top--]; left = stack[top--]; // 选择基准元素为子数组的最后一个元素 int pivot = arr[right]; int i = left - 1; // 将小于等于基准元素的元素放到左侧,大于基准元素的元素放到右侧 for (int j = left; j <= right - 1; j++) { if (arr[j] <= pivot) { i++; swap(&arr[i], &arr[j]); } } // 将基准元素放到正确的位置 swap(&arr[i + 1], &arr[right]); // 如果左侧子数组的长度大于1,则将左侧子数组的起始和结束下标入栈 if (i - left + 1 > 1) { stack[++top] = left; stack[++top] = i; } // 如果右侧子数组的长度大于1,则将右侧子数组的起始和结束下标入栈 if (right - i - 1 > 1) { stack[++top] = i + 2; stack[++top] = right; } } } int main() {
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值