【刷题】剑指Offer

感想:有序数组的搜索问题优先考虑二分法

03. 数组中重复的数字

难度:简单

找出数组中重复的数字。

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

看到不重复,想到HashSet,利用hashset元素不重复的特性,找出重复数字,但其实用Map也是一样。

class Solution {
    public int findRepeatNumber(int[] nums) {
        Set<Integer> hashSet = new HashSet<>();
        for(int c:nums){
            if(hashSet.contains(c)){
                return c;
            }else {
                hashSet.add(c);
            }
        }
        return 0;
    }
}

执行效率不高,但回过头想,可以将数组中的数看成一个个下标,利用新数组对应下标中存放的数来确定数字是否重复。

class Solution {
    public int findRepeatNumber(int[] nums) {
        int[] map = new int[nums.length];
        for(int n:nums){
            if(map[n]==0){
                map[n] = -1;
            }else{
                return n;
            }
        }
        return 0;
    }
}
53 - I. 在排序数组中查找数字 I

难度:简单

统计一个数字在排序数组中出现的次数。

嗯…看到这个题直接用Map重拳出击了,看了看评论区原来考的是二分法…

Map写法过是过了,但时间效率很慢。优化后的Map写法也在其中。

class Solution {
    public int search(int[] nums, int target) {
        Map<Integer,Integer> map = new HashMap<>();
        map.put(target,0);
        for(int n : nums){
            if(!map.containsKey(n)){
                map.put(n,1);
            }else{
                map.put(n,map.get(n)+1);
            }
        }
        return map.get(target)==0?0:map.get(target);
    }
}

//优化的Map
class Solution {
    public int search(int[] nums, int target) {
        Map<Integer,Integer> map = new HashMap<>();
        for(int n : nums){
            if(n == target){
                map.put(target,map.getOrDefault(target,0)+1);
            }
        }
        return map.getOrDefault(target,0);
    }
}   

我写出的二分法是,从两边分别找,找到target之后,再从mid出发,向前向后,在[low , mid],[mid , high]之间寻找有多少个target。因为数组有序,所以target必在这两个区间之内。(再想想就和大佬两次二分思路差不多了!!!)

class Solution {
    public int search(int[] nums, int target) {
        int low = 0, high = nums.length-1;
        while(low <= high){
            int mid = low+(high-low)/2;
            if(nums[mid] > target){
                high = mid-1;
            }else if(nums[mid] < target){
                low = mid + 1;
            }else{
                int count = 1;
                int temp = mid - 1;
                while(temp >= low && nums[temp] == target) {
                    count++;
                    temp--;
                }
                temp = mid + 1;
                while (temp <= high && nums[temp] == target) {
                    count++;
                    temp++;
                }
                return count;
            }
        }
        return 0;
    }
}

看了评论区,有大佬用了两次二分找到区间,直接计算得出个数。

53 - II. 0~n-1中缺失的数字

难度:简单

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

我…又创建了个数组,按下标写的…其实根本不用创建新数组,只需要比较下标与当前数字是否相同…其实考的又是二分…

class Solution {
   public int missingNumber(int[] nums) {
       int n = nums.length;
       int[] number = new int[n+1];
       for(int c:nums){
           number[c] = 1;
       }
       for(int i = 0;i < n+1;i++){
           if(number[i]==0){
               return i;
           }
       }
       return 0;
   }
}

//不创建新数组
class Solution {
   public int missingNumber(int[] nums) {
       if(nums[0] != 0) return 0;
       for(int i = 0;i < nums.length;i++){
           if(nums[i]!=i){
               return i;
           }
       }
       return nums.length;
   }
}
//二分
class Solution {
    public int missingNumber(int[] nums) {
        int n = nums.length;
        int low = 0, high = n - 1;
        while(low <= high){
            int mid = (low + high) / 2;
            if(nums[mid] == mid){
                low = mid + 1;
            }else {
                high = mid - 1;
            }
        }
        return low;
    }
}

还有更秀的,等差数组求和再减出差值。

class Solution {
    public int missingNumber(int[] nums) {
        int n = nums.length;
        int sum = (n+1)*n/2;
        for(int c:nums){
            sum -= c;
        }
        return sum;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值