当前位置: 首页 > news >正文

0115 查找算法Day4

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

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

示例 1:

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

class Solution {
    public int findRepeatNumber(int[] nums) {

    }
}

解题思路

数组元素的 索引 和 值 是 一对多 的关系。
因此,可遍历数组并通过交换操作,使元素的 索引 与 值 一一对应(即 nums[i] = i )。因而,就能通过索引映射对应的值,起到与字典等价的作用。

遍历中,第一次遇到数字 x 时,将其交换至索引 x 处;而当第二次遇到数字 x 时,一定有 nums[x]=x ,此时即可得到一组重复数字。

算法流程:
遍历数组 nums ,设索引初始值为 i = 0 :

若 nums[i] =i : 说明此数字已在对应索引位置,无需交换,因此跳过;
若 nums[nums[i]] = nums[i] : 代表索引 nums[i]处和索引 i 处的元素值都为nums[i] ,即找到一组重复值,返回此值 nums[i] ;
否则: 交换索引为 i 和 nums[i] 的元素值,将此数字交换至对应索引位置。
若遍历完毕尚未返回,则返回 -1 。

 

 

 

 

 

 

 

 代码如下:

class Solution {
    public int findRepeatNumber(int[] nums) {
        int i = 0;
        while(i < nums.length) {
            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. 在排序数组中查找数字

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

示例 1:

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

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

class Solution {
    public int search(int[] nums, int target) {

    }
}

解题思路

算法解析:
初始化: 左边界 i = 0 ,右边界 j = len(nums) - 1 。
循环二分: 当闭区间 [i, j]无元素时跳出;
计算中点 m = (i + j) / 2(向下取整);
若 nums[m] < target ,则target 在闭区间[m+1,j] 中,因此执行 i = m + 1;
若 nums[m] > target ,则 target 在闭区间 [i, m - 1] 中,因此执行 j = m - 1;
若 nums[m] = target,则右边界right 在闭区间[m+1,j] 中;左边界left 在闭区间 [i,m−1] 中。因此分为以下两种情况:
若查找 右边界 right ,则执行 i = m + 1;(跳出时 i 指向右边界)
若查找 左边界 left ,则执行j=m−1 ;(跳出时 j 指向左边界)
返回值: 应用两次二分,分别查找right 和 left ,最终返回 right - left - 1即可

 

 

 

 

 

代码如下:

class Solution {
    public int search(int[] nums, int target) {
        // 搜索右边界 right
        int 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 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;
    }
}

以上代码显得比较臃肿(两轮二分查找代码冗余)。为简化代码,可将二分查找右边界 right 的代码 封装至函数 helper() 。 

 


class Solution {
    public int search(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. 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

class Solution {
    public int missingNumber(int[] nums) {

    }
}

解题思路

算法解析:
初始化: 左边界 i = 0,右边界j=len(nums)−1 ;代表闭区间[i,j] 。
循环二分: 当 i≤j 时循环 (即当闭区间 [i,j] 为空时跳出) ;
计算中点 m=(i+j)/2 ;
若 nums[m] = m ,则 “右子数组的首位元素” 一定在闭区间[m+1,j] 中,因此执行i=m+1;
若 nums[m] !=m ,则 “左子数组的末位元素” 一定在闭区间 [i,m−1] 中,因此执行j=m−1;
返回值: 跳出时,变量 i 和 j 分别指向 “右子数组的首位元素” 和 “左子数组的末位元素” 。因此返回 i 即可。

 

 

 

 

 

代码如下:


class Solution {
    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;
    }
}

 

 

相关文章:

  • 【Go】结构体中Tag标识
  • 数字图像处理——直方图的均衡化
  • 常用的8个应用和中间件的Docker运行示例
  • 高效批量管理文件,轻松实现文件批量复制并覆盖相同文件名,轻松管理文件
  • 事件穿透效果
  • 考研复试细胞生物学3.细胞骨架(交通网络)
  • npm install报错,常见的解决方案
  • C语言统计成绩
  • 【Git】window下大小写不敏感问题处理
  • 开源大模型LLM大爆发,数据竞赛已开启!如何使用FuseLLM实现大语言模型的知识融合?
  • 2月21日,每日信息差
  • C++之类作用域
  • HTML5期末大作业:基于HTML+CSS+JavaScript实现中国风文化传媒企业官网源码
  • 计算机导论第十一周课后作业
  • [附源码]计算机毕业设计线上社区管理系统Springboot程序
  • GIT分布式版本控制系统 | 命令讲解入门
  • CentOS下将 /home 目录合并到 / 目录
  • 「Redis数据结构」RedisObject
  • 微服务框架 SpringCloud微服务架构 10 使用Docker 10.7 数据卷命令
  • Web中的Bias(更新中)
  • 计算机毕业设计Java的自助旅游导航系统(源码+系统+mysql数据库+lw文档)
  • 【LIN总线测试】——LIN主节点物理层测试
  • 安卓属性动画
  • JS 的 apply 方法
  • 【前沿技术RPA】 一文了解UiPath Orchestrator的触发器和监听器
  • Java基于springboot+vue的游戏物品销售购物商城系统 前后端分离
  • HTML5期末大作业:美妆网页主题网站设计——清新的手工肥皂网站展示(4页)HTML+CSS+JavaScript
  • [附源码]Python计算机毕业设计Django三星小区车辆登记系统
  • 《MySQL实战45讲》学习笔记
  • 【网关路由测试】——网关状态转换测试
  • Mali GPU“补丁缺口”让 Android 用户容易受到攻击
  • (一)整合管理范围管理