【Leetcode】vector刷题

这篇具有很好参考价值的文章主要介绍了【Leetcode】vector刷题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【Leetcode】vector刷题,leetcode刷题,c++,算法

🔥个人主页Quitecoder

🔥专栏Leetcode刷题

【Leetcode】vector刷题,leetcode刷题,c++,算法

1.只出现一次的数字

题目链接:136.只出现一次的数字
题目描述【Leetcode】vector刷题,leetcode刷题,c++,算法

这道题很简单,我们只需要遍历一遍数组,利用异或操作的性质(一个数与自身异或结果为0,任何数与0异或还是其本身)

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int value =0;
        for(auto v:nums)
        {
            value^=v;
        }
        return value;
    }
};

2.杨辉三角

题目链接:118.杨辉三角
题目描述【Leetcode】vector刷题,leetcode刷题,c++,算法

这道题我们需要构造二维数组,典型的vector的嵌套使用

【Leetcode】vector刷题,leetcode刷题,c++,算法
首先,我们先构建二维数组,开辟行数大小:

vector<vector<int>> v(numRows);

接着对每一行进行开辟空间,并将两端初始化为1

for(int i=0;i<numRows;i++)
{
    v[i].resize(i+1);
    v[i][0]=1;v[i][i]=1;
}

注意,resize是会进行初始化的,我们没有传值,默认为零

所以我们只需要遍历一遍,遍历到的位置为0,进行相加操作

完整代码如下:

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> v(numRows);
        for(int i=0;i<numRows;i++)
        {
            v[i].resize(i+1);
            v[i][0]=1;v[i][i]=1;
        }
        for(int i=0;i<numRows;i++)
        {
        for(int j=0;j<i;j++)
        {
            if(v[i][j]==0)
            {
                v[i][j]=v[i-1][j]+v[i-1][j-1];
            }
        }
        }
    return v;
    }
};

3.删除有序数组中的重复项

题目链接:26.删除有序数组中的重复项
题目描述【Leetcode】vector刷题,leetcode刷题,c++,算法

这题是一道简单的双指针思路的题,由于已经排序好,我们只需要设置两个索引,一个向后遍历,若与前面的索引指向值不相同,则对前面的值进行修改

lass Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if (nums.size() == 0) {
            return 0;
        }
        int slow = 0;
        for (int fast = 1; fast < nums.size(); fast++) {
            if (nums[fast] != nums[slow]) {
                slow++;
                nums[slow] = nums[fast];
            }
        }
        return slow + 1;
    }
};

完成了值的覆盖过程

4.只出现一次的数字II

题目链接:137.只出现一次的数字II
题目描述【Leetcode】vector刷题,leetcode刷题,c++,算法

这个问题的解决方案基于位操作和有限状态自动机的原理。我们要处理的数字是32位整数,因此,我们需要考虑每一位相加后的结果。由于除了一个数字以外,其它数字都出现了三次,我们可以构造一个数字的每一位相加后,模3的结果就是这个只出现一次的数字的相应位

思路如下:

使用两个整数变量onestwosones将会记录每个位只出现一次的情况,而twos将会记录每个位出现两次的情况

对于每个数字num及其每一位,我们更新onestwos

  1. 在第i个位置上,如果ones里的位是1,则表示num要么是第一次遇到i位为1,要么是第四次。如果是第四次,我们已经在twos里记录了两次,所以这次应该把ones里的该位清零,否则保持不变

  2. 同理,如果twos里的位是1,则是第二次遇到i位为1或者是第五次。如果是第五次,我们既要在ones里面加1,同时也要在twos里面清零该位,否则保持不变

  3. 由于我们只需要考虑每个位上1出现的次数,所以任何时候位上的1出现3次,我们都应该清零

最后,ones保留的就是每位上出现一次的结果,而twos将会是0。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ones = 0, twos = 0;       
        for (int num : nums) {
            ones = (ones ^ num) & ~twos;
            twos = (twos ^ num) & ~ones;
        }
        return ones;
    }
};

当我们讨论处理出现三次的数字和一个只出现一次的数字时,onestwos 的位操作确实是难以理解的 ,分解这两行代码:

对于每一个新的数字 num,我们用 onestwos 来跟踪彼此独立的状态:

  1. ones = (ones ^ num) & ~twos;

这里,我们正更新 ones 以包含出现一次的位。让我们分解这行代码:

  • ones ^ num:这个按位异或操作背后的思想是:当前的 ones 表示上一步迭代中已经出现一次的位。当我们再次看到这些位时(即 num 中的对应位也是1),我们希望重置 ones 中的那些位(因为出现一次变成了两次)。对于 num 中新出现的1,ones 中还没有记录,这将被加进 ones

  • & ~twos:接下来的按位与操作~twos 结合表示:我们删除 twos 中已经出现两次的位。~twos 是对 twos 取反,意味着取出 twos 中为0的位。只有那些在 twos 中没有记录(即还没达到两次)的1才应该加入 ones。即使刚才 ones ^ num 把某些位变成了1,若那些位在 twos 中已经出现过两次,我们必须确保它们在 ones 中不变成1

结合二者,ones 在每次迭代结束时仅保留那些恰好出现一次的位。如果某位在 ones 中变成了1但已经在 twos 中出现过,我们需要重置 ones 中的那位为0

  1. twos = (twos ^ num) & ~ones;

接着我们更新 twos 来反映那些已经看到两次的位:

  • twos ^ num:与更新 ones 类似,我们对于每个新来的 num,我们都会用按位异或更新 twos。如果在 twos 中的位是1,且对应的 num 中的位也是1,那么它们会重置为0,因为现在这个位出现了第三次,而我们的目标是找到出现了一次和两次的位。如果出现的是一个新的1(即 num 中的1,而 twos 中并没有记录),twos 就会记录它。这会出现加到三的情况,我们随后会处理。

  • & ~ones:这个按位与操作保证如果在 ones 中有1(意味着这个位已经出现了一次),我们不会在 twos 中加入该位。如果某个位同时在 onestwos 中出现,这意味着这个位出现了3次,并且最终会被忽略。

通过 & ~ones,我们确保了一个位仅仅当它在 num 中为1且在 ones 中尚未出现(即 ones 中为0)时,才会被加入 twos

总结来说,这两步操作是相互独立并且排他的:它们保证一个位在 onestwos 中出现,但不会同时出现。我们在整体数组中使用循环来考虑每个数字的影响。最终,由于所有出现三次的数字在这两个变量中都被消去,ones 会留下那个出现一次的唯一位

5.只出现一次的数字III

题目链接:260.只出现一次的数字III
题目描述【Leetcode】vector刷题,leetcode刷题,c++,算法

此类问题可以通过位运算(异或操作)来解决。首先,我们可以通过对所有数组元素执行异或操作来找出两个只出现一次的元素的异或结果。因为异或操作具有交换律和结合律,同时一个数字和自己进行异或会变成0,所以最终剩下的结果就是那两个只出现一次的数字的异或结果

这个结果中至少有一个位是1(否则这两个数相同),我们可以找到这个数中的任何一个为1的位,用它来把原数组分成两组,一组在该位上是1,一组在该位上是0。这样每组就包含了一个只出现一次的数字和一些成对出现的数字。然后再对这两个组分别进行异或操作,即可得到这两个只出现一次的数字。

下面是这个算法实现:

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        // 第一步,对所有元素进行异或,最终的结果就是两个只出现一次数的异或结果
        int diff = 0;
        for (int num : nums) {
            diff ^= num;
        }
        
        // 找到diff中任何为1的位,可以使用diff & -diff快速找到
        // 这个操作可以隔离出diff最右端的1
        unsigned int diff_unsigned = diff;
        diff_unsigned &= -diff_unsigned;

        // 使用找到的这一位将数组中的数字分成两组
        vector<int> results(2, 0); // 最终结果
        for (int num : nums) {
            if ((num & diff_unsigned) == 0) {
                // 第一组,与diff_unsigned对应位为0
                results[0] ^= num;
            } else {
                // 第二组,与diff_unsigned对应位为1
                results[1] ^= num;
            }
        }
        
        return results;
    }
};

在这个代码中:

  • diff_unsigned 最终会被设置为两个目标数字的异或结果。
  • diff_unsigned &= -diff_unsigned; 的结果是取出 diff_unsigned 最右边的1位,也就是两个只出现一次的数在这一位上不同的地方。
  • 然后我们通过判断这一位是否为1来将全部数字分为两组,并再次分别对它们进行异或操作,以此找到两个只出现一次的数。

这条语句 diff_unsigned &= -diff_unsigned; 是一种计算机用来找到一个数字中最右边的1的位,并且保持所有其他位为0的技巧。为了更好地理解这个技巧,我们需要先了解计算机中的数字表示——特别是补码表示法,因为这个技巧与负数的二进制表示相关

在补码表示中,一个负数是通过取其正值的二进制表示的反码(每个位取反)然后加1得到的。例如,假设我们有一个4位的系统:

正数 2 的二进制表示:  0010
反码 (invert):      1101
加1得到负数 -2:      1110

观察发现,从正数2的二进制表示到负数-2的表示,最右边的1以及之前的所有0都保持不变,而最右边的1之后的所有位都翻转了。这给了我们一种找到最右边的1的方法。现在,如果我们对2和-2执行按位与操作:

正数 2:                0010
负数 -2:               1110
按位与:                0010

按位与操作的结果就是只有最右边的1保留了下来,其它所有位都变成了0。换句话说,diff_unsigned &= -diff_unsigned; 将结果的所有位都置为0,除了最右边的1所在的位。

在解决问题时,我们首先会通过对所有数字进行异或得到 diff,这代表了两个只出现一次的数字的差异。
diff 变量首先被转换成一个无符号整数 diff_unsigned,然后对它进行取负和按位与操作,以避免未定义行为。这样就保证了即使 diff 的最高有效位是1,我们也不会超出无符号整型的范围

然后使用 diff_unsigned &= -diff_unsigned; 来保留最右边的1,这是两个独特数字在二进制表示中第一个不同的位。

通过这个位的差异,我们可以将所有的数字分成两组来进一步操作,每组包含一个只出现一次的数字以及成对出现的数字。这个1所在的位将用于分辨哪些数字在该位为0或1 —— 这正是对数组进行划分的依据

6.电话号码的字母组合

题目链接:17.电话号码的字母组合
题目描述【Leetcode】vector刷题,leetcode刷题,c++,算法

这个问题可以通过回溯法解决,这是一种通过穷举所有可能的解来找到全部解的算法。基本思想是从左到右遍历数字字符串,对于每个数字,向当前的字母组合中添加对应的每个字母,然后对剩余的字符串重复这个过程。

下面是递归解决实现:

class Solution {
public:
    vector<string> letterCombinations(string digits) {
        if (digits.empty()) return {}; // 如果输入为空,直接返回空数组
        
        vector<string> mappings = {  // 数字到字母的映射
            "", "", "abc", "def",   // '0','1','2',...
            "ghi", "jkl", "mno",
            "pqrs", "tuv", "wxyz"
        };
        vector<string> result;
        string current;
        
        backtrack(result, digits, 0, current, mappings);
        
        return result;
    }
    
private:
    void backtrack(vector<string>& result, const string& digits, 
                   int index, string& current, const vector<string>& mappings) {
        if (index == digits.length()) { // 如果到达了数字字符串的末尾,就添加当前的字母组合到结果中
            result.push_back(current);
            return;
        }
        
        string letters = mappings[digits[index] - '0']; // 获取当前数字对应的所有字母
        for (char letter : letters) { // 遍历这些字母
            current.push_back(letter);   // 添加当前的字母
            backtrack(result, digits, index + 1, current, mappings);  // 继续处理下一个数字
            current.pop_back();  // 回溯,移除当前字母,以便尝试下一个字母
        }
    }
};

这段代码定义了一个辅助函数 backtrack,用来递归寻找所有可能的字母组合。我们维护一个 current 字符串,它保存当前的部分组合。函数的工作流程是这样的:

  1. 确定终止条件:如果 current 的长度与输入数字字符串的长度相同,说明当前递归路径已经走到头,我们找到了一个完整的字母组合,将其添加到结果中。

  2. 确定递归逻辑:从 mappings 数组中获取当前处理的数字对应的所有可能字母,然后逐一向 current 添加每个字母,并递归地调用自己处理下一个数字。

  3. 回溯处理:每次递归调用完成后,需要将之前添加的字母移除,以便对当前位置尝试不同的字母。文章来源地址https://www.toymoban.com/news/detail-860738.html

到了这里,关于【Leetcode】vector刷题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Leetcode刷题】算法:罗马数字转整数

    定义一个 Solution 类,该类包含一个 romanToInt 方法用于将罗马数字转换为整数。 初始化变量 answer 为 0,用于保存转换后的整数值。 获取输入字符串 s 的长度,并保存在变量 length 中。 创建一个字典 d,将每个罗马数字字符与对应的数值进行映射。 使用 for 循环遍历 s 中的每个

    2024年02月05日
    浏览(55)
  • 数据结构算法leetcode刷题练习(1)

    给定一个三角形 triangle ,找出自顶向下的最小路径和。 每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标

    2023年04月24日
    浏览(22)
  • Java算法 leetcode简单刷题记录4

    买卖股票的最佳时机: https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/ 笨办法: 记录当天的值及之后的最大值,相减得到利润; 所有的天都计算下,比较得到利润最大值; 会超时 记录过程中遇到的最低值,每当有利润大于0及大于上一个利润值的情况,赋值; 最小和分割:

    2024年01月23日
    浏览(15)
  • Java算法 leetcode简单刷题记录6

    环和杆: https://leetcode.cn/problems/rings-and-rods/ 统计范围内的元音字符串数: https://leetcode.cn/problems/count-the-number-of-vowel-strings-in-range/ 最长平衡子字符串: https://leetcode.cn/problems/find-the-longest-balanced-substring-of-a-binary-string/ K 个元素的最大和: https://leetcode.cn/problems/maximum-sum-with-exa

    2024年01月24日
    浏览(18)
  • 算法刷题记录-双指针/滑动窗口(LeetCode)

    思路 根据题目描述,我们可以知道,如果要将某个单词定义为可扩张(stretchy),需要满足如下两个条件: 所以,我们在实现的时候,可以通过两个指针p1和p2,分别指向s和word,分别统计连续的相同字符数量c1和c2,然后再通过上述的两个条件进行判断,即:如果 则表示该单

    2024年02月09日
    浏览(11)
  • 【LeetCode刷题篇零】一些基础算法知识和前置技能(上)

    冒泡排序 冒泡的核心是两两比较,大数下沉,小数上浮,比较的轮数是数组的长度 N,每一轮比较的次数为 N - 当前轮的索引: 外层循环控制轮数 round: [1,N] 内层循环控制次数 i: [0,N - round) 在每一轮当中,内循环中两两比较相邻的两个数,大数下沉(交换),如果某一轮没

    2024年02月07日
    浏览(13)
  • 【leetcode刷题之路】剑指Offer(3)——搜索与回溯算法

    7 搜索与回溯算法 7.1 【BFS】剑指 Offer 32 - I - 从上到下打印二叉树 https://leetcode.cn/problems/cong-shang-dao-xia-da-yin-er-cha-shu-lcof/   这里借助队列来实现广度优先遍历,由于需要访问每一层的节点,而且这一层访问才可以访问下一层,所以可以考虑队列的先进先出,先把每一层的节

    2024年02月13日
    浏览(18)
  • java数据结构与算法刷题-----LeetCode283. 移动零

    java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完): https://blog.csdn.net/grd_java/article/details/123063846 解题思路 双指针,用right和left两个指针,将非0元素,全部按顺序换到数组前面。left指向左边非0元素应该插入的位置,right找到非

    2024年01月21日
    浏览(20)
  • java数据结构与算法刷题-----LeetCode566. 重塑矩阵

    java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完): https://blog.csdn.net/grd_java/article/details/123063846 代码:时间复杂度O(r*c).除题目要求外,算法本身没有需要额外空间,空间复杂度O(1) 从0开始算,一个3列n行矩阵中,每行就有3个元

    2024年01月21日
    浏览(18)
  • 【leetcode刷题之路】剑指Offer(4)——分治+排序算法+动态规划

    8 分治算法 8.1 【递归】剑指 Offer 07 - 重建二叉树 https://leetcode.cn/problems/zhong-jian-er-cha-shu-lcof/   前序遍历是根左右,中序遍历是左根右,这也就意味着前序遍历的第一个节点是整棵树的根节点,顺着这个节点找到它在中序遍历中的位置,即为in_root,那么in_root左边的都在左子

    2024年02月11日
    浏览(18)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包