算法练习-四数之和(思路+流程图+代码)

难度参考

        难度:中等

        分类:数组

        难度与分类由我所参与的培训课程提供,但需要注意的是,难度与分类仅供参考。且所在课程未提供测试平台,故实现代码主要为自行测试的那种,以下内容均为个人笔记,旨在督促自己认真学习。

题目

        给定一个包含n个整数的数组nums和一个目标值target,判断nums中是否存在四个元素a,b,c和d,使得a+b+c+d的值与target相等?找出所有满足条件且不重复的四元组。
        示例1:
        输入:nums=[1,0,-1,0,-2,2]和target=0
        输出:[[-1,0,0,1],[-2,-1,1,2],[-2,0,0,2]]
        额外要求:
        ·答案中不可以包含重复的四元组

思路

  1. 排序: 首先将数组nums排序。排序是为了后面能够方便地跳过重复的元素。

  2. 循环遍历: 使用两层嵌套循环遍历数组,外层循环选择第一个数字a,内层循环选择第二个数字b

  3. 双指针寻找: 内层循环固定了ab之后,使用一对双指针leftright(分别初始化为b之后的下一个元素和数组末尾的元素)来查找剩下的两个数字。

  4. 移动双指针: 如果a + b + nums[left] + nums[right]的和小于target,则移动left指针;如果和大于target,则移动right指针;如果和等于target,则将这四个元素作为一组加入结果集。

  5. 跳过重复元素: 在循环和双指针移动的过程中,每当我们要移动某个指针时,如果下一个数字与当前数字相同,那么我们就继续移动指针,直到遇到一个不同的数字为止。这样可以避免重复的组合加入结果集。

  6. 返回结果: 最后返回存放所有四元组的结果集。

示例

        假设我们得到的输入是nums = [1, 0, -1, 0, -2, 2],并且target = 0

  1. 排序:
    排序后的nums数组将是[-2, -1, 0, 0, 1, 2]

  2. 循环遍历双指针寻找:

    a. 在最外层循环中,我们首先选择-2作为a。此时i=0

    b. 在第二层循环中,我们选择-1作为b。此时j=1

    c. 现在我们将left设为j+1,即2的位置,right设为数组的最后一个元素的位置,即2的位置。

    d. 进行双指针查找,我们开始检查sum = a + b + nums[left] + nums[right]是否等于target

  3. 移动双指针:

    a. 第一次计算得到sum = -2 + (-1) + 0 + 2 = -1,这比target小,所以我们移动左指针left向右一位。

    b. 现在left在第三个0的位置上,我们再次计算得到sum = -2 + (-1) + 0 + 2 = -1,依然比target小,所以我们再次移动左指针。

    c. 左指针不断右移,直到leftright指向同一个元素或者leftright左边时停止。

  4. 找到合适的组合:

    a. 如果在移动指针的过程中找到sum = target,例如我们发现nums[0] + nums[1] + nums[3] + nums[5] = -2 + (-1) + 0 + 2 = 0,我们加入这个组合[-2, -1, 0, 2]到结果集中。

  5. 跳过重复元素:

    如果有重复的元素,例如在nums = [1, 0, -1, 0, -2, 2]排序后的数组中从第三个元素开始到第四个元素是0,我们在寻找组合[-1, 0, 0, 1]之后,需要跳过所有接下来的相同的数字(这里是0),以避免重复的组合被加入结果集。

梳理

        这种解法有效地解决了四数之和的问题,它使用了以下几个关键的处理方式:

  1. 排序: 这是双指针方法能够工作的前提。通过对原数组进行排序,我们能够以线性的方式(通过移动指针)来改变所选元素组合的和(sum)。如果和小了,我们可以通过移动左指针向右方向增大;如果和大了,可以通过移动右指针向左方向减小。

  2. 双指针寻找: 这是减少算法复杂度的关键。对于已排序的数组,固定两个数后,可以在O(n)的时间内通过双指针找到符合条件的其他两个数,该策略避免了简单粗暴的四层循环(O(n^4))。

  3. 跳过重复元素: 这种处理方式利用了数组已排序的性质。当固定住一个或多个数字时,为了避免重复出现解,需要跳过数组中接下来的相同数字。这种去重是通过在循环和双指针移动的过程中检测相邻数字是否相同来实现的。

        这个解法借鉴了三数之和问题(3Sum)的经典解法,扩展到了四个数字,使得该问题的时间复杂度从O(n^4)降低到了O(n^3)。它有效地运用了双指针技巧来减少对时间复杂度的需求,同时也利用了排序以及跳过重复元素来确保找到所有可能的唯一结果。这种方法在解决类似的求和问题中是相当常见和高效的。

        三数之和:算法练习-三数之和(思路+流程图+代码)-CSDN博客

代码

#include <iostream> // 导入输入输出流的库
#include <vector>   // 导入向量容器的库
#include <algorithm> // 导入算法库(包括sort)

using namespace std; // 使用标准命名空间以简化代码

// fourSum函数,找出所有和为target的四元组合
vector<vector<int>> fourSum(vector<int>& nums, int target) {
    // 初始化返回的结果集
    vector<vector<int>> result;

    // 当输入向量的大小小于4时,不可能有四个数的组合,直接返回空的结果集
    if (nums.size() < 4) return result;
    
    // 对输入向量进行排序,为了后续操作可以采用双指针的方法
    sort(nums.begin(), nums.end());

    // 第一层循环,确定第一个数
    for (unsigned int i = 0; i < nums.size() - 3; ++i) {
        // 如果当前数字和之前的数字相同,则跳过,以避免产生重复结果
        if (i > 0 && nums[i] == nums[i-1])
            continue;
        
        // 第二层循环,确定第二个数
        for (unsigned int j = i + 1; j < nums.size() - 2; ++j) {
            // 如果当前数字和之前的数字相同,则跳过,以避免产生重复结果
            if (j > i + 1 && nums[j] == nums[j-1])
                continue;

            // 第三层循环采用双指针方法,确定剩下的两个数
            int left = j + 1;  // 左指针初始化
            int right = nums.size() - 1;  // 右指针初始化

            // 使用双指针在剩余数组中寻找合适的两个数字,使得这四个数字之和为target
            while (left < right) {
                // 计算当前四个数的和
                int sum = nums[i] + nums[j] + nums[left] + nums[right];
                // 如果四数之和等于目标和,则将它们作为一个四元组添加到结果集中
                if (sum == target) {
                    // 添加到结果集
                    result.push_back({nums[i], nums[j], nums[left], nums[right]});
                    
                    // 为了避免添加重复的四元组,需要将左指针移到下一个不同的数
                    while (left < right && nums[left] == nums[left+1]) left++;
                    // 同样的,将右指针移到上一个不同的数
                    while (left < right && nums[right] == nums[right-1]) right--;

                    // 将左、右指针各自移到下一个位置
                    left++;
                    right--;
                // 如果四数之和小于目标和,则将左指针右移,增加总和
                } else if (sum < target) {
                    left++;
                // 如果四数之和大于目标和,则将右指针左移,减少总和
                } else {
                    right--;
                }
            }
        }
    }
    
    // 返回最终结果集
    return result;
}

// 辅助函数,用于打印四数之和的结果
void printResult(const vector<vector<int>>& res) {
    cout << "["; // 开始打印输出结果
    // 遍历所有的四元组并打印它们
    for (const auto &v : res) {
        cout << "[";
        for (int i = 0; i < v.size(); ++i) {
            cout << v[i];
            if (i < v.size() - 1) cout << ","; // 用逗号分隔同一个四元组的数
        }
        cout << "],"; // 结束四元组的打印
    }
    if (!res.empty()) cout << "\b"; // 如果结果集不为空,则去掉最后一个多余的逗号
    cout << "]" << endl; // 结束打印输出结果
}

int main() {
    // 创建一个示例向量
    vector<int> nums = {1, 0, -1, 0, -2, 2};
    // 定义目标和
    int target = 0;
    // 调用函数并保存结果
    vector<vector<int>> res = fourSum(nums, target);
    // 打印结果
    printResult(res); 
    // main函数正常退出
    return 0;
}

打卡

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/373754.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

配置git环境与项目创建

项目设计 名称&#xff1a;KOB 项目包含的模块 PK模块&#xff1a;匹配界面&#xff08;微服务&#xff09;、实况直播界面&#xff08;WebSocket协议&#xff09; 对局列表模块&#xff1a;对局列表界面、对局录像界面 排行榜模块&#xff1a;Bot排行榜界面 用户中心模块&…

【Qt】常见问题

1.存在未解析的标识符 将build文件夹删掉重新编译。 2.左侧项目目录栏无法删除已添加项目 打开目标项目上一级的pro文件&#xff0c;将目标文件名字注释或者删除掉&#xff0c;最后保存&#xff0c;qt就会自动更新&#xff0c;将该项目隐藏掉。 3.在qt creator下添加槽函数…

大型装备制造企业案例分享——通过CRM系统管理全球业务

本期&#xff0c;小Z为大家带来的CRM管理系统客户案例是某大型装备制造企业运用Zoho CRM管理全球业务的过程分享。该企业是创业板上市公司&#xff0c;业务遍及100多个国家和地区&#xff0c;合作伙伴超百位&#xff0c;拥有覆盖全球的销售和服务网络。截止目前&#xff0c;相继…

油猴js 获取替换网页链接并重定向

场景 适用一些镜像网站进行重定向&#xff0c;比如Github。 代码 // UserScript // name New Userscript // namespace http://tampermonkey.net/ // version 2024-02-06 // description try to take over the world! // author You // match …

❤ React18 环境搭建项目与运行(地址已经放Gitee开源)

❤ React项目搭建与运行 环境介绍 node v20.11.0 react 18.2 react-dom 18.2.0一、React环境搭建 第一种普通cra搭建 1、检查本地环境 node版本 18.17.0 检查node和npm环境 node -v npm -v 2、安装yarn npm install -g yarn yarn --version 3、创建一个新的React项目…

OpenCV 图像处理六(傅里叶变换、模板匹配与霍夫变换)

文章目录 一、傅里叶变换1.1 NumPy实现和逆实现1.1.1 NumPy实现傅里叶变换Demo 1.1.2 NumPy实现逆傅里叶变换Demo 1.2 OpenCV实现和逆实现1.2.1 OpenCV实现傅里叶变换Demo 1.2.2 OpenCV实现逆傅里叶变换Demo 1.3 频域滤波1.3.1低频、高频1.3.2 高通滤波器构造高通滤波器Demo 1.…

jquery写表格,通过后端传值,并合并单元格

<!DOCTYPE html> <html> <head><title>Table Using jQuery</title><style>#tableWrapper {width: 100%;height: 200px; /* 设置表格容器的高度 */overflow: auto; /* 添加滚动条 */margin-top: -10px; /* 负的外边距值&#xff0c;根据实际…

Mac OS中创建适合网络备份的加密镜像文件:详细步骤与参数选择

这篇文章提供了在Mac OS中创建适合网络备份的加密镜像文件的详细步骤&#xff0c;同时探讨了在选择相关参数时的关键考虑因素&#xff0c;以确保用户能够安全、高效地存储和保护重要数据。 创建步骤 在Mac OS Monterey中&#xff0c;你可以使用“磁盘工具”&#xff08;Disk …

【制作100个unity游戏之23】实现类似七日杀、森林一样的生存游戏12(附项目源码)

本节最终效果演示 文章目录 本节最终效果演示系列目录前言斧头动画控制配置拿出 待机和攻击动画代码控制攻击动画 源码完结 系列目录 前言 欢迎来到【制作100个Unity游戏】系列&#xff01;本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第23篇中&#xff0…

LRU缓存

有人从网络读数据&#xff0c;有人从磁盘读数据&#xff0c;机智的人懂得合理利用缓存加速数据的读取效率&#xff0c;提升程序的性能&#xff0c;搏得上司的赏识&#xff0c;赢得白富美的青睐&#xff0c;进一步走向人生巅峰~ LRU假说 LRU缓存&#xff08;Least Recently Used…

SQL--函数

概念 函数 是指一段可以直接被另一段程序调用的程序或代码。 也就意味着&#xff0c;这一段程序或代码在MySQL中 已经给我们提供了&#xff0c;我们要做的就是在合适的业务场景调用对应的函数完成对应的业务需求即可。 那 么&#xff0c;函数到底在哪儿使用呢&#xff1f; 我…

【Python 实战】---- 实现向指定PDF指定页面指定位置插入图片

1. 需求 想要能否实现批量自动为多个pdf加盖不同六格虚拟章(不改变pdf原有分辨率和文字可识别性);改在pdf首页上方空白位置,一般居中即可;如可由使用者自主选择靠页边距更好,以便部分首页上方有字的文件时人工可微调位置。2. 需求分析 直接将 pdf 文件转换为图片,在将图…

飞天使-k8s知识点12-kubernetes散装知识点1-架构有状态资源对象分类

文章目录 k8s架构图有状态和无状态服务 资源和对象对象规约和状态 资源的对象-资源的分类元数据型与集群型资源命名空间 k8s架构图 有状态和无状态服务 区分有状态和无状态服务有利于维护yaml文件 因为配置不同资源和对象 命令行yaml来定义对象对象规约和状态 规约 spec 描述…

Unity_修改天空球

Unity_修改天空球 Unity循序渐进的深入会发现可以改变的其实很多&#xff0c;剖开代码逻辑&#xff0c;可视化的表现对于吸引客户的眼球是很重要的。尤其对于知之甚少的客户&#xff0c;代码一般很难说服客户&#xff0c;然表现确很容易。 非代码色彩通才&#xff0c;持续学习…

洞悉未来,解锁因果:2023年DataFunSummit因果推断在线峰会全景解读

随着大数据和人工智能的飞速发展&#xff0c;因果推断作为连接数据与决策的桥梁&#xff0c;正日益受到各行业的广泛关注。 在这样的背景下&#xff0c;2023年DataFunSummit因果推断在线峰会如期而至&#xff0c;汇聚了众多业界领袖和专家学者&#xff0c;共同探讨因果推断的最…

【Java从入门到精通】Java注释

Java 注释 在计算机语言中&#xff0c;注释是计算机语言的一个重要组成部分&#xff0c;用于在源代码中解释代码的作用&#xff0c;可以增强程序的可读性&#xff0c;可维护性。 Java 注释是一种在 Java 程序中用于提供代码功能说明的文本。 注释不会被编译器包含在最终的可…

FANUC机器人如何清除示教器右上角的白色感叹号?

FANUC机器人如何清除示教器右上角的白色感叹号&#xff1f; 如下图所示&#xff0c;示教器上显示白色的感叹号&#xff0c;如何清除呢&#xff1f; 具体可参考以下步骤&#xff1a; 按下示教器上白色的“i”键&#xff0c;如下图所示&#xff0c; 如下图所示&#xff0c;按…

Linux截图快捷键以及修改快捷键方式

1. 截图快捷键 初始快捷键如下 全屏截图并保存&#xff1a;AltPrint 选区截图并保存&#xff1a;ShiftPrint 全屏截图并复制到剪贴板&#xff1a;AltCtrlPrint 选区截图并复制到剪贴板&#xff1a;ShiftCtrlPrint 会保存到Pictures文件夹下面 2. 修改快捷键 打开Settings界面…

一文了解 ArrayList 的扩容机制

了解 ArrayList 在 Java 中常用集合类之间的关系如下图所示&#xff1a; 从图中可以看出 ArrayList 是实现了 List 接口&#xff0c;并是一个可扩容数组&#xff08;动态数组&#xff09;&#xff0c;它的内部是基于数组实现的。它的源码定义如下&#xff1a; public class A…

BEV感知算法学习

BEV感知算法学习 3D目标检测系列 Mono3D(Monocular 3D Object Detection for Autonomous Driving) 流程&#xff1a; 通过在地平面上假设先验&#xff0c;在3D空间中对具有典型物理尺寸的候选边界框进行采样&#xff1b;然后我们将这些方框投影到图像平面上&#xff0c;从而避…