【前言】本文以及之后的一些题解都会陆续整理到目录中,若想了解全部题解整理,请看这里:
第0007页 · 数组中重复的数据
今天,我们来看一个在实际工作中运用不多,但是对于一些算法题还是有必要的奇技淫巧——数组的原地修改。下面我们将通过两道题目来学习这种技巧。
【找到所有数组中消失的数】 给你一个含
n
个整数的数组nums
,其中nums[i]
在区间[1, n]
内。请你找出所有在[1, n]
范围内但没有出现在nums
中的数字,并以数组的形式返回结果。
示例1 示例2 输入描述:nums = [4, 3, 2, 7, 8, 2, 3,1] 输入描述:nums = [1, 1] 输出描述:[5, 6] 输出描述:[2] 【解题分析】这道题本来十分简单,但是如果加上一个要求:在不使用额外空间且时间复杂度为 O
(n)
的情况下解决这个问题吗? 可以假定返回的数组不算在额外空间内。那么这道题就有些内容可以说了。这里我们先来讲解一下这种原地修改的想法:首先,由于 nums 的长度恰好为 n,而我们要查找的数字范围均在 [1, n] 中,那么我们不妨将位置 k 处的值当成数字 k + 1 是否出现的凭证。以上是 Hash 的思想。
接下来,我们需要考虑如何才能使位置 k 处的值能够代表数字 k + 1 是否出现过。我们提供一种想法是如果 k + 1 出现过,需要将位置 k 处的值加上 n,因为本来数组中所有的数都小于 n,只能是由于出现过而导致大于 n。根据以上想法,我们就可以使位置 k 处的值能够代表数字 k + 1 是否出现过。
【源码展示】
class Solution { public:vector<int> findDisappearedNumbers(vector<int>& nums) {int n = nums.size();for (int num : nums) {int x = (num - 1) % n;nums[x] += n;}vector<int> ret;for (int i = 0; i < n; i++) {if (nums[i] <= n) {ret.push_back(i + 1);}}return ret;} };
解决完上面这道题,我们再来看今天的第二题,中间也需要用到类似的思想。
【数组中重复的数据】给你一个长度为
n
的整数数组nums
,其中nums
的所有整数都在范围[1, n]
内,且每个整数出现 一次 或 两次 。请你找出所有出现 两次 的整数,并以数组形式返回。你必须设计并实现一个时间复杂度为
O(n)
且仅使用常量额外空间(不包括存储输出所需的空间)的算法解决此问题。
示例1 示例2 示例3 输入:nums = [4,3,2,7,8,2,3,1] 输入:nums = [1,1,2] 输入:nums = [1] 输出:[2.3] 输出:[1] 输出:[1] 【解题分析】这里和上面一题的要求几乎一致,思路也差不多,就留给读者自行思考了
【源码展示】
class Solution { public:vector<int> findDuplicates(vector<int>& nums) {int size = nums.size();for (int i = 0; i< size; i++) {// Swap until appear in right poswhile (nums[i] != nums[nums[i] - 1]) {swap(nums[i], nums[nums[i] - 1]);}}vector<int> ans;for (int i = 0; i < size; i++) {if (nums[i] != (i+1)) {ans.emplace_back(nums[i]);}}return ans;} };