深度剖析 快排 和 归并 非递归

我们重点在非递归,这里快排递归的代码入下

void swap(int* a, int* b)//交换
{
	int tmp = *a;
	*a = *b; 
	*b = tmp;
}
void InsertSort(int* a, int n)//插入排序
{
	assert(a);
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int x = a[end + 1];
		while (end >= 0)
		{
			if (a[end] > x)
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = x;
	}
}
int GetMid(int* a, int left, int right) //三数去中
{
	int mid = left + ((right - left) >> 1);
	if (a[left] > a[right])
	{
		if (a[mid] > a[left])
			return left;
		else if (a[right] > a[mid])
			return right;
		else
			return mid;
	}
	else  //a[left] <= a[rigth]
	{
		if (a[mid] < a[left])
			return mid;
		else if (a[mid] > a[right])
			return right;
		else
			return mid;
			
	}
}
int partion1(int* a, int left, int rigth)//hoare法
{
	int mid = GetMid(a, left, rigth);
	swap(&a[left], &a[mid]);
	int keyi = left;
	while (left < rigth)
	{
		while (left < rigth && a[rigth] >= a[keyi])  //找小
		{
			rigth--;
		}
		while (left < rigth && a[left] <= a[keyi]) //找大
		{
			left++;
		}
		swap(&a[left], &a[rigth]);    //小的往左边甩,大的往右边甩
	}
	swap(&a[left], &a[keyi]);
	return left;
}
int  partion2(int* a, int left, int rigth)//挖坑法
{
	int mid = GetMid(a, left, rigth);
	swap(&a[left], &a[mid]);
	int provit = left;
	int key = a[left];
	while (left < rigth)
	{
		while (left < rigth && a[rigth] >= key)
		{
			rigth--;
		}
		a[provit] = a[rigth];  //让坑位在右边
		provit = rigth;
		while (left < rigth && a[left] <= key)
		{
			left++;
		}
		a[provit] = a[left];
		provit = left;          //把坑位放在左边
	}
	a[provit] = key;
	return provit;
}
int partion3(int* a, int left, int rigth)//前后指针法
{
	int mid = GetMid(a, left, rigth);
	swap(&a[left], &a[mid]);
	int key = left;
	int prev = left;
	int cur = left + 1;
	while (cur <= rigth)
	{
		if (a[cur] >= a[key] && ++prev != cur)
		{
			swap(&a[cur], &a[prev]);
		}
		cur++;
	}
	swap(&a[prev], &a[key]);
	return prev;
}
void QuickSort(int* a, int left, int rigth) //快速排序
{
	if (left >= rigth)
		return;
	if (rigth - left + 1 < 10)
	{
		InsertSort(a + left, rigth - left + 1); //在个数小于10的时,可以采取插入来
	                                         	//优化递归次数
	}
	else
	{
		int key = partion1(a, left, rigth);
		QuickSort(a, left, key - 1);
		QuickSort(a, key + 1, rigth);
	}
	
}

快排的单趟排序一共有三种排序方式(hoare法,挖坑法,前后指针法)

前两种方法思想差不多,都是右边找小,左边找大,交换;前后指针法是把小的往左边甩,这里不过多介绍,每次单趟排序都是让一个元素拍好序

下面我来看非递归的思想,首先我们知道递归是先单趟排序确定一个元素(key)在递归[left,key-1]区间和[key+1] [rigth],而非递归则是用栈保存这些区间,直到栈空就表示这些区间处理完毕,及数组有序,我们画下图

 重复以上操作直到栈空数组有序

代码我们要序构建一个栈:栈的头文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int StackDataType;
struct Stack
{
	StackDataType data;
	int* a;
	int size;
	int capatity;
};
void StackInit(struct Stack* pc); //初始化栈
void StackPush(struct Stack* pc, StackDataType x);//进栈
void StackPop(struct Stack* pc);//弹栈
StackDataType StackTop(struct Stack* pc);//取栈顶元素
bool StackEmpty(struct Stack* pc);//判断栈是否为空
void StackDestroy(struct Stack* pc);//销毁栈

栈的源文件

#include"stack.h"


void StackInit(struct Stack* pc) //初始化栈
{
	assert(pc);
	pc->a = NULL;
	pc->size = pc->capatity = 0;
}
void StackPush(struct Stack* pc, StackDataType x)//进栈
{
	assert(pc);
	if (pc->size == pc->capatity)
	{
		int newcapatity = pc->capatity == 0 ? 4 : pc->capatity * 2;
		StackDataType* tmp = (StackDataType*)realloc(pc->a,sizeof(StackDataType) * newcapatity);
		if (tmp == NULL)
			exit(-1);
		pc->capatity = newcapatity;
		pc->a = tmp;
	}
	pc->a[pc->size++] = x;
}
void StackPop(struct Stack* pc)//弹栈
{
	assert(pc);
	if (pc->size == 0)
	{
		printf("Stack is full\n");
		exit(-1);
	}
	pc->size--;
}
StackDataType StackTop(struct Stack* pc)//取栈顶元素
{
	assert(pc);
	assert(pc->size >= 0);
	return pc->a[pc->size -1];
}
bool StackEmpty(struct Stack* pc)//判断栈是否为空
{
	assert(pc);
	return pc->size == 0;
}
void StackDestroy(struct Stack* pc)//销毁栈
{
	assert(pc);
	free(pc->a);
	pc->a = NULL;
	pc->size = pc->capatity = 0;
}

快排的非递归代码

#include"stack.h"
#include"sort.h"
void QuickNoR(int* a, int left, int rigth)
{
	struct Stack s;
	StackInit(&s);
	StackPush(&s, left);
	StackPush(&s, rigth);
	while (!StackEmpty(&s))
	{
		int end = StackTop(&s);   //右区间
		StackPop(&s);
		int begin = StackTop(&s);   //左区间
		StackPop(&s);
		int key = partion1(a, begin, end);    //每次在区间里确定一个元素的位置(及元素在数组最终的位置)
		if (end > key + 1)   //如果有区间大于key + 1,说明还有元素,如果等于1就不用在处理了,等于1及在数组中最终的位置
		{
			StackPush(&s, key + 1);   //先入左区间入栈
			StackPush(&s, end);      //后入右区间入栈
		}
		if (begin < key - 1)     //同上
		{
			StackPush(&s, begin);
			StackPush(&s, key - 1);
		}
	}
	//PrintSort(a, 10);   //这里是打印,大家可以忽略
	StackDestroy(&s);
}

我们下面来讲讲归并:

归并递归代码,每次处理完数组都需要存入零时数组

 

void _MergeSort(int* a, int left, int rigth, int* tmp)
{
	if (left >= rigth)
		return;
	int mid = left + ((rigth - left) >> 1);  //这里是求中位数
	_MergeSort(a, left, mid, tmp);      //递归左区间
	_MergeSort(a, mid + 1, rigth, tmp);   //递归右区间

	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = rigth;
	int i = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}
	for (int j = left; j <= rigth; j++)
	{
		a[j] = tmp[j];
	}
}
void MergeSort(int* a, int n)//归并排序
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	assert(tmp);
	_MergeSort(a, 0, n - 1, tmp);     //用子函数递归
	free(tmp);
	tmp = NULL;
}

归并的非递归难在怎么用迭代控制间距和边界处理,这是十分麻烦的,我们看图

void MergrSortNon(int* a, int n) //归并的非递归
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
		return;
	int  gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			//[i,i+gap -1]   [i+gap] [i+2*gap -1]
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			int index = i;
              // [begin1,end1] [begin2,end2]
			if (end1 >= n || begin1 >= n)   //如果end1 和 begin1越界则说明不需处理了
			{
				break;
			}
			if (end2 >= n)    //end2越界一定一定要修正,不然数组不会有序,因为没右处理这个数,图中画的 数是 2 ^ n 个数所以不用考虑越界,那要是9 ,10 ...个数这需要处理
			{
				end2 = n - 1;
			}
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[index++] = a[begin1++];
				}
				else
				{
					tmp[index++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[index++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[index++] = a[begin2++];
			}
			for (int j = i; j <= end2; j++)  //把零时数组考会原数组
			{
				a[j] = tmp[j];
			}
		
		}
		gap *= 2;    //间距* 2
	}
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值