hot 100 技巧题
技巧
只出现一次的数字
任何数与自身异或结果为 0:即 a ^ a = 0;
任何数与 0 异或结果为自身:即 a ^ 0 = a。
给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
class Solution {
public:int singleNumber(vector<int>& nums) {int ans = nums[0];if(nums.size() > 1) {for(int i = 1; i < nums.size(); i++) {ans = ans ^ nums[i];}}return ans;}
};
多数元素
给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
采用了摩尔投票算法(Boyer-Moore Majority Vote-counting algorithm) 来求解 “多数元素” 问题,核心思路是通过 “抵消” 不同元素的票数,最终多数元素(出现次数超过一半的元素)最终会被保留下来。
具体步骤:
维护两个变量:x(当前候选的多数元素)和 vote(候选元素的票数)。
遍历数组中的每个元素 num:
若 vote == 0(当前没有候选元素),则将 num 设为新的候选元素 x = num。
若 num == x(当前元素与候选元素相同),则票数 vote += 1(支持)。
若 num != x(当前元素与候选元素不同),则票数 vote -= 1(抵消)。
遍历结束后,x 就是多数元素。
class Solution {
public:int majorityElement(vector<int>& nums) {int x = 0, vote = 0;for(int num : nums) {if(vote == 0) {x = num;}vote += (x == num) ? 1 : -1;}return x;}
};
颜色分类
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
必须在不使用库内置的 sort 函数的情况下解决这个问题。
核心思路
通过两个指针 p0 和 p1 划分区间,遍历数组时将 0 和 1 交换到正确的位置:
p0:指向 “下一个 0 应该存放的位置”(p0 左侧全是 0)。
p1:指向 “下一个 1 应该存放的位置”(p0 到 p1-1 之间全是 1)。
遍历到 i 时,i 右侧是待处理的元素,最终 p1 右侧全是 2。
分步拆解逻辑
初始化指针:p0 = 0,p1 = 0(初始时无 0 和 1,指针都在起点)。
遍历数组,对每个元素 nums[i] 处理:
情况 1:nums[i] == 1直接将 nums[i] 交换到 p1 指向的位置(因为 p1 是下一个 1 的存放处),然后 p1++(1 的区间向右扩展)。例:[0,1,1,2] 中,i=2 是 1,交换后 p1 从 1 变为 2。
情况 2:nums[i] == 0第一步:将 nums[i] 交换到 p0 指向的位置(0 的存放处),此时 p0 处可能原本是 1(因为 p0 <= p1,p0 到 p1 之间是 1)。第二步:如果 p0 < p1(说明刚才交换走的是 1),需要把这个 1 交换到 p1 指向的位置(保证 1 的区间正确)。第三步:p0++ 和 p1++(0 和 1 的区间都向右扩展)。例:[1,0,2] 中,i=1 是 0:
先交换 nums[1] 和 nums[0] → [0,1,2];
因 p0=0 < p1=1,无需二次交换;
p0 和 p1 都变为 1 和 2。
情况 3:nums[i] == 2无需处理,因为遍历结束后 p1 右侧自然都是 2。
class Solution {
public:void sortColors(vector<int>& nums) {int n = nums.size();int p0 = 0, p1 = 0;for(int i = 0; i < n; i++) {if(nums[i] == 1){swap(nums[i], nums[p1]);++p1;}else if(nums[i] == 0){swap(nums[i],nums[p0]);if(p0 < p1){swap(nums[i], nums[p1]);}p0++;p1++;}}}
};
下一个排列
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。
必须 原地 修改,只允许使用额外常数空间。
class Solution {
public:void nextPermutation(vector<int>& nums) {int n = nums.size();int i = n - 2;while(i >= 0 && nums[i] >= nums[i+1]) {i--;}if(i >= 0) {int j = n - 1;while(nums[j] <= nums[i]){j--;}swap(nums[i], nums[j]);}reverse(nums.begin() + i + 1, nums.end());}
};
寻找重复数
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。
你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。
class Solution {
public:int findDuplicate(vector<int>& nums) {int tmp;sort(nums.begin(), nums.end());for(int i = 0; i < nums.size() - 1; i++) {tmp = nums[i+1] - nums[i];if(tmp == 0) return nums[i];}return -1;}
};
