算法打卡第七天

21.两数之和

(力扣1题)

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

你可以按任意顺序返回答案。

示例 1:

复制代码
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

复制代码
输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

复制代码
输入:nums = [3,3], target = 6
输出:[0,1]
  • 解题思路
  1. 初始化一个空的哈希表。
  2. 遍历数组中的每个元素,在每一步中,计算目标值与当前元素的差值。
  3. 检查哈希表中是否存在这个差值,如果存在,立即返回对应的索引和当前元素的索引。
  4. 如果不存在,将当前元素及其索引存入哈希表,继续遍历。 这种方法确保每个元素只遍历一次,时间复杂度为O(n),空间复杂度为O(n),通过哈希表的快速查找特性,大大提高了效率。

代码

c++ 复制代码
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
    std::unordered_map<int, int> map;
    for(int i = 0 ; i < nums.size(); i++)
    {
          // 遍历当前元素,并在map中寻找是否有匹配的key
          auto iter =   map.find(target - nums[i]);
          if(iter != map.end())
          {
            // 找到了返回找到数组下标和当前数组下标
            return {iter->second, i};
          }
           // 如果没找到匹配对,就把访问过的元素和下标加入到map中
           map.insert(pair<int, int>(nums[i], i));
    }
          
    return {};
    }
};

22. 快乐数

(力扣202题)

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n快乐数 就返回 true ;不是,则返回 false

示例 1:

复制代码
输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

示例 2:

复制代码
输入:n = 2
输出:false
  • 解题思路
  1. 计算平方和 :定义 getSum 函数,用于计算一个数各位数字的平方和。
  2. 检测循环 :在 isHappy 函数中,使用哈希集合 unordered_set 存储出现过的平方和。
  3. 循环判断 :不断计算当前数的平方和,如果平方和为 1,返回 true 表示是快乐数;如果平方和重复出现(已在集合中),返回 false 表示陷入无限循环。
  4. 更新数值:将当前数更新为计算得到的平方和,继续下一轮判断。
c++ 复制代码
class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> umap;
        for (int a : nums1) 
        {
            for (int b : nums2) 
            {
                umap[a + b]++;
            }
        }
        int count = 0; // 统计a+b+c+d = 0 出现的次数
        // 再遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。
        for (int c : nums3) 
        {
            for (int d : nums4) {
                if (umap.find(0 - (c + d)) != umap.end()) 
                {
                    count += umap[0 - (c + d)];
                }
            }
        }
        return count;
    }
};

23.四数相加II

(力扣454题)

给你四个整数数组 nums1nums2nums3nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

  • 0 <= i, j, k, l < n
  • nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

示例 1:

复制代码
输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释:
两个元组如下:
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0

示例 2:

复制代码
输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
输出:1
  • 解题思路
  1. 构建哈希表 :遍历数组 nums1nums2,计算每对元素之和 a + b,并将这些和及其出现的次数存储在哈希表 umap 中。
  2. 查找补数 :遍历数组 nums3nums4,计算每对元素之和 c + d,然后查找哈希表中是否存在 0 - (c + d) 这个键。如果存在,将对应的出现次数累加到结果 count 中。

这种方法通过将问题分解为两部分(计算 a + b 和查找 -(c + d)),将四数之和的问题转化为两次两数之和的问题,从而降低了时间复杂度。通过哈希表的高效查找特性,确保了算法的高效性

代码

c++ 复制代码
class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> umap;
        for (int a : nums1) 
        {
            for (int b : nums2) 
            {
                umap[a + b]++;
            }
        }
        int count = 0; // 统计a+b+c+d = 0 出现的次数
        // 再遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。
        for (int c : nums3) 
        {
            for (int d : nums4) {
                if (umap.find(0 - (c + d)) != umap.end()) 
                {
                    count += umap[0 - (c + d)];
                }
            }
        }
        return count;
    }
};

24.赎金信

(力扣383题)

给你两个字符串:ransomNotemagazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false

magazine 中的每个字符只能在 ransomNote 中使用一次。

示例 1:

复制代码
输入:ransomNote = "a", magazine = "b"
输出:false

示例 2:

复制代码
输入:ransomNote = "aa", magazine = "ab"
输出:false

示例 3:

复制代码
输入:ransomNote = "aa", magazine = "aab"
输出:true
  • 解题思路
  1. 初始化哈希表 :使用一个大小为26的整型数组 record 来记录每个字母的出现次数,初始值为0。
  2. 统计杂志字符 :遍历 magazine 中的每个字符,将其对应的位置在 record 中的计数加1。
  3. 验证赎金信字符 :遍历 ransomNote 中的每个字符,将其对应的位置在 record 中的计数减1。如果某个字符的计数变为负数,说明杂志中没有足够的该字符,无法构造赎金信,返回 false
  4. 大小检查 :如果 magazine 的长度小于 ransomNote 的长度,直接返回 false,因为不可能有足够的字符来构造赎金信。
  5. 返回结果 :如果所有字符都验证通过,则返回 true,表示可以构造赎金信

代码

c++ 复制代码
#include <iostream>
using namespace std;
class Solution
{
public:
    bool canConstruct(std::string ransomNote, string magazine)
    {
        // 哈希表
        int record[26] = {0};
        if(magazine.size() < ransomNote.size())
        {
            return false;
        }
        // 遍历ransomNote元素存入哈希表
        for (int i = 0; i < magazine.size(); i++)
        {
            record[magazine[i] - 'a']++;
        }
        for (int i = 0; i < ransomNote.size(); i++)
        {
             record[ransomNote[i] - 'a']--;
            if(record[ransomNote[i] - 'a'] < 0)
            {
                return false;
            }
        }
        return true;
     
    }
};

25.三数之和

(力扣15题)

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

**注意:**答案中不可以包含重复的三元组。

示例 1:

复制代码
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

复制代码
输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:

复制代码
输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。
  • 解题思路

这段代码实现了寻找数组中所有和为零的三元组,核心在于利用排序和双指针技术来高效查找和去重。

首先对数组进行排序,这是为了方便后续的去重操作和双指针的应用。排序后,数组中的元素按非递减顺序排列,相同的元素都相邻。

接下来,外层循环遍历数组,固定第一个元素作为三元组的第一个元素。这里有一个优化条件,如果当前元素大于零则直接返回结果,因为后面的元素都大于零,不可能再形成和为零的三元组。

然后,对固定元素进行去重,避免重复处理相同的第一个元素。内层循环使用双指针法,左指针初始化为当前固定元素的下一个位置,右指针初始化为数组末尾。通过计算三数之和来调整指针的位置,小于零则左指针右移,大于零则右指针左移,直到找到和为零的三元组。

在找到一个有效的三元组后,对左右指针进行去重,跳过所有重复的元素,确保每个三元组只被记录一次。最后,左右指针同时向中间移动,继续寻找下一个可能的三元组。

这种方法的时间复杂度为O(n²),空间复杂度为O(1)

代码

复制代码
// 使用双指针法
class Solution
{
public:
    vector<vector<int>> threeSum(vector<int> &nums)
    {
        // 存储结果集
        vector<vector<int>> result;
        // 数组排序
        sort(nums.begin(), nums.end());
        for (int i = 0; i < nums.size(); i++)
        {
            // 如果a是正数,a<b<c,不可能形成和为0的三元组
            if (nums[i] > 0)
            {
               return result;
            }
            // 错误去重a方法,将会漏掉-1,-1,2 这种情况
            /*
            if (nums[i] == nums[i + 1]) {
                continue;
            }
            */
            // [a, a, ...] 如果本轮a和上轮a相同,那么找到的b,c也是相同的,所以去重a
            if (i > 0 && nums[i] == nums[i - 1])
            {
                continue;
            }
            //    定义双指针
            int left = i + 1;
            int right = nums.size() - 1;
            while (left < right) // 等于的话就是双元组了
            {
                // 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组
                /*
                while (right > left && nums[right] == nums[right - 1]) right--;
                while (right > left && nums[left] == nums[left + 1]) left++;
                */
                if (nums[i] + nums[left] + nums[right] < 0)
                    left++;   // 向右(大)移动
                else if (nums[i] + nums[left] + nums[right] > 0)
                    right--; // 向左(小)移动
                else
                {
                    // 找到目标
                    result.push_back(vector<int>{nums[i], nums[left], nums[right]});
                    // 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
                    while (left < right && nums[left] == nums[left + 1])
                        left++;
                    while (left < right && nums[right] == nums[right - 1])
                        right--;
                    // 找到目标,双指针同时收缩
                    left++;
                    right--;
                }
            }
        }
        return result;
    }
};

wchart.js/

相关推荐
啥都想学的又啥都不会的研究生42 分钟前
常规算法学习
java·数据结构·b树·学习·算法·排序算法
I AM_SUN2 小时前
153. 寻找旋转排序数组中的最小值
数据结构·c++·算法·leetcode·二分法
小白菜又菜2 小时前
Leetcode 2942. Find Words Containing Character
算法·leetcode·职场和发展
蒟蒻小袁4 小时前
力扣面试150题--二叉树的最近公共祖先
leetcode·面试·深度优先
数据与人工智能律师7 小时前
加密货币投资亏损后,能否以“欺诈”或“不当销售”索赔?
大数据·网络·算法·云计算·区块链
努力学习的小廉8 小时前
我爱学算法之—— 二分查找(下)
算法
AdSet聚合广告8 小时前
APP广告变现,开发者如何判断对接的广告SDK安全合规?
大数据·后端·算法·安全·uni-app
不二狗8 小时前
每日算法 -【Swift 算法】实现回文数判断!
开发语言·算法·swift
野犬寒鸦9 小时前
Redis核心数据结构操作指南:字符串、哈希、列表详解
数据结构·数据库·redis·后端·缓存·哈希算法
梁下轻语的秋缘11 小时前
Python人工智能算法 模拟退火算法求解01背包问题:从理论到实践的完整攻略
人工智能·python·算法·数学建模·模拟退火算法