题目:
给你一个含 n
个整数的数组 nums
,其中 nums[i]
在区间 [1, n]
内。请你找出所有在 [1, n]
范围内但没有出现在 nums
中的数字,并以数组的形式返回结果。
示例 1:
输入:nums = [4,3,2,7,8,2,3,1] 输出:[5,6]
示例 2:
输入:nums = [1,1] 输出:[2]
提示:
n == nums.length
1 <= n <= 105
1 <= nums[i] <= n
进阶:你能在不使用额外空间且时间复杂度为 O(n)
的情况下解决这个问题吗? 你可以假定返回的数组不算在额外空间内。
解决:
解法1:直接用哈希表
引入hashmap,记录在数组nums中出现的数值,再通过检索hashmap就可以找到哪些是在hashmap中没有出现的。把没有的放进list。不过使用hashmap就不符合进阶要求中的“不使用额外空间”这一条。
public List<Integer> findDisappearedNumbers(int[] nums) {
HashMap<Integer,Boolean> hash = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
hash.put(nums[i],true);
}
List<Integer> list = new LinkedList<>();
for (int i = 1; i <= nums.length ; i++) {
if(!hash.containsKey(i)){
list.add(i);
}
}
return list;
}
由于数字范围均在[1,n]中,可以用一个长度为你的数组来代替哈希表。利用区间之外的数字来记录数组元素,比如都加某个值,或者把数组元素都变成负的。就是下面的解法2和3。
解法2:变负数法
例:数组[4,3,2,7,8,2,3,1]
将这些整数作为数组下标,将对应的数组置为负值,找遍历完后是正数的。
从第0个元素开始,4-1=3,则把下标为3的数的值,即7变为负,即改为-7
第1个元素,3-1=2,则把下标为2的数的值,即2变为负,即改为-2
第2个元素,-2-1=-3,则把下标为-3的数的值,即下标为5的数变为负,即-2
第3个元素,-7-1=-8,则把下标为0的数的值改为负,即-4
第4个元素,8-1=7,则把下标为7的元素改为负,即-1
第5个元素,-2-1=-3,则把下标为-3的元素改为负,即2
第6个元素,3-1=2,则把下标为2的元素改为负,即-2
第7个元素,-1-1=-2,则把下标为-2的元素改为负,即下标为6的元素改为负,即-3
遍历完数组后我们发现,数组变成了[-4,-3,-2,-7,8,2,-3,-1],则只有下标为4和5的数没有变成负数,则缺失的就是4+1=5,以及5+1=6。
又比如数组中没有消失的数字,[3,2,1,4,5]
第0个元素,3-1=2,则下标为2的元素改为负,即1变成-1
第1个元素,2-1=1,则下标为1的元素改为负,即2变成-2
第2个元素,-1-1=-2,则下标为3的元素改为负,即4变成-4
第3个元素,-4-1=-5,则下标为0的元素改为负,即3变成-3
第4个元素,5-1=4,则下标为4的元素改为负,即5变成-5
遍历完数组后我们发现,数组中所有元素都变成负数了,则没有缺失的数字。
public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> results = new ArrayList<>();
for(int i = 0 ; i< nums.length;i++)
{
if(nums[Math.abs(nums[i])-1]>0)
{
nums[Math.abs(nums[i])-1] = -nums[Math.abs(nums[i])-1];
}
}
for(int i =0;i<nums.length;i++)
{
if(nums[i]>0)
{
results.add(i+1);
}
}
return results;
}
或者
public List<Integer> findDisappearedNumbers(int[] nums) {
//用来存放结果
List<Integer> res = new ArrayList<>();
//1. 遍历下数组的元素,对对应的索引位置的元素作标记
int len = nums.length;
for(int i = 0; i < len; i++){
int num = Math.abs(nums[i]); //由于数组的元素有可能被*-1,所以取绝对值
int index = num - 1;
if(nums[index] > 0){
nums[index] *= -1;
}
}
// 寻找没有标记的索引位置
for(int i = 0; i < len; i++){
if(nums[i] > 0){
int num = i + 1; //将索引转化为对应的元素
res.add(num);
}
}
return res;
}
解法3:加数字法
加数字的话要注意,加完后的数不能在原始数组中出现过。由于数组区间是1到n,则加n,加完后最小的也是n+1。将原数组当成哈希表,用每个数对长度n取余,取余后的数一定都小于n,再将这个数加n,遍历数组,当数组中的数小于n时说明数缺失了。
public List<Integer> findDisappearedNumbers(int[] nums) {
int n=nums.length;
for(int num : nums){
int x=(num-1)%n;//对n取模来还原它本来的值
nums[x]+=n;
}
List<Integer> result=new ArrayList<Integer>();
for(int i=0;i<n;i++){
if(nums[i]<=n){
result.add(i+1);
}
}
return result;
}
加油加油^_^