乱序版 ● 剑指offer每日算法题打卡题解—— 查找算法 (题号3,4,11,53)

Posted 寂静花开

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了乱序版 ● 剑指offer每日算法题打卡题解—— 查找算法 (题号3,4,11,53)相关的知识,希望对你有一定的参考价值。

打卡day2
这次题是读懂了,然而依然做不对。。。
/(ㄒoㄒ)/~~

第一题:剑指 Offer 03. 数组中重复的数字

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

示例 1:

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

限制: 2 <= n <= 100000

题目理解:
但达到找出重复数字的目的可通过哈希表来实现。但此时题目说明尚未被充分使用。
题中还说长度为 n 的数组 nums 里的所有数字都在 0 ~ n-1 的范围内 。 说明:数组元素的 索引 和 值 是 一对多 的关系
因此,可遍历数组并通过交换操作,使元素的 索引 与 值 一一对应(即 nums[i] = inums[i]=i )。因而,就能通过索引映射对应的值,起到与字典等价的作用。

解题思路
所以有两种方法可以解题,
一种是哈希表,一种是原地交换

原地交换思想 : 一个萝卜一个坑
1 . 所谓原地交换,就是如果发现这个坑里面的萝卜不是这个坑应该有的萝卜,就看看你是哪家的萝卜,然后把你送到哪家,再把你家里现在那个萝卜拿回来。
2 . 拿回来之后,再看看拿回来的这个萝卜应该是属于哪家的,再跟哪家交换。
3 . 就这样交换萝卜,直到想把这个萝卜送回它家的时候,发现人家家里已经有了这个萝卜了(双胞胎啊),那这个萝卜就多余了,就把这个萝卜上交给国家。

java代码

	//法一:哈希表
    public int findRepeatNumber(int[] nums) {
        Set<Integer> dic = new HashSet<>();//创建HashSet
        for(int num : nums) {//遍历数组nums中的每个数字num
            if(dic.contains(num)) //当num在Hashset中
            	return num;
            dic.add(num);//若不在,则加入HashSet中
        }
        return -1;
    }
    
    //法二:原地交换
    public int findRepeatNumber2(int[] nums) {
        int i = 0;
        while(i < nums.length) {//遍历数组nums
            if(nums[i] == i) {//当数字和下标相等时,说明数字无需交换
                i++;
                continue;//跳过本次循环
            }
            if(nums[nums[i]] == nums[i]) {//此时证明找到重复值
            	return nums[i];
            }
            //两种情况都不是的话,进行交换
            int tmp = nums[i];
            nums[i] = nums[tmp];
            nums[tmp] = tmp;
        }
        return -1;
    }


第二题:剑指 Offer 53 - I. 在排序数组中查找数字 I

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

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2

示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof

解题思路
排序数组中的搜索问题,首先想到 二分法 解决。
二分法模板:三道题学会二分查找——java、python、c三种语言解题( ̄▽ ̄)~*
java代码

    public int search(int[] nums, int target) {
        // 搜索右边界 right
        int i = 0, j = nums.length - 1;//定义左边界为i,右边界为j
        while(i <= j) {//开始二分法循环,数组中无元素时跳出
            int m = (i + j) / 2;//计算终点
            if(nums[m] <= target) {
            	i = m + 1;
            }
            else j = m - 1;
        }
        int right = i;//更新右边界
        // 若数组中无 target ,则提前返回
        if(j >= 0 && nums[j] != target) {
        	return 0;
        }
        // 搜索左边界 right
        i = 0; j = nums.length - 1;//初始化左右边界
        while(i <= j) {//开始第二次二分
            int m = (i + j) / 2;
            if(nums[m] < target) {
            	i = m + 1;
            }
            else j = m - 1;
        }
        int left = j;//更新左边界
        return right - left - 1;
    }
    
    //法二:将二分查找右边界的代码封装到函数helper()里
    public int search2(int[] nums, int target) {
        return helper(nums, target) - helper(nums, target - 1);
    }
    int helper(int[] nums, int tar) {
        int i = 0, j = nums.length - 1;
        while(i <= j) {
            int m = (i + j) / 2;
            if(nums[m] <= tar) i = m + 1;
            else j = m - 1;
        }
        return i;
    }


第三题:剑指 Offer 53 - II. 0~n-1中缺失的数字

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

示例 1:

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

示例 2:

输入: [0,1,2,3,4,5,6,7,9]
输出: 8

限制:

1 <= 数组长度 <= 10000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/que-shi-de-shu-zi-lcof

解题思路
依旧是排序数组中的搜索问题,依然用二分法。

java代码

    public int missingNumber(int[] nums) {
        int i = 0, j = nums.length - 1;//定义左右边界
        while(i <= j) {//开始二分循环
            int m = (i + j) / 2;//计算终点
            if(nums[m] == m) //当下标和值相等时
            	i = m + 1;//取右半数组,左边界加一
            else j = m - 1;
        }
        return i;
    }

第四题:剑指 Offer 04. 二维数组中的查找

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

示例:

现有矩阵 matrix 如下:

[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。

给定 target = 20,返回 false。

限制:

0 <= n <= 1000

0 <= m <= 1000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof

解题思路
要利用数组 从上到下递增、从左到右递增 的特点。
画图,将矩阵逆时针旋转 45° ,可以观察发现其类似于 二叉搜索树 ,即对于每个元素,其左分支元素更小、右分支元素更大。因此,通过从 “根节点” 开始搜索,遇到比 target 大的元素就向左,反之向右,即可找到目标值 target 。这里建议去看力扣大佬的题解

二叉搜索树的节点放置规则是:任何节点的键值一定大于去其左子树中的每一个节点的键值,并小于其右子树的每一个节点的键值。

java代码

    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        int i = matrix.length - 1, j = 0;//索引设为(i,j)
        while(i >= 0 && j < matrix[0].length)//从矩阵左下角元素开始遍历
        {
            if(matrix[i][j] > target) i--;//消去第i行元素
            else if(matrix[i][j] < target) j++;//消去第j列元素
            else return true;//找到目标值
        }
        return false;
    }

第五题:剑指 Offer 11. 旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。

示例 1:

输入:[3,4,5,1,2]
输出:1

示例 2:

输入:[2,2,2,0,1]
输出:0

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof

题目理解:
观察示例,可以发现,数组的最小元素是旋转后数组的非降序排列中的首个降序,也是旋转数组的旋转点。

解题思路
排序数组的查找问题首先考虑使用 二分法 解决,二分法可以降低时间复杂度。

java代码

    public int minArray(int[] numbers) {
        int i = 0, j = numbers.length - 1;//定义左右边界
        while (i < j) {//进行二分循环。注意:相等时也跳出循环
            int m = (i + j) / 2;
            if (numbers[m] > numbers[j]) {
            	i = m + 1;
            }
            else if (numbers[m] < numbers[j]) {
            	j = m;
            }
            else j--;
        }
        return numbers[i];
    }

以上是关于乱序版 ● 剑指offer每日算法题打卡题解—— 查找算法 (题号3,4,11,53)的主要内容,如果未能解决你的问题,请参考以下文章

乱序版 ● 剑指offer每日算法题打卡题解—— 查找算法 (题号3,4,11,53)

乱序版 ● 剑指offer每日算法题打卡题解——分治算法(题号17,14)

乱序版 ● 剑指offer每日算法题打卡题解——数学 (题号39,66)

乱序版 ● 剑指offer每日算法题打卡题解——动态规划(题号19,49,60)

乱序版 ● 剑指offer每日算法题打卡题解——动态规划 (题号10,63)

乱序版 ● 剑指offer每日算法题打卡题解——栈与队列(题号59)