LeetCode Hot 100 | 技巧(C++ 题解)

LeetCode Hot 100 | 技巧(C++ 题解)

Hot100 技巧(官方顺序):136 / 169 / 75 / 31 / 287


前言

"技巧"专题汇集了利用位运算、摩尔投票、双指针等非常规方法解决问题的经典题目。本篇涵盖只出现一次的数字、多数元素、颜色分类、下一个排列和寻找重复数五道题(268、448暂无C++ AC提交,跳过)。


1. 136. 只出现一次的数字 🟢

题目描述

给你一个 非空 整数数组 nums,除了某个元素只出现 一次 以外,其余每个元素均出现 两次。找出那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。

示例 1:

输入:nums = [2,2,1]
输出:1

示例 2:

输入:nums = [4,1,2,1,2]
输出:4

约束: 1 <= nums.length <= 3 * 10^4, -3 * 10^4 <= nums[i] <= 3 * 10^4

图解

136. 图解

解题思路

异或性质:a ^ a = 0a ^ 0 = a。将所有元素异或,成对的数字相消,剩下只出现一次的数字。

nums = [4,1,2,1,2] 为例:

步骤xans
初始-0
x=440^4=4
x=114^1=5
x=225^2=7
x=117^1=6
x=226^2=4 ✓

复杂度分析

  • 时间: O(n)
  • 空间: O(1)

C++ 代码

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

2. 169. 多数元素 🟢

题目描述

给定一个大小为 n 的数组 nums,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

输入:nums = [3,2,3]
输出:3

示例 2:

输入:nums = [2,2,1,1,1,2,2]
输出:2

约束: n == nums.length, 1 <= n <= 5 * 10^4, -10^9 <= nums[i] <= 10^9

图解

169. 图解

解题思路

摩尔投票:维护候选人 ans 和计数 count。若 count==0 则换候选人,若当前元素与候选人相同则 +1,否则 -1。超过半数的元素必然是最终候选人。

nums = [2,2,1,1,1,2,2] 为例:

xcountans操作
20→12count=0, ans=2, count=1
21→22相同, count=2
12→12不同, count=1
11→02不同, count=0
10→11count=0, ans=1, count=1
21→01不同, count=0
20→12count=0, ans=2, count=1

返回 ans=2 ✓

复杂度分析

  • 时间: O(n)
  • 空间: O(1)

C++ 代码

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int count = 0;
        int ans = 0;
        
        for (int &x : nums) {
            if (count == 0) {
                ans = x;
                count++;
            } else {
                if (ans != x)
                    count--;
                else
                    count++;
            }
        }
        
        return ans;
    }
};

3. 75. 颜色分类 🟡

题目描述

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 012 分别表示红色、白色和蓝色。必须在不使用库内置的 sort 函数的情况下解决这个问题。

示例 1:

输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]

示例 2:

输入:nums = [2,0,1]
输出:[0,1,2]

约束: n == nums.length, 1 <= n <= 300, nums[i]012

图解

75. 图解

解题思路

荷兰国旗问题(三路快排):维护三个指针 left(0的右边界)、right(2的左边界)、i(当前遍历):

  • nums[i]==0:与 left 互换,left++, i++
  • nums[i]==2:与 right 互换,right–(i不动,需再检查)
  • nums[i]==1:i++

nums = [2,0,2,1,1,0] 为例:

ileftrightnums[i]操作数组
0052与right互换,right–[0,0,2,1,1,2]
0040与left互换,left++,i++[0,0,2,1,1,2]
1140与left互换[0,0,2,1,1,2]
[0,0,1,1,2,2]

复杂度分析

  • 时间: O(n)
  • 空间: O(1)

C++ 代码

class Solution {
public:
    void sortColors(vector<int>& nums) {
        int left = 0;
        int right = nums.size() - 1;
        int i = 0;
        while (i <= right) {
            if (nums[i] == 0) {
                swap(nums[i], nums[left]);
                left++;
                i++;
            } else if (nums[i] == 2) {
                swap(nums[i], nums[right]);
                right--;
            } else {
                i++;
            }
        }
    }
};

4. 31. 下一个排列 🟡

题目描述

整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。

下一个排列 是指其整数的下一个字典序更大的排列。如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

必须 原地 修改,只允许使用额外常数空间。

示例 1:

输入:nums = [1,2,3]
输出:[1,3,2]

示例 2:

输入:nums = [3,2,1]
输出:[1,2,3]

示例 3:

输入:nums = [1,1,5]
输出:[1,5,1]

约束: 1 <= nums.length <= 100, 0 <= nums[i] <= 100

图解

31. 图解

解题思路

三步走:

  1. 从右找第一个下降点 posnums[pos] < nums[pos+1]
  2. 从右找第一个大于 nums[pos] 的数,与 pos 交换
  3. pos+1 之后的部分翻转(由降序变升序)

若不存在下降点(整体降序),pos=-1,直接翻转全部。

nums = [1,2,3] 为例:

步骤操作数组
1从右找下降点: pos=1(nums[1]=2 < nums[2]=3)[1,2,3]
2从右找>2的数: i=2(3>2), swap(nums[2],nums[1])[1,3,2]
3翻转pos+1=2之后: 无需翻转[1,3,2] ✓

复杂度分析

  • 时间: O(n)
  • 空间: O(1)

C++ 代码

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int n = nums.size();
        int pos = -1;
        for (int i = n - 1; i >= 1; i--) {
            if (nums[i - 1] < nums[i]) {
                pos = i - 1;
                break;
            }
        }
        
        if (pos != -1) {
            for (int i = n - 1; i > pos; i--) {
                if (nums[i] > nums[pos]) {
                    swap(nums[i], nums[pos]);
                    break;
                }
            }
        }
        std::cout<< pos << std::endl;
        reverse(nums.begin() + pos + 1, nums.end());
    }
};

5. 287. 寻找重复数 🟡

题目描述

给定一个包含 n + 1 个整数的数组 nums,其数字都在 [1, n] 范围内(包括 1n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数,返回 这个重复的数

你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。

示例 1:

输入:nums = [1,3,4,2,2]
输出:2

示例 2:

输入:nums = [3,1,3,4,2]
输出:3

约束: 1 <= n <= 10^5, nums.length == n + 1, 1 <= nums[i] <= n

图解

287. 图解

解题思路

Floyd 判圈算法(龟兔赛跑):将数组视为链表(i -> nums[i]),重复数即为环的入口。快慢指针相遇后,用新指针从头出发,与慢指针同速走,再次相遇即环入口。

nums = [1,3,4,2,2] 为例:

步骤slowfast路径
初始00
1nums[0]=1nums[nums[0]]=3
2nums[1]=3nums[nums[3]]=2
3nums[3]=2nums[nums[2]]=2slow==fast=2, 相遇
head=0slow=2head=0
4nums[2]=4nums[0]=1
5nums[4]=2nums[1]=3
6nums[2]=4→…实际: head走1→3→2,slow走2→4→2,在2处相遇,返回2 ✓

复杂度分析

  • 时间: O(n)
  • 空间: O(1)

C++ 代码

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int fast = 0;
        int slow = 0;
        
        while (true) {
            slow = nums[slow];
            fast = nums[nums[fast]];
            if (slow == fast)
                break;
        }
        
        int head = 0;
        while (head != slow) {
            slow = nums[slow];
            head = nums[head];
        }
        
        return slow;
    }
};

⚠️ 以下题目未找到 C++ AC 提交记录,跳过:

    1. 丢失的数字
    1. 找到所有数组中消失的数字

总结

题号题目难度核心技巧时间复杂度
136只出现一次的数字🟢简单全员异或,成对消零O(n)
169多数元素🟢简单摩尔投票,超半数必胜O(n)
75颜色分类🟡中等荷兰国旗三路快排O(n)
31下一个排列🟡中等找下降点→交换→翻转后缀O(n)
287寻找重复数🟡中等Floyd判圈,数组模拟链表O(n)
268丢失的数字🟢简单暂无C++ AC提交-
448消失的数字🟢简单暂无C++ AC提交-
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值