C语言实现快排(荷兰国旗问题)

问题一:

  • 给定一个数组arr,和一个数num,请把小于等于num的数放在数组的左边,大于num的数放在数组的右边。要求额外空间复杂度0(1),时间复杂度O(N)

解法一:

关于这个问题,左程云(左神)的解法是创建两个标志(可以是指针),第一个标志指向左侧的移动范围用L表示,第二个标志指向数组的元素用i表示。

从左到右依次遍历数组

当数组中的数小于目标数字时,并且交换L位和i位的数字,L和i都左移

当数组中的数不小于目标数字时,i左移,L不移动

当i元素越界后结束判断表示已经分边操作完成

代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <windows.h>
void ExChange(int* arr, int x, int y)
{
	int temp = 0;
	temp = *(arr + x);
	*(arr + x) = *(arr + y);
	*(arr + y) = temp;
}

/*arr: 数组
 *cpm: 需比较的数字
 *size: 数组大小
 */

void Fun(int* arr, int cpm, int size)
{
	int L = 0;
	int i = 0;
	while (i < size)
	{
		if (arr[i] < cpm)                //目标数字小于num
		{
			ExChange(arr, L++, i++);     //交换L位置和i位置的数字,并将他们右移
		}
		else                             //目标数字不小于num
		{
			i++;
		}
	}
}

int main()
{
	int arr[] = { 1, 5, 2, 3, 1, 4, 2, 3 };
	int num = 4;
	Fun(arr, num, sizeof(arr) / sizeof(arr[0]));

	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

解法二:

可以使用左右两个指针依次向中间收缩的方式遍历数组,分别用left和right表示

当left位数字小于num,则left左移

当left位数字不小于num,则判断right位数字是否大于num

                若right大于等于num,则right左移

                若right小于num,则将此时left位和right位交换

当left和right重合表示分边操作完成。

代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <windows.h>

void ExChange(int* x, int* y)
{
	int temp = 0;
	temp = *x;
	*x = *y;
	*y = temp;
}

void SeparateSort(int *arr, int num, int size)
{
	int* left = arr;
	int* right = arr + size - 1;
	while (right > left)
	{
		if (*left < num)                //left位的数字小于num
			left++;                     //left右移
		else                            //left位数字不小于num
		{
			if (*right >= num)          //right位的数字大于等于num
				right--;                //right位左移
			else                        //当left位数字不小于num且right位数字小于num才执行
				ExChange(left,right);
		}
	}
}

int main()
{
	int arr[] = {1,5,2,3,1,4,2,3};
	int sz = sizeof(arr) / sizeof(arr[0]);
	int num = 3;
	SeparateSort(arr, num, sz);

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	system("pause");
	return 0;

}

问题二:荷兰国旗

  •  给定一个数组arr,和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的右边。要求额外空间复杂度0(1),时间复杂度0(N)。

解题思路:

左程云(左神)的解法是创建三个标志,其中两个标志分别表示左边界和右边界(left和right),一个标志表示遍历数组的位置标志(i)

左边界内存放比num小的元素,右边界内存放比num大的元素,中间存放未遍历和判断的元素和与num相等的元素。

 以 i 为指针遍历数组(使用下标也可以)

当 i 位置元素小于num时,将 left 位和 i 位置的元素相互交换,并且 left 位和 i 位都向右移一位

当 i 位置元素等于num时,将i++,即向右移一位

当 i 位置元素大于num时,将 right 位和 i 位置的元素相互交换,并且只将right 位向左移一位

(right与i位置交换 i 不移位,在下一次进入时判断的就是原本right位置的,被交换到 i 位的元素)

在 i 移动越过 right时表示排列完毕

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <windows.h>

//荷兰国旗问题
void ExChange(int* x, int* y)
{
	int temp = 0;
	temp = *x;
	*x = *y;
	*y = temp;
}

void SeparateSort(int *arr, int num, int size)
{
	int* left = arr;
	int* right = arr + size - 1;
	int* i = arr;
	while (i <= right)
	{
		if (*i < num)                //小于时交换left和i
		{
			ExChange(left++, i++);
		}
		else if (*i == num)          //等于时跳过当前元素
			i++;
		else                         //大于num时与right位交换
		{
			ExChange(right--, i);
		}
	}
}

int main()
{
	int arr[] = { 1, 5, 2, 3, 1, 4, 2, 3, 2,3,1,2,4,5,3,2 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int num = 3;
	SeparateSort(arr, num, sz);

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	system("pause");
	return 0;

}

问题三:实现快排

  • 试着写出一个排序算法,使时间复杂度小于O(N^2),额外空间复杂度O(logN)。

基于现在所学的知识,我所知的时间复杂度小于O(N^2)的算法只有归并排序,但是归并排序额外空间复杂度为O(1),不符合要求,所以这里不选用这种做法。将荷兰国旗问题进行深度修改,即可找到新的题解。

解题思路:

排序思路与上个问题基本相同,主要有几个不同点:

1、需要创建一个能容纳两个元素的堆空间,作为暂时存放该次操作中左边界终点和右边界起点。

2、进行递归操作,分别对左侧和右侧进行分边操作,直到 right < left 为止

3、需要随机数来随机取出区间内的一个数来作为num分界点的值,将它和边界上最后一个数字交换,以便在子函数中取用。这里使用时间戳来实现。

为什么一定要随机数呢?因为当该方法固定取出尾部数字作为分界点值,那么当数组为递增的有序数组时,时间复杂度为O(N^2),所以取用随机值可以避免这种情况。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <time.h>
#include <malloc.h>
#include <memory.h> 

//快排3.0
void ExChange(int* arr, int x, int y)
{
	int temp = 0;
	temp = *(arr + x);
	*(arr + x) = *(arr + y);
	*(arr + y) = temp;
}

int *Fun(int* arr, int left, int right)
{
	int* pf = (int*)malloc(sizeof(int) * 2);    //创建存储空间分别存储
	memset(pf, 0, sizeof(int) * 2);
	int cpm = arr[right];                //将本次排序的终点元素取出作为比较num
	int i = left;
	while (i <= right)
	{
		if (arr[i] < cpm)
		{
			ExChange(arr, left++, i++);
		}
		else if (arr[i] == cpm)
			i++;
		else
		{
			ExChange(arr, right--, i);
		}
	}

	*(pf + 1) = i--;        //存右边元素起点
	while (arr[i] == cpm)
		i--;
	*pf = i;                //存左边元素终点
	return pf;
}
void SeparateSort(int *arr, int left, int right)
{
	int *pf = NULL;
	if (left >= right)
	{
		return;
	}
	ExChange(arr, left + rand() % (right - left), right);	//随机取数与最后以一个交换
	
	pf = Fun(arr, left, right);								//排序
	SeparateSort(arr, left, *pf);							//左排序
	SeparateSort(arr, *(pf + 1), right);					//右排序

	free(pf);
	pf = NULL;
}

int main()
{
	srand((unsigned)time(NULL));

	int arr[] = { 1, 5, 2, 3, 1, 4, 2, 3, 2, 3, 1, 2, 4, 5, 3, 2 };

	SeparateSort(arr, 0, sizeof(arr)/sizeof(arr[0]) - 1);

	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	system("pause");
	return 0;

}

 注:参考链接:一周刷爆LeetCode,算法大神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教程,直击BTAJ等一线大厂必问算法面试题真题详解_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV13g41157hK?p=3&spm_id_from=pageDriver

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KamikazePilot

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值