zl程序教程

您现在的位置是:首页 >  其他

当前栏目

leetcode 167. 两数之和 II - 输入有序数组----双指针篇六,二分篇二

2023-03-14 22:54:11 时间

两数之和二题解集合


暴力法

思路:

枚举出数组中所有两个元素相加的可能结果,与给定目标值进行比较

代码:

class Solution {
public:
	vector<int> twoSum(vector<int>& numbers, int target) 
	{
		for (int i = 0; i < numbers.size(); i++)
		{
			for (int j = i + 1; j < numbers.size(); j++)
			{
				if (numbers[i] + numbers[j] > target) break;
				if (numbers[i] + numbers[j] == target) return vector<int>({ i+1,j+1 });
			}
		 }
		return vector<int>();
	}
};

二分查找法

思路:

双 指 针 i 和 j , 固定指针i指向的元素,然后指针j指向元素的大小就已经确定了,等于target-numbers[i],我们可以用二分查找到区间[i+1,size)去快速查找值为target-numbers[i]的元素。

代码:

class Solution {
public:
	vector<int> twoSum(vector<int>& numbers, int target) 
	{
		//最后一个数不会成为起始的数字
		for (int i = 0; i< numbers.size()-1; i++)
		{
			//得到我们要在剩余区间查找到的目标值
			int targetNum = target - numbers[i];
			int left = i + 1;
			int right = numbers.size() - 1;
			while (left < right)
			{
				int mid = (left + right)/2;
				if (numbers[mid] < targetNum)
					left = mid + 1;
				else if (numbers[mid] > targetNum)
					right = mid-1;
				else
					return vector<int>({ i + 1,mid + 1 });
			}
			if (targetNum == numbers[left]) 
				return vector<int>({ i + 1,left + 1 });
	    }
		return vector<int>();
	}
};

双指针法

图解双指针解法的原理:

在这道题中,我们要寻找的是符合条件的一对下标 (i, j),它们需要满足的约束条件是:

  • 0<=i<n , 0<=j<n
  • i<j

而我们希望从中找到满足 A[i] + A[j] == target 的下标 (i, j)。以 n=8为例,这时候全部的搜索空间是:

由于 i、j 的约束条件的限制,搜索空间是白色的倒三角部分。

可以看到,搜索空间的大小是 O(n^2 )数量级的。如果用暴力解法求解,一次只检查一个单元格,那么时间复杂度一定是 O(n^2)。要想得到 O(n)的解法,我们就需要能够一次排除多个单元格。那么我们来看看,本题的双指针解法是如何削减搜索空间的:

一开始,我们检查右上方单元格 (0, 7),即计算 A[0] + A[7] ,与 target 进行比较。如果不相等的话,则要么大于 target,要么小于 target。

假设此时 A[0] + A[7] 小于 target。这时候,我们应该去找和更大的两个数.

由于 A[7] 已经是最大的数了,其他的数跟 A[0] 相加,和只会更小。也就是说 A[0] + A[6] 、A[0] + A[5]、……、A[0] + A[1] 也都小于 target,这些都是不合要求的解,可以一次排除。这相当于i=0 的情况全部被排除。对应用双指针解法的代码,就是 i++,对应于搜索空间,就是削减了一行的搜索空间,如下图所示。

排除掉了搜索空间中的一行之后,我们再看剩余的搜索空间,仍然是倒三角形状。我们检查右上方的单元格 (1, 7),计算 A[1] + A[7] 与 target 进行比较。

假设此时 A[0] + A[7] 大于 target。这时候,我们应该去找和更小的两个数。由于 A[1] 已经是当前搜索空间最小的数了,其他的数跟 A[7] 相加的话,和只会更大。也就是说 A[1] + A[7] 、A[2] + A[7]、……、A[6] + A[7] 也都大于 target,这些都是不合要求的解,可以一次排除。这相当于 j=0j=0 的情况全部被排除。对应用双指针解法的代码,就是 j++,对应于搜索空间,就是削减了一列的搜索空间,如下图所示。

可以看到,无论 A[i] + A[j] 的结果是大了还是小了,我们都可以排除掉一行或者一列的搜索空间。经过 n 步以后,就能排除所有的搜索空间,检查完所有的可能性。

总结:

  • i指针指向数组起始位置,j指针指向数组末尾位置,显然i指针指向的数字较小,而j指针指向的数字较大
  • 当num[i]+num[j]<target时,j位置指向的元素已经是当前区间最大的元素了,那么接下来我们只能通过让i指向的元素的值变大,即i++,来寻找使得num[i]+num[j]=target。
  • 当num[i]+num[j]>target时,i位置指向的元素已经是当前区间最大的元素了,那么接下来我们只能通过让j指向的元素的值变小,即j–,来寻找使得num[i]+num[j]=target。

代码:

class Solution {
public:
	vector<int> twoSum(vector<int>& numbers, int target) 
	{
		int i = 0, j = numbers.size()-1;
		while (i < j)
		{
			if (numbers[i] + numbers[j] > target)
				j--;
			else if (numbers[i] + numbers[j] < target)
				i++;
			else
				return vector<int>({ i + 1,j + 1 });
		}
		return vector<int>({ i + 1,j + 1 });
	}
};