【交换排序】C语言实现交换排序---冒泡排序和快速排序

本文详细介绍了冒泡排序的基本思想和步骤,以及快速排序的hoare版本、挖坑法、前后指针版本和非递归实现。同时讨论了快速排序的优化方法,尤其针对最大值在最左边的情况。
摘要由CSDN通过智能技术生成

冒泡排序

基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
以 int arr[] = { 10, 1, 2, 7, 9, 3, 4, 5, 8, 6}; 举例(升序)
步骤:

  1. 将 arr[0] 与arr[1]比较,因为arr[1]>arr[1],进行换值,arr[0]=1,arr[1]=10
  2. 将 arr[1] 与arr[2]比较,因为arr[1]>arr[2],进行换值,arr[1]=2,arr[2]=10
  3. 将 arr[2] 与arr[3]比较,因为arr[3]>arr[2],进行换值,arr[2]=7,arr[3]=10
  4. 进行上述换值9次,arr[9]=10, 即可完成全部排序
int main()
{
	//升序
	int arr[] = { 10, 1, 2, 7, 9, 3, 4, 5, 8, 6 };
	//需要循环 sizeof(arr) / sizeof(int) - 1 次才能完成任何复杂的排序
	for (int i = 0; i < sizeof(arr) / sizeof(int) - 1; i++)
	{
	    //遍历
		for (int j = 0; j < sizeof(arr) / sizeof(int) - 1 - i; j++)
		{
		   //把相邻的数都进行比较
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}


	return 0;
}

快速排序

快速排序是是hoare1962年年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元数序列中某元数作为基准值按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

//假设一个函数  按升序对arr数组的区间(left,right)进行排序
QuickSort_2(int *arr,int left,int right)
{
...
}
void QuickSort_1(int *arr,int left,int right)
{
     if (left >= right)
	{
     	return;
	}
	//按照基准值对arr数组的区间进行划分
	int key = QuickSort_2(arr,leght,right)
	//划分后区间成为[ left,key-1]和[key+1,right]两部分
	//递归
	QuickSort_1(arr,left,key-1)
	//递归
	QuickSort_1(arr,key+1, right)
}

上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像,同学们在写递归框架时可想想二叉树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。
将区间按照基准值划分为左右两半部分的常见方式有:

hoare版本

L找比arr[key]大的值,R找比arr[key]小的值
在这里插入图片描述
步骤(升序):

  1. 先让R先走,走到arr[R]=5
  2. 再让L走,走到arr[L]=7
  3. 交换arr[R] 和 arr[L]的值
  4. 重复上述步骤,直到 R=L
  5. 最后,key=R(key=L),进行递归

代码演示:

//进行排序的函数
int PartQuickSort_2(int* arr, int left, int right)
{
	//定义基准值
	int key = left;
	while(left < right)
	{
		//右取小   没有=会死循环   没 left<right会在此while中left>right
		while(arr[right] >= arr[key]&&left<right)
		{
			--right;
		}
		//左取大
		while(arr[left] <= arr[key]&&left<right)
		{
			++left;
		}
		//交换left 和 right值
		int tmp = arr[left];
		arr[left] = arr[right];
		arr[right] = tmp;
	}
	//交换key 和 left(right)值
	int tmp = arr[key];
	arr[key] = arr[right];
	arr[right] = tmp;
	return right;
}

//进行递归的函数
void PartQuickSort_1(int* arr, int left, int right)
{
	//终止条件
	if (left >= right)
	{
		return;
	}
	//先找一次 key
	int key = PartQuickSort_2( arr, left, right);
    //整个数的集合为 [left,k-1] key [k+1, right]
	//[left,k-1] 部分进行排序
	PartQuickSort_1(arr, left, key - 1);
	//[k+1, right] 部分进行排序
	PartQuickSort_1(arr, key + 1, right);

}

int main()
{
	int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
	PartQuickSort_1(arr, 0, sizeof(arr) / sizeof(int)-1);
	return 0;
}

挖坑法

L找比 key 大的值,R找比 key 小的值
在这里插入图片描述
步骤(升序):

  1. key = arr[L], arr[0]为坑
  2. 先让R先走,走到arr[R]=5,让 arr[L]=5, arr[R]为坑
  3. 再让L走,走到arr[L]=7,让 arr[R]=7,arr[L]为坑
  4. 重复上述步骤,直到 R=L
  5. 最后,key=R(key=L),进行递归

代码演示:

//进行排序的函数
int PartQuickSort_2(int* arr, int left, int right)
{
	//定义基准值
	int key = arr[left];
	int hole = left;
	while (left < right)
	{
		//右取小   没有=会死循环   没 left<right会在此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[hole] = key;
	
	return hole;
}

//递归函数
void PartQuickSort_1(int* arr, int left, int right)
{
	//终止条件
	if (left >= right)
	{
		return;
	}
	//先找一次 key
	int key = PartQuickSort_2(arr, left, right);
	//整个数的集合为 [left,k-1] key [k+1, right]
	// [left,k-1] 部分进行排序
	PartQuickSort_1(arr, left, key - 1);
	//  [k+1, right] 部分进行排序
	PartQuickSort_1(arr, key + 1, right);

}

int main()
{
	int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
	PartQuickSort_1(arr, 0, sizeof(arr) / sizeof(int) - 1);
	return 0;
}

前后指针版本

在这里插入图片描述
代码演示:

//进行排序的函数
int PartQuickSort_2(int* arr, int left, int right)
{
	//定义基准值
	int key = left;
	int pre = left+1;
	int cur = left;
	while (pre <= right)
	{
		//pre 取小
		if(arr[pre] < arr[key])
		{
			cur++;
			int tmp = arr[pre];
			arr[pre] = arr[cur];
			arr[cur] = tmp;
		}
		++pre;
	}
	//交换key 和 cur值
	int tmp = arr[key];
	arr[key] = arr[cur];
	arr[cur] = tmp;
	return cur;
}

//递归函数
void PartQuickSort_1(int* arr, int left, int right)
{
	//终止条件
	if (left >= right)
	{
		return;
	}
	//先找一次 key
	int cur = PartQuickSort_2(arr, left, right);
	//整个数的集合为 [left,cur-1] cur [cur+1, right]
	//[left,cur-1] 部分进行排序
	PartQuickSort_1(arr, left, cur - 1);
	//[cur+1, right] 部分进行排序
	PartQuickSort_1(arr, cur + 1, right);
}

int main()
{
	int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
	PartQuickSort_1(arr, 0, sizeof(arr) / sizeof(int) - 1);
	return 0;
}

非递归版本

基本思想和递归思想一样。
这里需要用C语言实现栈,不会用C语言实现的可以参考 C语言实现栈

//排序函数
int PartQuickSort_2(int* arr, int left, int right)
{
	//定义基准值
	int key = left;
	int pre = left + 1;
	int cur = left;
	while (pre <= right)
	{
		//pre 取小
		if (arr[pre] < arr[key])
		{
			cur++;
			int tmp = arr[pre];
			arr[pre] = arr[cur];
			arr[cur] = tmp;
		}
		++pre;
	}
	//交换key 和 cur值
	int tmp = arr[key];
	arr[key] = arr[cur];
	arr[cur] = tmp;
	return cur;
}

void QuickSortNoR(int* arr, int left, int right,  int n)
{
	//栈的初始化
	SL st;
	SLInit(&st);
	//入栈,先进后出
	SLPush(&st, left);
	SLPush(&st, right);
	
	while (!SLEmpty(&st))
	{
		//取左值
		int left = SLTop(&st);
		SLPop(&st);
		//取右值
		int right = SLTop(&st);
		SLPop(&st);
		//取key,分区间
		int key = PartQuickSort_2(arr, left, right);
		// [left, key-1] key [key+1,right]
		//进栈
		if (key + 1 < right)
		{
			SLPush(&st, right);
			SLPush(&st, key + 1);
		}
		if (left < key - 1)
		{
			SLPush(&st, key - 1);
			SLPush(&st, left);
		}

	}
	//销栈
	SLDestroy(&st);
}

快速排序优化

可以想象一下,( 排升序 )当最大值在最左边时是最复杂的情况,排序时间变长。我们不知道大中小各各的坐标,这时可以取一个相对的中间值放在最左边。

代码演示(以hoare版本为例进行改进):

void Swap(int* arr1, int *arr2)
{
	int tmp = *arr1;
	*arr1 = *arr2;
	*arr2 = tmp;
}

void FindMid(int* arr, int left, int right)
{
	//假设arr[mid]相对中间值
	int mid = (left + right) / 2;
	//找出正确的相对中间值
	if (arr[left] < arr[mid])
	{
		if (arr[mid] < arr[right])
		{
			return;
		}
		else if (arr[right] < arr[left])
		{
			Swap(&arr[left], &arr[mid]);
			return;
		}
		else
		{
			Swap(&arr[right], &arr[mid]);
			return;
		}
	}
	else //arr[mid]<=arr[left]
	{
		if (arr[right] > arr[left])
		{
			Swap(&arr[mid], &arr[left]);
			return;
		}
		else if (arr[right] < arr[mid])
		{
			return;
		}
		else
		{
			Swap(&arr[right], &arr[mid]);
		}
	}
}

int PartQuickSort_2(int* arr, int left, int right)
{
	//定义基准值
	int key = left;
	while(left < right)
	{
		//右取小   没有=会死循环   没 left<right会在此while中left>right
		while(arr[right] >= arr[key]&&left<right)
		{
			--right;
		}
		//左取大
		while(arr[left] <= arr[key]&&left<right)
		{
			++left;
		}
		//交换left 和 right值
		int tmp = arr[left];
		arr[left] = arr[right];
		arr[right] = tmp;
	}
	//交换key 和 left(right)值
	int tmp = arr[key];
	arr[key] = arr[right];
	arr[right] = tmp;
	return right;
}

//进行递归的函数
void PartQuickSort_1(int* arr, int left, int right)
{
    FindMid( arr,  left,  right);
	//终止条件
	if (left >= right)
	{
		return;
	}
	//先找一次 key
	int key = PartQuickSort_2( arr, left, right);
    //整个数的集合为 [left,k-1] key [k+1, right]
	//[left,k-1] 部分进行排序
	PartQuickSort_1(arr, left, key - 1);
	//[k+1, right] 部分进行排序
	PartQuickSort_1(arr, key + 1, right);

}

int main()
{
	int arr[] = { 6, 1, 2, 7, 9, 3, 4, 5, 10, 8 };
	PartQuickSort_1(arr, 0, sizeof(arr) / sizeof(int)-1);
	return 0;
}
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值