C语言造轮子————快速排序

快速排序基本思想:通过一趟排序 将待排序序列 分割成独立的两个部分,其中一部分记录的关键字均比另一部分关键字小(分割位置的Key  已达到它应该在的位置    例如:升序    Key左边的都比它小,Key右边的都比它大 ,故Key即是最终排序时所在的位置,不需要在更改) 继续对这两个部分进行继续分割排序,最终达到整个序列有序的目的。

这里列举了   Hoare大佬、挖坑法、前后指针法、非递归、改进的快速排序结合了直接插入排序   五个方面进行 阐述

Hoare创世人大佬的方法:


void Swanp(int* x, int* y)
{
	int a = *x;
	*x = *y;
	*y = a;
}


//Hoare版本
void QuickSortMethod1(int* head, int left, int right)
{
	if (left >= right)
		return;
	//确定阈值
		//三数取中  下面三个if 使右边的最大  中间的最小   左边的中间值即为阈值
	int Keyi = (left + right) / 2;
	if (head[left] > head[right])
		Swanp(&head[left], &head[right]);
	if (head[Keyi] > head[right])
		Swanp(&head[Keyi], &head[right]);
	if (head[left] < head[Keyi])
		Swanp(&head[Keyi], &head[left]);
	int fail = right;
	int begin = left;
	while (begin < fail)
	{
		while (fail > begin && head[fail] >= head[left])   //右边找小
			fail--;

		while (fail > begin && head[begin] <= head[left])    //左边找大
			begin++;
		
		if(fail!=begin)
		Swanp(&head[fail], &head[begin]);

	}
	if(fail!=left)
	Swanp(&head[fail], &head[left]);

	if (fail - 1 > left)
		QuickSortMethod1(head, left, fail - 1);
	
	if (right> fail+1)
		QuickSortMethod1(head, fail+1, right);

}

三数取中确定Key后面在叙述

这个算法的思想就是:

总体思想如上  不多赘述了。

挖坑法:

//挖坑法
void QuickSortMethod2(int* head, int left, int right)
{
	if (left >= right)
		return;
	//确定阈值
		//三数取中  下面三个if 使右边的最大  中间的最小   左边的中间值即为阈值
	int Keyi = (left + right) / 2;
	if (head[left] > head[right])
		Swanp(&head[left], &head[right]);
	if (head[Keyi] > head[right])
		Swanp(&head[Keyi], &head[right]);
	if (head[left] < head[Keyi])
		Swanp(&head[Keyi], &head[left]);
	int fail = right;
	int begin = left;
	int Key = head[left]; //存一下 关键字
	while (begin < fail)
	{
		while (fail > begin && head[fail] >= Key)   //右边找小
			fail--;
		if(begin!=fail)
		head[begin] = head[fail];

		while (fail > begin && head[begin] <= Key)    //左边找大
			begin++;
		if (begin != fail)
		head[fail] = head[begin];

	}
	    head[fail] = Key;

	if (fail - 1 > left)
		QuickSortMethod2(head, left, fail - 1);

	if (right > fail + 1)
		QuickSortMethod2(head, fail + 1, right);
}

前后指针法:

//前后指针法
void QuickSortMethod3(int* head, int left, int right)
{
	if (left >= right)
		return;
	//确定阈值
		//三数取中  下面三个if 使右边的最大  中间的最小   左边的中间值即为阈值
	int Keyi = (left + right) / 2;
	if (head[left] > head[right])
		Swanp(&head[left], &head[right]);
	if (head[Keyi] > head[right])
		Swanp(&head[Keyi], &head[right]);
	if (head[left] < head[Keyi])
		Swanp(&head[Keyi], &head[left]);
	int slow = left;
	int fast = left+1;
	int Key = head[left]; //存一下 关键字

	while (fast <= right)
	{
		if (head[fast] <= Key)
		{
			if (slow + 1 != fast)   //说明中间有大于关键词的值了   要进行交换
			Swanp(&head[slow+1], &head[fast]);

			slow++;
		}
			fast++;
	}
	Swanp(&head[left], &head[slow]);


	if (slow - 1 > left)
		QuickSortMethod3(head, left, slow - 1);

	if (right > slow + 1)
		QuickSortMethod3(head, slow + 1, right);

}

非递归快速排序:

 

一般  递归改非递归   

                           方法一 :直接改循环 (例如‌费波纳界数列(Fibonacci sequence)‌)

                           方法二 :  使用栈辅助改循环

这里采用栈来操作比较简单

1、栈里面Pop得左右区间,单趟排序

2、单趟分割后,自区间入栈

3、子区间只有一个值或不存在时就不入栈

//非递归 快速排序
void QuickSortMethod4(int* head, int left, int right)
{
	
	assert(head);
	if (left >= right)
		return;
	Stack stack;
	StackInit(&stack);
	StackPush(&stack, right);
	StackPush(&stack, left);
	while (!StackEmpty(&stack))
	{
		int begin = StackTop(&stack);
		StackPop(&stack);
		int end= StackTop(&stack);
			StackPop(&stack);
			
			//确定阈值
					//三数取中  下面三个if 使右边的最大  中间的最小   左边的中间值即为阈值
			int Keyi = (begin + end) / 2;
			if (head[begin] > head[end])
				Swanp(&head[begin], &head[end]);
			if (head[Keyi] > head[end])
				Swanp(&head[Keyi], &head[end]);
			if (head[begin] < head[Keyi])
				Swanp(&head[Keyi], &head[begin]);

			int slow = begin;
			int fast = begin + 1;
			int Key = head[begin]; //存一下 关键字

			while (fast <= end)
			{
				if (head[fast] <= Key)
				{
					
					if (slow + 1 != fast)   //说明中间有大于关键词的值了   要进行交换
						Swanp(&head[slow+1], &head[fast]);
					slow++;

				}
				fast++;
			}
			Swanp(&head[begin], &head[slow]);


			if (slow - 1 > begin)
			{
				StackPush(&stack, slow - 1);
				StackPush(&stack, begin);
			}

			if (slow + 1 < end)
			{
				StackPush(&stack, end);
				StackPush(&stack, slow + 1);
			}
	}
}

栈函数原码(头文件):

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>

#define QuickSortThreshold 7


typedef int SatackDataType;


typedef struct Stack
{
	int capacity;
	int num;
	SatackDataType* head;

}Stack;
void StackPush(Stack* stack, SatackDataType x);
void StackPop(Stack* stack);
StackInit(Stack* stack);
SatackDataType StackTop(Stack* stack);
bool StackEmpty(Stack* stack);
void StackDestroy(Stack* stack);




//直接插入排序(升序)
void InsertSort(int* a, int n);

栈函数(源文件)接口函数:

#define _CRT_SECURE_NO_WARNINGS
#include"标头.h"


StackInit(Stack* stack)
{
	assert(stack);
	stack->head = NULL;
	stack->capacity = stack->num = 0;
}

void StackPush(Stack* stack, SatackDataType x)
{
	assert(stack);
	if (stack->capacity == stack->num)   //扩容
	{
		stack->capacity = stack->capacity == 0 ? 2 : stack->capacity * 2;
		stack->head = (SatackDataType*)realloc(stack->head, sizeof(SatackDataType) * stack->capacity);
		if (stack->head == NULL)
		{
			perror("StackPush realloc fail");
			exit(-1);
		}
	}

	stack->head[stack->num] = x;
	stack->num++;
}


void StackPop(Stack* stack)
{
	if (stack->num == 0)
		return;

	stack->num--;
}


SatackDataType StackTop(Stack* stack)
{
	if (stack->num == 0)
		return;

	return stack->head[stack->num - 1];

}

bool StackEmpty(Stack* stack)
{
	if (stack->num == 0)
		return true;
	return false;
}


void StackDestroy(Stack* stack)
{
	free(stack->head);
	stack->head = NULL;
	stack->capacity = stack->num = 0;
}






//直接插入排序 (升序)
//排升序
void InsertSort(int* a, int n)
{
	int i = 1;

	while (i <= n - 1)
	{
		int j = 0;
		while (j <= i)    //找到i应该在的位置
		{
			if (a[i] < a[j])
				break;
			j++;
		}
		int tem = i;
		int obj = a[i];  //暂存需要插入的值
		while (tem > j)
		{
			a[tem] = a[tem - 1];
			tem--;
		}
		a[tem] = obj;
		i++;
	}


}

最后快速排序的优化:

在待排序序列较小时,快速排序的速度其实并不理想,反而没有直接插入排序来的快,故可以考虑,当元素个数小于一定值时,进行插入排序,大于这个值时采用快速排序。

有的资料显示认为 阈值为7比较合适,也有资料认为阈值为50较为合适,经过个人测试 选择了7作为阈值  

源码:


//快速排序优化+直接插入排序
void QuickSortMethod5(int* head, int left, int right)
{
	if (left >= right)
		return;
	if (right - left + 1 > 7)
	{
		//确定阈值
			//三数取中  下面三个if 使右边的最大  中间的最小   左边的中间值即为阈值
		int Keyi = (left + right) / 2;
		if (head[left] > head[right])
			Swanp(&head[left], &head[right]);
		if (head[Keyi] > head[right])
			Swanp(&head[Keyi], &head[right]);
		if (head[left] < head[Keyi])
			Swanp(&head[Keyi], &head[left]);
		int slow = left;
		int fast = left + 1;
		int Key = head[left]; //存一下 关键字

		while (fast <= right)
		{
			if (head[fast] <= Key)
			{
				if (slow + 1 != fast)   //说明中间有大于关键词的值了   要进行交换
					Swanp(&head[slow + 1], &head[fast]);

				slow++;
			}
			fast++;
		}
		Swanp(&head[left], &head[slow]);


		if ((slow - 1 > left) && (slow - left > 7))
			QuickSortMethod5(head, left, slow - 1);
		else
			InsertSort(head + left, slow - left);

		if ((right > slow + 1) && (right - slow > 7))
			QuickSortMethod5(head, slow + 1, right);
		else
			InsertSort(head + slow + 1, right - slow);
	}
	else
		InsertSort(head, right-left+1);
}

测试结果:测试结果都是没问题的  大家可用 百万 千万 亿个量级的数  来跑一跑  测试一下

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值