leetcode——脑筋急转弯(萝卜坑)——剑指03、41、448、442

剑指 Offer 03. 数组中重复的数字

难度简单384

找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3 

思路

应该首先沟通题目要求:

1、不要求空间,要求速度则用 hash 法(遍历数组,将元素存入 hashSet 中,遇到 Set 已经包含该元素的情况,则返回该重复值)

2、要求不占用额外空间,但可以修改数组本身,则先排序,然后遍历数组,判断数组元素与前一个元素重复即可;

3、要求不占用额外空间,但可以修改数组本身,还可以利用原地 hash(萝卜)法 、还可以利用相应坑位取负法

4、要求不占用额外空间,且不可以修改数组本身,使用二分法(不是先排序,再二分查找的那个——自己写的287),而是取中间值 m,遍历数组中小于 m 的元素的数量,如果数量大于 m 则说明重复值在(left,m)中,否则在(m,right)中

//萝卜坑法:一个萝卜一个坑,把萝卜放回它自己的坑里(使后来能够重复),如果发现他的坑里有了自己,也就是重复了,但是要对原位置进行重复判断(多次swap)
//bug1:for 内没用 while 循环,swap 一次之后,还要再判断当前坑位萝卜是不是属于这个坑
//bug2:只有当要换的元素不属于它的坑时,才进行替换(否则会不断争强该坑位)
public int findRepeatNumber(int[] nums) {
        //原地 hash 法,时间 O(n)、空间O(1)
        for(int i=0;i<nums.length;i++){
            while(nums[i]!=i){
                if(nums[nums[i]]!=nums[i]){
                    swap(nums,i,nums[i]);
                }
                else return nums[i]; 
            }
        }
        return 0;
        
    }

41. 缺失的第一个正数

难度困难1052

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

进阶:你可以实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案吗?

示例 1:

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

示例 2:

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

示例 3:

输入:nums = [7,8,9,11,12]
输出:1

思路

萝卜坑,对于值不属于数组下标范围的元素,则不用管即可(因为要找的是没出现过的最小元素),第二次遍历,如果发现某元素不等于其下标加一,则说明该元素没出现过

class Solution {
    public void swap(int[] nums,int i,int j){
        int t=nums[i];
        nums[i]=nums[j];
        nums[j]=t;
    }
    //使用一个萝卜一个坑的解法
    public int firstMissingPositive(int[] nums) {
        for(int i=0;i<nums.length;i++){
            while(nums[i]>0&&nums[i]<=nums.length&&nums[i]!=i+1&&nums[nums[i]-1]!=nums[i]){
                //将 num[i]放在位置 num[i]上,并判断新的 num[i]
                //nums[nums[i]-1]=nums[i];
                swap(nums,i,nums[i]-1);
            }
        }

        int i=0;
        for(;i<nums.length;i++){
            if(nums[i]!=i+1){
                break;
            }
        }
        return i+1;
    }
}

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

难度简单713

给定一个范围在  1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。

找到所有在 [1, n] 范围之间没有出现在数组中的数字。

您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。

示例:

输入:
[4,3,2,7,8,2,3,1]

输出:
[5,6]

思路

相应坑位取负法:数组元素均为正,且值在【1,n】之间,因此可利用长度为 n 的数组记录遍历过的元素是否存在,不利用额外空间的话,可在原数组进行操作,为了避免冲掉后续还未遍历的元素,通过对该元素取反的形式进行表示:对下标为 i 的元素取反,表示数组中包含 元素i+1,第二次遍历时,如果nums[i]为正,说明 i+1没出现过

class Solution {
    //利用下标为 i 的元素的正负,作为对值为i的元素 是否存在的记录(存在则将响应位置元素置为负)
    public List<Integer> findDisappearedNumbers(int[] nums) {
        List<Integer> r=new ArrayList<>();
        for(int i=0;i<nums.length;i++){
            int num=Math.abs(nums[i]);
            if(nums[num-1]>0){
                nums[num-1]=-nums[num-1];
            }
        }
        for(int i=0;i<nums.length;i++){
            if(nums[i]>0){
                r.add(i+1);
            }
        }
        return r;
    }
}

442. 数组中重复的数据

难度中等371

给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次

找到所有出现两次的元素。

你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?

示例:

输入:
[4,3,2,7,8,2,3,1]

输出:
[2,3]

思路

数组元素均为正,且值在【1,n】之间,因此可利用长度为 n 的数组记录遍历过的元素是否存在,不利用额外空间的话,可在原数组进行操作,为了避免冲掉后续还未遍历的元素,通过对该元素取反的形式进行表示:对下标为 i 的元素取反,表示数组中已经包含 元素i+1,即该元素重复

public List<Integer> findDuplicates(int[] nums) {
        //数组元素均为正,且值在【1,n】之间,因此可利用长度为 n 的数组记录遍历过的元素是否存在,不利用额外空间的话,可在原数组进行操作,为了避免冲掉后续还未遍历的元素,通过对该元素取反的形式进行表示:对下标为 i 的元素取反,表示数组中包含 元素i+1
        List<Integer> result=new LinkedList<Integer>();
        for(int i=0;i<nums.length;i++){
            int num=Math.abs(nums[i]);
            if(nums[num-1]<0){
                result.add(num);
            }else{
                nums[num-1]=-nums[num-1];
            }
        }

        return result;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值