算法经典:对撞指针(此指针非彼指针)

双指针分为三种:

(1)普通指针:多是两个指着往同一个方向移动(无快慢之分)

(2)对撞指针:两个指针,一个位于头,一个位于尾,它们向中间移动(主要应用于有序数组)

(3)快慢指针:两个指针,起始点位于开头,当A条件成立时,A指针++;

同理:当B条件成立时,B指针++(其中A,B的条件束缚不同因此产生快慢之分)

我们这里来介绍第二种(2)对撞指针:

第一个例子:找相应数字求和==一个特值:

题目:给定一个有序数组(数组是递增的),如数组arr = {1,4,5,7,9};找两个数之和为12,找到一组即可停止。

方法一:暴力遍历:两层for循环查找即可,但时间复杂度就要为O(N^2)了

void find(int *a,int n,int temp)
{
	for(int i=0;i<n;i++)
	{
		for(int j=i;j<n;j++)
		{
			if(a[i]+a[j]==temp)
			{
				printf("%d+%d=%d\n",a[i],a[j],temp);
			}
		}
	}
}

你发现了吗?:因为是有序的,所以顺序查找会的值会逐渐增加,但效率太低了

这时我们就可以采用方法二:对撞指针来更高效的完成这个任务

对撞指针的思路类似于二分查找:将i指针放在首,将j指针放在尾

则会有以下三种情况:

(1):a[i]+a[j]<temp,我们则可知这两个指针对应的数组元素的和小于特值,但此时a[j]已经是数组里最大的元素了,所以我们需要动i指针,使它++,对应下个更大的a[i]元素

(2):a[i]+a[j]>temp,同理,a[i]已经是数组里最小的元素了,你们两的和还要比temp大,就只能让j--,从右向左寻找依次寻找更小的数,所以j--,对应下个更小的a[j]元素

(3)a[i]+a[j]==temp,不多说了,满足条件,跳出循环即可

主要思路有了:但由于不太可能判断一次就出结果,所以我们要在外面加一个循环,循环跳出的条件是什么呢?:很简单:当左指针>右指针时(注意是下标,不是下标对应的数组元素),说明都搜了个遍了,都没有结果,那就证明数组里真的没有满足的这两个元素

void find(int *a,int n,int temp,int i,int j)
{
	//i为首指针,j为尾指针 
	while(i<j)
	{
		if(a[i]+a[j]<temp)
		{
			i++;
		}
		else if(a[i]+a[j]>temp)
		{
			j--;
		}
		else
		{
			printf("%d+%d=%d\n",a[i],a[j],temp);
			break;
		}
	}
}

时间复杂度就被我们优化到了O(N)  ^-^

第二个例子:判断回文串:

输入字符串,判断其是否为回文串    若是:输出Y  否则:输出N

回文串:回文串就是从左边读和从右边读的结果是一样的,例如:aba qweewq 121 111

思路解析:初始化flag=1,将i指针指向首,将j指针指向尾,如果不是回文串我们需要flag=0做标记

(1):如果a[i]==a[j],则i指针++,j指针--

(2):如果a[i]!=a[j],flag=0,break跳出循环,这个字符串不是回文串

循环条件为:左指针i<右指针j

(1)if(flag==1):它没有进入过a[i]!=a[j] :则可知该字符串为回文串

(2)else: 它进入了a[i]!=a[j]:则可知该字符串不是回文串

代码呈上:

#include<stdio.h>
#include<string.h>
int main()
{
	char str[255];
	while(gets(str))
	{
		int len=strlen(str);
		int i=0;
		int j=len-1;
		int flag=0;
		while(i<j)
		{
			if(str[i]==str[j])
			{
				i++;
				j--;
			}
			else
			{
				flag=1;
				break;
			}
		}
		if(flag==0)
		{
			printf("Y\n");
		}
		else
		{
			printf("N\n");
		}
	}
	return 0;
}

第三个例子:反转字符串

 题目本身不难,唯一的难点是它的要求:不能再去开一个数组

输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]

void Swap(int *left,int *right)
{
	char temp=*right;
	*right=*left;
	*left=temp;
}
 
void ReverseString(char* s, int size){
    char* left = s;//指向起始地址:左指针
    char* right = s + size - 1;//指向末位地址:右指针
    while(left < right)//左指针小于右指针的下标
    {
        Swap(&left,&right);
        left++;
        right--;
    }

 第四个例子:选择排序:

本质:应用双指针,begin指针指向首,end指针指向尾

使用for循环在begin——end范围内寻找并记录最大的数下标:maxi,和最小数字的下标:mini

 这里我们想将数字进行升序排列,只需将begin——end范围内的最小数下标与begin交换即可

即:Swap(&a[begin],&a[mini])  同理将最大数下标与end交换: Swap(&a[end],&a[maxi])

之后让begin++,end--即可;循环条件还是左指针小于右指针

#include<stdio.h>
 
void Swap(int* p1, int* p2)
{
	int tmp = *p2;
	*p2 = *p1;
	*p1 = tmp;
}
void SelectSort(int* a, int n)
{
	int begin = 0;
	int end = n - 1;
	while (begin < end)
	{
		int mini = begin;
		int maxi = begin;
		for (int i = begin; i <= end; i++)
		{
			//寻找最小的数字的下标
			if (a[i] < a[mini])
			{
				mini = i;
			}
			//寻找最大数字的下标
			if (a[i] > a[maxi])
			{
				maxi = i;
			}
		}
		Swap(&a[begin],&a[mini]);//最小的放到第一位
		if (begin == maxi)
		{
			maxi = mini;
		}
		Swap(&a[end],&a[maxi]);//最大的放到最后一位
		++begin;
		--end;
	}
}
void Printarray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
}
void TestSelectSort()
{
	int a[] = { 3,5,4,2,1 };
	int n = sizeof(a) / sizeof(a[0]);
	SelectSort(a, n);
	Printarray(a, n);
}
int main()
{
	TestSelectSort();
	return 0;
}

请注意:为什么我又加了个条件判断(如果:begin==maxi):即如果最大数的下标位于第一个的话

我们将最小值的下标赋值给最大值的下标?为什么?

因为我们是先进行的最小值与begin的交换,交换后,如果很不巧,begin对应的数字就是最大数,那么原来maxi的值会被mini对应元素的值覆盖。

举个例子:a[]={5,3,1,2,4};

如果不加这个判断条件:

第一次mini对应值与begin对应值交换的结果:a[]={1,3,5,2,4};

第一次maxi对应值与end对应值的交换的结果:a[]={4,3,5,2,1};

产生错误的结果是因为maxi被第一次mini和begin交换时被破坏了

正确的情况是:

mini与begin  的对应值:a[]={1,3,5,2,4};

maxi与end    的对应值:a[]={1,3,4,2,5}:


---------------------
作者:lihua777
来源:CSDN
原文:https://blog.csdn.net/lihua777/article/details/122243651
版权声明:本文为作者原创文章,转载请附上博文链接!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值