快速排序(非递归)——C语言实现

文章目录

  • 一、递归算法快速排序分析
  • 二、非递归算法实现快速排序
    • 2.1 需求分析
    • 2.2 图解分析(如何利用栈)
  • 三、快排(非递归)代码

在这里插入图片描述

一、递归算法快速排序分析

在快速排序这篇文章中——>【快速排序点击这里】已经提到并分析了,递归会建立函数栈帧,递归的深度越深,占用栈区的空间就越大,栈区的大小一般是8M,10M。当深度足够深时,栈区的空间就会被用完,导致栈溢出,即便是加了小区间优化,依然会存在栈溢出的缺陷,所以我们必须寻求一种稳妥的方法,这种方法就是非递归实现。

二、非递归算法实现快速排序

2.1 需求分析

  我们采用递归算法来实现快速排序时,我们递归的到底是什么?很简单,我们通过递归,就是再次调用该函数的功能,也就是单趟排序。那么我们如果不用递归,该如何保证能像递归一样,一直调用单趟排序这个功能呢?

答案就是:循环
  只要满足循环条件,就一直循环下去。 到这里,我们又会想到,每一次进行单趟排序的函数参数不一样啊。是的,没错。由于递归会在栈上建立函数栈帧,而栈帧里面伴我们存着下次调用该函数的参数,所以,如果我们用非递归,那我们就必须把每一次循环的参数记录下来,供单趟排序使用。怎么解决呢:
答案就是:使用数据结构里面的栈

2.2 图解分析(如何利用栈)

众所周知,栈的特性是先进后出,我们拿数组arr=[5,2,4,7,9,1,3,6]来举栗子。
第一步:我们先把区间的右边界值7进行压栈,然后把区间的左边界值0进行压栈,那我们取出时就可以先取到左边界值,后取到后边界值
快速排序(非递归)——C语言实现_第1张图片
第二步:我们获取栈顶元素,先取到0给left,后取到7给right,进行单趟排序
快速排序(非递归)——C语言实现_第2张图片第三步:第一趟排完后,区间被分为左子区间和右子区间。为了先处理左边,所以我们先将右子区间压栈,分别压入7和5,然后压入左子区间,3和0
快速排序(非递归)——C语言实现_第3张图片第四步:取出0和3进行单趟排序
快速排序(非递归)——C语言实现_第4张图片第五步:此时左子区间又被划分为左右两个子区间,但是右子区间只有4一个值,不再压栈,所以只入左子区间,将1和0压栈
快速排序(非递归)——C语言实现_第5张图片
第六步:取出0和1进行单趟排序
快速排序(非递归)——C语言实现_第6张图片第七步:至此,左子区间全部被排完,这时候才可以出5和7排右子区间,是不是很神奇?这个流程其实和递归是一模一样的,顺序也没变,但解决了递归的致命缺陷——栈溢出。后面的流程就不一一展现了
快速排序(非递归)——C语言实现_第7张图片

三、快排(非递归)代码

这里我会把数据结构的栈也放出来,三数取中也加在里面

QuickSort:

//交换函数
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

//三数取中
int GetMinIndex(int* arr, int left, int right)
{
	int mid = (left + right) >> 1;
	if (arr[left] < arr[mid])
	{
		if (arr[mid] < arr[right])
		{
			return mid;
		}
		if (arr[left] < arr[right] && arr[right] < arr[mid])
		{
			return right;
		}
		return left;
	}
	else//arr[left] >= arr[mid]
	{
		if (arr[left] < arr[right])
		{
			return left;
		}
		if (arr[mid] < arr[right] && arr[right] < arr[left])
		{
			return right;
		}
		return mid;
	}
}

//快排非递归
void QuickSort(int* arr, int n)
{
	ST st;
	StackInit(&st);

	//把左右区间压栈,先压右边
	StackPush(&st, n - 1);
	//后压左边
	StackPush(&st, 0);

	//只要栈不为空,就继续分割排序
	while (!StackEmpty(&st))
	{
		//从栈里面取出左右区间
		int left = StackTop(&st);
		StackPop(&st);
		int right = StackTop(&st);
		StackPop(&st);

		int index = GetMinIndex(arr, left, right);
		//因为我们下面的逻辑都是把第一个数作为key,
		//为了避免改动代码,这里我们直接交换就可以
		Swap(&arr[left], &arr[index]);

		//开始单趟排序
		int begin = left;
		int end = right;
		int pivot = begin;
		int key = arr[begin];

		while (begin < end)
		{
			//end开始找小
			while (begin < end && arr[end] >= key)
			{
				end--;
			}
			arr[pivot] = arr[end];
			pivot = end;
			//begin开始找大
			while (begin < end && arr[begin] <= key)
			{
				begin++;
			}
			arr[pivot] = arr[begin];
			pivot = begin;
		}
		pivot = begin;
		arr[pivot] = key;

		//区间分为[left,pivot-1]pivot[pivot+1,right]
		//利用循环继续分割区间
		//先入右子区间
		if (pivot + 1 < right)
		{
			//说明右子区间不止一个数
			//先入右边边界
			StackPush(&st, right);
			//再入左边边界
			StackPush(&st, pivot+1);
		}

		//再入左子区间
		if (left < pivot-1)
		{
			//说明左子区间不止一个数
			//先入右边边界
			StackPush(&st, pivot-1);
			//再入左边边界
			StackPush(&st, left);
		}	
	}
	StackDestory(&st);
}

Stack.h:

#pragma once
#include
#include
#include
#include
typedef int STDateType;
typedef struct Stack
{
	STDateType* a;
	STDateType top;
	int capacity;
}ST;

//初始化栈
void StackInit(ST* ps);

//销毁栈
void StackDestory(ST* ps);

//入栈
void StackPush(ST* ps, STDateType x);

//出栈
void StackPop(ST* ps);

//取出栈顶元素
STDateType StackTop(ST* ps);

//获取当前栈大小
int StackSize(ST* ps);

//判断栈是否为空
bool StackEmpty(ST* ps);

Stack.c:

#include"Stact.h"

//初始化
void StackInit(ST* ps)
{
	ps->a = (STDateType*)malloc(sizeof(STDateType) * 4);
	ps->top = 0;
	ps->capacity = 4;
}

//销毁栈
void StackDestory(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

//入栈
void StackPush(ST* ps, STDateType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		STDateType* tmp = (STDateType*)realloc(ps->a, ps->capacity * 2 * sizeof(STDateType));
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit (-1);
		}
		else
		{
			ps->a = tmp;
			ps -> capacity *= 2;
		}
	}
	ps->a[ps->top] = x;
	ps->top++;
}


//出栈
void StackPop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	ps->top--;
}

//获取栈顶元素
STDateType StackTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	return ps->a[ps->top - 1];
}

//获取栈元素个数
int StackSize(ST* ps)
{
	assert(ps);
	return ps->top;
}

//判断栈是否为空
bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}

如果写C++代码,那么这里的栈以直接用STL里面的栈,很方便。

你可能感兴趣的:(常用排序算法,c++,排序算法,算法,c语言)