C语言实现快速排序和归并排序

快速排序

快速排序最坏的情况(在接近有序或有序时)是每次选的key都是最大或最小的,时间复杂度是O(N^2);最好的情况是每次都选的key都是中位数,时间复杂度是O(N* LogN),

方法一:左右指针法

int PartSort1(int* a, int begin, int end)//单趟快排方法1:左右指针法
{
	int midIndex = GetMidIndex(a, begin, end);
	Swap(&a[midIndex], &a[end]);//把中间值放到最后
	int keyindex = end;
 
	while (begin < end)//选哪一边做keyindex要让另一边先走,否则begin和end会错过
	{
		while (begin < end && a[begin] <= a[keyindex])//找大的
		{
			++begin;
		}
 
		while (begin < end && a[end] >= a[keyindex])//找小的
		{
			--end;
		}
 
		Swap(&a[begin], &a[end]);//左边找大,右边找小,再交换
	}
 
	Swap(&a[begin], &a[keyindex]);//begin和end相遇时交换begin和Keyindex
	return begin;//返回相遇的位置,将原数组分成两段,再递归
}

方法二:挖坑法

int PartSort2(int* a, int begin, int end)//单趟快排方法2:挖坑法
{
	int midIndex = GetMidIndex(a, begin, end);
	Swap(&a[midIndex], &a[end]);
	int keyindex = a[end];//end是第一个坑(坑的意思是该位置的值被拿走了,可以覆盖新的值)
	
	while (begin < end)
	{
		while (begin < end && a[begin] <= keyindex)//找大的
		{
			++begin;
		}

		a[end] = a[begin];//左边找的比key大的坑填到右边,begin位置形成新的坑

		while (begin < end && a[end] >= keyindex)//找小的
		{
			++end;
		}

		a[begin] = a[end];//右边找的比key小的坑填到左边,end位置形成新的坑
	}

	a[begin] = keyindex;
	return begin;//返回相遇的位置,将原数组分成两段
}

方法三:前后指针法

int PartSort3(int* a, int begin, int end)//单趟快排方法3:前后指针法
{
	int cur = begin;
	int prev=begin-1;
	int keyindex = end;
	while (cur < end)
	{
		if (a[cur] < a[keyindex] && ++prev != cur)//prev和cur相等可以不交换
		{
			Swap(&a[prev], &a[cur]);
		}
		++cur;
	}

	Swap(&a[++prev], &a[keyindex]);
	return prev;
}

快速排序递归实现

//快速排序递归三种实现方法
void QuickSort(int* a, int left, int right)//快排空间复杂度O(LogN)
{
	//assert(a);
	//if (left >= right)//第一种方法
	//{
	//	return;
	//}
	//int div = PartSort3(a, left, right);
    //接收中间值,将整个数组分为三部分 [[left,div-1],div,[div+1,right]]再递归
	//QuickSort(a, left, div - 1);
	//QuickSort(a, div + 1, right);

	//if (left < right)//第二种方法
	//{
	//	int div = PartSort3(a, left, right);
	//	QuickSort(a, left, div - 1);
	//	QuickSort(a, div + 1, right);
	//}

    //针对第3种方法的优化,当小于10个数时不再使用递归排序,改用直接插入排序(效率更高)
	 assert(a);
	if (left >= right)
	{
		return;
	}
	if ((right - left + 1) > 10)
	{
		int div = PartSort3(a, left, right);
		QuickSort(a, left, div - 1);
		QuickSort(a, div + 1, right); 
	}
	else
	{
		InsertSort(a + left, right - left + 1);//小区间优化
	}
}

快速排序迭代法

void QuickSortNonR(int* a, int left, int right)//快速排序非递归实现(效率更高,避免栈溢出)
{
	ST st;
	StackInit(&st);
	StackPush(&st, right);
	StackPush(&st, left);

	while (!StackEmpty(&st))//不为空就继续
	{
		int begin = StackTop(&st);
		StackPop(&st);
		int end = StackTop(&st);
		StackPop(&st);

		int div = PartSort3(a, begin, end);//分割成3部分
		if (div + 1 < end)
		{
			StackPush(&st, end);
			StackPush(&st, div+1);
		}
		if (begin < div - 1 )
		{
			StackPush(&st, div - 1);
			StackPush(&st, begin);
		}
	}
	StackDestroy(&st);
}

归并排序递归法

// 归并排序递归实现
void _MergeSort(int* a, int left, int right, int* tmp)//这是一段临时空间
{
	if (left >= right)// >表示这个区间不存在了,=表示只有一个值
	{
		return; 
	}
	int mid = (left + right) / 2;//将整个数组分割为两个部分[left,mid][mid+1,right]

	_MergeSort(a, left, mid, tmp);//不断递归
	_MergeSort(a, mid+1,right, tmp);

	int begin1 = left, end1 = mid;//原数组分为两个有序数组后归并(类似合并两个有序数组OJ题)
	int begin2 = mid + 1, end2 = right;
	int index = begin1;
	while (begin1 <= end1 && begin2 <= end2)//两个数组都没合并结束才继续
	{
		if (a[begin1] < a[begin2])
		{
			tmp[index++] = a[begin1++];
		}
		else
		{
			tmp[index++] = a[begin2++];
		}
	}

	while (begin1 <= end1)//当其中一个数组完成拷贝后还剩下一个,但不知道是哪个,所以判断一下,只有一个数组的数据能进入index
	{
		tmp[index++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[index++] = a[begin2++];
	}

	for (int i = left; i <= right; ++i)//有序数组已经拷贝到tmp,但tmp是临时开辟的数组,所以要将tmp拷贝到原数组
	{
		a[i] = tmp[i];
	}
}

void MergeSort(int* a, int n)//归并排序递归实现,
{
	assert(a);
	int* tmp = malloc(sizeof(int) * n);//tmp是临时数组
	_MergeSort(a, 0, n - 1, tmp);//_MergeSort的意思是MergeSort的子函数
	free(tmp);
}

归并排序迭代法

//归并排序非递归实现
void MergeArr(int* a, int begin1, int end1, int begin2, int end2, int* tmp)
{
	int left = begin1, right=end2;
	int index = begin1;
	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 i = left; i <= right; i++)//有序数组已经拷贝到tmp,但tmp是临时开辟的数组,所以要将tmp拷贝到原数组
	{
		a[i] = tmp[i];
	}
}


void MergeSortNonR(int* a, int n)
{
	assert(a);
	int* tmp = malloc(sizeof(int) * n);//tmp是临时数组
	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;

			if (begin2 >= n)//合并时只有第一组,没有第二组,就不需要合并
			{
				break;
			}

			if (end2 >= n)//合并时第二组只有部分数据,需要修正end2的边界
			{
				end2 = n - 1;
			}
			MergeArr(a, begin1, end1, begin2, end2, tmp);
		}
		//PrintArray(a, n);
		gap *= 2;
	}
	free(tmp);
}

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值