2 条题解

  • 0
    @ 2025-12-4 23:49:30

    你好!我是你的OI教练。

    这道题虽然标题提到了“栈和队列”,但实际上这是一个美丽的误会。这道题最经典、最高效的解法并不需要栈或队列,而是用一种非常巧妙的双指针技巧。不过,我也会解释为什么人们可能会联想到栈和队列,以及如何用它们“强行”解决这个问题。


    第一部分:最优解法 —— 双指针 (Two Pointers)

    这才是这道题的正解,也是面试和竞赛中的标准解法。它利用了数组**“升序排序”**这个关键信息。

    a. 思路提示 (启发式教学)

    拿出你的草稿纸,画出这个数组:1 2 4 7 11 15,目标和是 15

    1. 设置起点:我们设置两个“指针”(其实就是数组下标),一个指向数组的开头left),一个指向结尾right)。

      1   2   4   7   11   15
      ^                   ^
      left                right
      
    2. 第一次尝试:计算 arr[left] + arr[right],也就是 1 + 15 = 16

      • 思考16 比我们的目标 15 了。我们应该怎么调整指针来让和变小一点?
      • left 指向的是最小的数,right 指向的是最大的数。
      • 如果我把 left 往右移,和只会变得更大。
      • 所以,唯一的办法是把 right 向左移动,换一个稍小一点的数。
    3. 第二次尝试left 不动,right 左移一位。

      1   2   4   7   11   15
      ^               ^
      left            right
      

      计算 arr[left] + arr[right],也就是 1 + 11 = 12

      • 思考12 比我们的目标 15 了。我们应该怎么调整指针来让和变大一点?
      • 如果我把 right 再往左移,和只会更小。
      • 所以,唯一的办法是把 left 向右移动,换一个稍大一点的数。
    4. 第三次尝试left 右移一位,right 不动。

      1   2   4   7   11   15
          ^           ^
          left        right
      

      计算 arr[left] + arr[right],也就是 2 + 11 = 13

      • 思考13 还是比 15 。继续把 left 向右移动
    5. 第四次尝试left 右移一位,right 不动。

      1   2   4   7   11   15
              ^       ^
              left    right
      

      计算 arr[left] + arr[right],也就是 4 + 11 = 15

      • 找到了! 和正好等于目标 15。输出 411,程序结束。

    b. 算法总结

    1. 初始化 left = 0, right = n - 1
    2. left < right 时,循环: a. 计算 sum = arr[left] + arr[right]。 b. 如果 sum == target,恭喜你找到了,输出 arr[left]arr[right],然后 break。 c. 如果 sum < target,说明和太小了,需要增大,所以 left++。 d. 如果 sum > target,说明和太大了,需要减小,所以 right--

    时间复杂度leftright 指针都只从头到尾(或从尾到头)扫了一遍,所以复杂度是 O(N)O(N),非常高效。


    第二部分:为什么会提到“栈和队列”?

    这可能是出题人想考察你对数据结构基本特性的理解,或者这是一个思维陷阱。

    • 栈 (Stack):特性是后进先出 (LIFO)
    • 队列 (Queue):特性是先进先出 (FIFO)

    你看,left 指针从左到右移动,像不像一个队列的出队操作?right 指针从右到左移动,像不像一个特殊的(把数组反过来看)的出栈操作?

    双指针算法的移动方式,确实和这两种数据结构的“访问头部/尾部”的行为有相似之处。我们可以用它们来模拟双指针,但这会把简单问题复杂化。

    a. 用栈和队列“强行”求解 (不推荐)

    这是一种为了用而用的解法,不实用,但可以帮你理解数据结构。

    1. 把数组中的所有元素都压入一个 s 和一个队列 q
    2. s.top() 就是最大的元素(对应 right 指针)。
    3. q.front() 就是最小的元素(对应 left 指针)。
    4. 循环: a. sum = s.top() + q.front()。 b. 如果 sum == target,找到答案。 c. 如果 sum < targetq.pop() (相当于 left++)。 d. 如果 sum > targets.pop() (相当于 right--)。

    缺点

    • 需要 O(N)O(N) 的额外空间来存储栈和队列。
    • 代码比双指针更长,也更慢(因为数据结构的维护有开销)。
    • 完全没有必要。

    第三部分:代码实现

    这里只给出最高效、最正确的双指针解法。

    #include <iostream>
    #include <vector>
    #include <sstream> // 用于处理单行不定长输入
    
    using namespace std;
    
    // 解决问题的核心函数
    void find_two_sum(const vector<int>& nums, int target) {
        if (nums.size() < 2) {
            // 数组元素少于2个,不可能找到
            return;
        }
    
        int left = 0;
        int right = nums.size() - 1;
    
        while (left < right) {
            int current_sum = nums[left] + nums[right];
    
            if (current_sum == target) {
                // 找到了,输出结果
                cout << nums[left] << " " << nums[right] << endl;
                return; // 找到一对即可退出
            } else if (current_sum < target) {
                // 和太小,移动左指针以增大和
                left++;
            } else { // current_sum > target
                // 和太大,移动右指针以减小和
                right--;
            }
        }
    
        // 如果循环结束还没找到,可以输出提示(题目没要求)
        // cout << "No pair found." << endl;
    }
    
    int main() {
        // 优化IO
        ios::sync_with_stdio(false);
        cin.tie(0);
    
        // --- 处理输入 ---
        // 读取第一行不定长的数组 (这是本题的第一个难点,stringstream和getline配合可以方便地处理)
        string line;
        getline(cin, line);
        
        stringstream ss(line);
        vector<int> nums;
        int num;
        while (ss >> num) {
            nums.push_back(num);
        }
    
        // 读取第二行的目标值
        int target;
        cin >> target;
    
        // --- 调用算法 ---
        find_two_sum(nums, target);
    
        return 0;
    }
    

    教练总结: 这道题是双指针算法的完美应用范例。一定要牢记它的使用前提——数组有序。它通过从两端向中间“夹逼”的方式,在 O(N)O(N) 时间内解决问题。虽然可以用栈和队列模拟,但这并非问题的本质解法。

    • 0
      @ 2025-9-10 0:00:09

      C++ :

      #include<cstdio>
      int a[10000],d;
      int main()
      {
      	int i,j,m,s,flag=1;
      	char c;
      	d=0;
      	while(scanf("%d%c",&m,&c))
      	{
      		a[d++]=m;
      		if(c=='\n'||c=='\r')
      			break;
      	}
      	scanf("%d",&s);
      	i=0;
      	while(i<d&&flag)
      	{
      		for(j=i+1;;j++)
      		{
      			if(a[i]+a[j]==s)
      			{
      				printf("%d %d\n",a[i],a[j]);
      				flag=0;
      				break;
      			}
      			if(a[i]+a[j]>s)
      				break;
      		}
      		i++;
      	}
      	return 0;
      }
      
      • 1

      在数组中查找两个数之和等于输入的另一个数(栈和队列)

      信息

      ID
      2427
      时间
      1000ms
      内存
      128MiB
      难度
      (无)
      标签
      (无)
      递交数
      0
      已通过
      0
      上传者