C++ 优先算法 —— 四数之和(双指针)

news/2024/11/14 0:31:10 标签: c++, 开发语言, 算法, leetcode, 1024程序员节, c语言

目录

题目:四数之和

1. 题目解析

2. 算法原理

Ⅰ. 暴力枚举

Ⅱ. 双指针算法

不漏的处理:

去重处理:

3. 代码实现

Ⅰ. 暴力枚举

Ⅱ. 双指针算法


题目:四数之和

1. 题目解析

题目截图:

这道题与三数之和(三数之和的解答)及两数之和(两数之和的解答)是很相似的,这道题目要求,也类似于三数之和,它这里的顺序上:

  • 每个四元组的前后顺序可以任意
  • 每个四元组中的数据顺序可以任意

这里target可以不同(target可以随意)。

 

 

2. 算法原理

这里也是同样有两种解法:

Ⅰ. 暴力枚举

这里方法也是:排序+暴力枚举(从左往右)+去重

类比前面的三数之和:

//伪代码演示
for (int i = 0; i < n; i++) // 固定一个数a

    for (int j = i + 1; j < n; j++)     //依次固定枚举第二个数
    
        for (int k = j + 1; k < n; k++)    //枚举第三个数
        
            for(int z = k + 1; z < n; z++)    //枚举第四个数
            
                查找符合的四元组,去重...
  

 这里套了四层for循环,时间复杂度就是O(N⁴)。(会出现超时问题)

Ⅱ. 双指针算法

这里同三数之和也是:排序+双指针+去重

这里就相当于划分成,固定第一个数,剩下的就是利用三数之和思想解决,三数之和中,也是要固定一个数(这里也就是第二个数),接着利用两数之和解决,也就是在剩下的区间内用双指针算法

总结一下解决方法: 

  1. 依次固定一个数a(从左往右固定a,固定枚举完这一个后,再换下一个)。
  2. 在a的后面的区间内,利用“三数之和”的思想,找到三个数,使这三个数的和等于target-a即可。

这里三数之和:

  1. 依次固定一个数b。
  2. 在b后面的区间内,利用双指针,找到两个数,使这两个数的和等于target-a-b即可。

通过上图可以看到需要套两次for循环加一个while,时间复杂度为O(N²)。 

这里也是需要处理同三数之和的细节问题:

不漏的处理:

也就是确保所有符合条件的情况不漏掉,这里也是找到一种结果之后,不要停(两个指针不停),缩小区间(两指针都向内移动一步),继续寻找符合的情况。

 去重处理:

这里需要考虑三个去重情况:

 

//伪代码演示
for (i = 0; i < n; i++)  //固定数a		
		//利用三数之和
    for (j = i + 1; j < n; j++)  //固定数b
		//双指针
		while (left < right) 
		//处理数据,并去重		

        //这里去重同样注意越界问题的判断:
        left < right
        j < n
        i < n

接下来,实现代码。

3. 代码实现

题目链接:四数之和

Ⅰ. 暴力枚举

//这里时间复杂度:O(N⁴)
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        // 1.排序
        sort(nums.begin(), nums.end());

        vector<vector<int>> ret; // 用来存储所有的四元组
        // 2.暴力枚举
        int n = nums.size();
        int i, j, k, z;
        for (i = 0; i < n;) // 固定一个数a
        {
            for (j = i + 1; j < n;) // 依次固定枚举第二个数
            {
                for (k = j + 1; k < n;) // 枚举第三个数
                {
                    for (z = k + 1; z < n;)  // 枚举第四个数
                    {
                        long long sum = (long)nums[i] + nums[j] + nums[k] + nums[z];
                        if (sum == target) 
                        {
                            ret.push_back({nums[i], nums[j], nums[k], nums[z]});
                        }
                        // 去重z
                        ++z;
                        while (z < n && nums[z] == nums[z - 1]) 
                        {
                            ++z;
                        }
                    }

                    // 去重k
                    ++k;
                    while (k < n && nums[k] == nums[k - 1]) 
                    {
                        ++k;
                    }
                }
                // 去重j
                ++j;
                while (j < n && nums[j] == nums[j - 1]) 
                {
                    ++j;
                }
            }
            // 去重i
            ++i;
            while (i < n && nums[i] == nums[i - 1]) 
            {
                ++i;
            }
        }
        return ret;
    }
};

提交记录(这里按理说会超时,时间复杂度:O(N⁴),但这里通过了):

Ⅱ. 双指针算法

//这里时间复杂度是O(N²)
class Solution {
public:
	vector<vector<int>> fourSum(vector<int>& nums, int target) {
		// 1.排序
		sort(nums.begin(), nums.end());

		vector<vector<int>> ret;
		int n = nums.size();
		// 2.利用双指针算法
		int i, j;
		for (i = 0; i < n;)  //固定数a
		{
			//利用三数之和
			for (j = i + 1; j < n;)  //固定数b
			{
				//双指针
				int left = j + 1;
				int right = n - 1;
				long long t = (long)target - nums[i] - nums[j];  //注意数据溢出问题,用long long解决
				while (left < right) 
				{
					int sum = nums[left] + nums[right];
					if (sum > t) 
					{
						--right;
					}
					else if (sum < t) 
					{
						++left;
					}
					else 
					{
						// 将获取的四元组尾插ret里
						ret.push_back({ nums[i], nums[j], nums[left++], nums[right--] });
						// 缩小区间查找
						// 不漏,去重

						// 去重left和right,注意区间越界问题
						while (left < right && nums[left] == nums[left - 1]) 
						{
							++left;
						}
						while (left < right && nums[right] == nums[right + 1])
						{
							--right;
						}
					}
				}
				// 注意j的去重
				++j;
				while (j < n && nums[j] == nums[j - 1]) 
				{
					++j;
				}
			}
			// 注意去重i
			++i;
			while (i < n && nums[i] == nums[i - 1]) 
			{
				++i;
			}
		}
		return ret;
	}
};

提交记录:

制作不易,若有不足之处或出问题的地方,请各位大佬多多指教 ,感谢大家的阅读支持!!!   

 


http://www.niftyadmin.cn/n/5750923.html

相关文章

//结构体练习:定义一个结构体表示学生//学生属性有:姓名,年龄//要求:把三个学生信息放入到数组当中,并遍历数组

//结构体练习:定义一个结构体表示学生 //学生属性有&#xff1a;姓名&#xff0c;年龄 //要求&#xff1a;把三个学生信息放入到数组当中&#xff0c;并遍历数组 #include<stdio.h> struct student { char name[100]; int age; }; int main() { struct student s…

【C语言 - 简易架构】

对于很多半路出家的程序员来说&#xff0c;很多基础的事情反而不知道怎么办。类似全局变量、局部变量、函数声明、函数定义之类的。 一、头文件&#xff08;.h文件&#xff09;的作用 头文件在C语言中扮演着至关重要的角色&#xff0c;它们主要用于声明函数、宏定义、类型定义…

【初阶数据结构与算法】链表刷题之移除链表元素、反转链表、找中间节点、合并有序链表、链表的回文结构

文章目录 一、移除链表元素思路一思路二 二、合并两个有序链表思路&#xff1a;优化&#xff1a; 三、反转链表思路一思路二 四、链表的中间节点思路一思路二 五、综合应用之链表的回文结构思路一&#xff1a;思路二&#xff1a; 一、移除链表元素 题目链接&#xff1a;https:…

【大数据学习 | HBASE高级】hive操作hbase

一般在查询hbase的数据的时候我们可以直接使用hbase的命令行或者是api进行查询就行了&#xff0c;但是在日常的计算过程中我们一般都不是为了查询&#xff0c;都是在查询的基础上进行二次计算&#xff0c;所以使用hbase的命令是没有办法进行数据计算的&#xff0c;并且对于hbas…

【大数据学习 | HBASE高级】rowkey的设计,hbase的预分区和压缩

1. rowkey的设计 ​ RowKey可以是任意字符串&#xff0c;最大长度64KB&#xff0c;实际应用中一般为10~100bytes&#xff0c;字典顺序排序&#xff0c;rowkey的设计至关重要&#xff0c;会影响region分布&#xff0c;如果rowkey设计不合理还会出现region写热点等一系列问题。 …

SQL 外连接

1 外连接 外连接是一种用于结合两个或多个表的方式&#xff0c;返回至少一个表中的所有记录。 左外连接 LEFT JOIN&#xff0c;左表为驱动表&#xff0c;右表为从表。返回驱动表的所有记录以及从表中的匹配记录。如果从表没有匹配&#xff0c;则结果中从表的部分为NULL。 右…

技术栈2:Git分布式版本控制工具

目录 1.版本控制器 2.Git概述 3.Git常用命令 4.获取本地仓库 5.基础操作指令 6.gitignore文件 7.分支与合并 8.远程仓库 1.版本控制器 1.1集中式版本控制器 集中式版本控制工具&#xff0c;版本库是集中存放在中央服务器的&#xff0c;team里每个人work时…

Linux网络——自定义协议与序列化

一、协议 协议是一种 " 约定 ". socket api 的接口 , 在读写数据时 , 都是按 " 字符串 " 的方式来发送接收的。如 果我们要传输一些 " 结构化的数据 "&#xff0c;依然可以通过协议。 其实&#xff0c;协议就是双方约定好的结构化的数据。…