双指针应用十分广泛后面就不再详细说了,数组很多操作都可以通过双指针来实现,尤其是在排序算法中,包括快排、归并等,而很多题的暴力解法用一般的排序容易时间复杂度过高,可以先用快排、堆排转化为有序数组,例如后面提到的经典-----三数之和。这里先通过一道小题来学习一趟快速排序的思想。
一、奇偶分界 力扣
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。
例子【 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版权协议,转载请附上原文出处链接及本声明。