【21天考研算法带刷leetcode-剑指21】快速排序 双指针大法的最后应用(5/21)

双指针应用十分广泛后面就不再详细说了,数组很多操作都可以通过双指针来实现,尤其是在排序算法中,包括快排、归并等,而很多题的暴力解法用一般的排序容易时间复杂度过高,可以先用快排、堆排转化为有序数组,例如后面提到的经典-----三数之和。这里先通过一道小题来学习一趟快速排序的思想。

一、奇偶分界 力扣

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。

例子【    int nums[6] = { 2,1,-1,4,5,-3 }】

这里通过左右两个向中间遍历的指针,从high来走,也就是从数组右端开始走,遇到奇数就赋到前面,此时的nums[low]已经被保存了下来,从low走,遇到的第一个不是奇数的就会赋值到原来的high,以次类推

直到low=high 相遇的地方要赋枢轴的值,但是这里我们不用考虑枢轴是奇数还是偶数的问题,因为赋值到low的位置就正好是分界线,以这个题目为例子,2放到分界线上,偶数就是从2开始向后了。如果是1当第一趟的枢轴,那么偶数的范围就是low+1  ~ numsSize-1

一趟快速排序可以确定枢轴的位置,也可以是一种条件,这里就是奇偶,当然也可以时正负数等条件,如果这里进行展开的话,可以通过swtich case延申到3类问题,见王道书快排课后题。


int* exchange(int* nums, int numsSize, int* returnSize){
    int low=0; int high= numsSize-1;
    *(returnSize)=numsSize;
    if(high==-1) return nums;
    int pivot=nums[low];
    while(low<high){
        while(low<high&&nums[high]%2==0){//偶数一直走 遇到奇数停下来进行赋值
            --high;
        }
         nums[low]=nums[high];
        while(low<high&&nums[low]%2!=0){//奇数一直走 遇到偶数停下来进行赋值
            ++low;
        }
         nums[high]=nums[low];
    }
    nums[low]=pivot;
    return nums;
}

二、三数之和的暴力实现

力扣,快排在整个数组中是递归的过程,因为确定完一个枢轴还要去左右子区间再划分,执行的趟数跟数组的初始有关,数组有序时执行次数最多为n次,需要空间复杂度为n的递归工作栈。

int Partition(int* arr, int low, int high)
{
	//一次划分
	int tmp = arr[low];//基准
	while (low < high)
	{
		//(1)从后往前找比基准小的数字
		while (low<high && arr[high]>tmp)
		{
			high--;
		}
		if (low < high)
		{
			arr[low] = arr[high];
		}
		//(2)从前往后找比基准大的数字
		while (low < high && arr[low] <= tmp)
		{
			low++;
		}
		if (low < high)
		{
			arr[high] = arr[low];
		}
	}
	arr[low] = tmp;
	return low;
}
void Quick(int* arr, int low, int high)
{
	int par = Partition(arr, low, high);
	if (low < par - 1)//左边数据超过一个
	{
		Quick(arr, low, par - 1);
	}
	if (par + 1 < high)//右边数据超过一个
	{
		Quick(arr, par + 1, high);
	}
}
void QuickSort(int* arr, int len)
{
	Quick(arr, 0, len - 1);//参数一致
}

int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
	//开辟ans数组空间
	int** ans = (int**)malloc(sizeof(int*) * 18000);
	int ansTop = 0;
	//若传入nums数组大小小于3,则需要返回数组大小为0
	if (numsSize < 3) {
		*returnSize = 0;
		return ans;
	}
	//对nums数组进行排序
	QuickSort(nums, numsSize);


	int i;
	//用for循环遍历数组,结束条件为i < numsSize - 2(因为要预留左右指针的位置)
	for (i = 0; i < numsSize - 2; i++) {
		//若当前i指向元素>0,则代表left和right以及i的和大于0。直接break
		if (nums[i] > 0)
			break;
		//去重:i > 0 && nums[i] == nums[i-1]
		if (i > 0 && nums[i] == nums[i - 1])
			continue;
		//定义左指针和右指针
		int left = i + 1;
		int right = numsSize - 1;
		//当右指针比左指针大时进行循环
		while (right > left) {
			//求出三数之和
			int sum = nums[right] + nums[left] + nums[i];
			//若和小于0,则左指针+1(因为左指针右边的数比当前所指元素大)
			if (sum < 0)
				left++;
			//若和大于0,则将右指针-1
			else if (sum > 0)
				right--;
			//若和等于0
			else {
				//开辟一个大小为3的数组空间,存入nums[i], nums[left]和nums[right]
				int* arr = (int*)malloc(sizeof(int) * 3);
				arr[0] = nums[i];
				arr[1] = nums[left];
				arr[2] = nums[right];
				//将开辟数组存入ans中
				ans[ansTop++] = arr;
				//去重
				while (right > left && nums[right] == nums[right - 1])
					right--;
				while (left < right && nums[left] == nums[left + 1])
					left++;
				//更新左右指针
				left++;
				right--;
			}
		}
	}

	//设定返回的数组大小
	*returnSize = ansTop;
	*returnColumnSizes = (int*)malloc(sizeof(int) * ansTop);
	int z;
	for (z = 0; z < ansTop; z++) {
		(*returnColumnSizes)[z] = 3;
	}
	return ans;
}

版权声明:本文为CSDN博主「CSDN官方博客」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

积极向上的11

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

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

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

打赏作者

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

抵扣说明:

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

余额充值