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

示例 1:

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

示例 2:

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

限制:

0 <= 数组长度 <= 50000

排序数组中的搜索问题,首先想到 二分法 解决。

使用遍历数组再 count++ 的话,时间复杂度是 O(n)

但是使用二分法,使时间复杂度降低到 O(log n)

自己第一次提交时的代码:

class Solution {
public int search(int[] nums, int target) {
int count = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == target) {
count++;
}
}
return count;
}
}

使用非常粗暴的循环求个数,时间非常慢。看题解知道:

排序数组中的搜索问题,首先想到 二分法 解决。

二分查找法(时间复杂度:O(log n)):

二分查找要求线性表必须顺序存储,并且元素是有序排列

二分查找(折半查找)的基本思想:减少查找序列的长度,分而治之地进行关键字的查找。他的查找过程是:先确定待查找记录的所在的范围,然后逐渐缩小查找的范围,直至找到该记录为止(也可能查找失败)。

int binarySerch(vector<int> & nums,int low,int hi,int target)
{
int len = nums.size();
if(low<0 || low > len-1 || hi < 0 || hi > len-1 || 0 == len)
{
return -1;
} while(low <= hi) //注意这里一定要加上=
{
int mid = low + (hi -low)/2;
if(target == nums[mid])
return mid;
else if(target > nums[mid])
low = mid+1;
else
hi = mid-1;
} return -1; }

Notes:

线性表与非线性表的区别:

(1) 非线性表:二维数组、多维数组、广义表、树、图

(2) 线性表:

① 顺序存储结构(顺序表):数组、队列、栈

② 链式存储结构(链表):链表

顺序存储结构和链式存储结构的区别:

(1) 链式存储结构的内存地址不一定连续,但顺序存储结构的内存地址一定连续

(2)链式存储适用于在较频繁的插入、删除、更新元素时,而顺序存储结构适用于频繁查询时使用

顺序存储结构和链式存储结构的优缺点:

(1)空间上:顺序比链式节约空间。因为链式结构每一个节点都有一个指针存储域。

(2)存储操作上:顺序支持随机存取,方便操作

(3)插入和删除上:链式比顺序方便。因为顺序表的插入要执行更大的空间复杂度,包括一个从表头索引以及索引后的元素后移。

总结:

查询频繁 + 存储量固定 = 顺序结构

插入删除频繁 + 存储量不固定 = 链式结构

二分法解法1:

将已排序的数组记左右边界的索引为 left / right。本题要求统计数字 target 的出现次数,可以转化为:使用二分法找出有序数组中出现该 target 的左边界和右边界,可以得出 target 的数量为 right - left - 1

算法解析:

初始化:左边界 i = 0,右边界 j = nums.length - 1。

循环二分:

  1. 计算中点: mid = i + (j - i) / 2;     ----> 防止数值溢出
  2. 若 nums[mid] < target,则 target 在闭区间 [mid + 1, right]中,执行 i = m + 1;
  3. 若 nums[mid] > target,则 target 在闭区间 [left, mid - 1]中,执行 j = m - 1;
  4. 若 nums[mid] = target,则 left(左边界) 在闭区间 [i, mid - 1] 中,right(右边界) 在闭区间  [mid + 1, j] 中。需要分一下两种情况:
    1. 若查找 left,执行 i = m - 1,当 nums[mid] != target 时跳出,此时 j 指向 left;
    2. 若查找 right,执行 j = m + 1,当 nums[mid] != target 时跳出,此时 i 指向 right;

返回值:找出 left 和 right 之后,最终返回 right - left - 1

代码

class Solution {
public int search(int[] nums, int target) {
int i = 0;
int j = nums.length - 1; while (i <= j) {
int mid = i + (j - i) / 2;
if (nums[mid] <= target) {
i = mid + 1;
} else if (nums[mid] > target) {
j = mid - 1;
}
} int right = i;
if (j >= 0 && nums[j] != target) {
return 0;
}
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 的代码封装起来,找左边界的时候只需找数组中比 target 小且最邻近的数字的右边界,最后将两结果相减并返回即可

② 将二分查找左边界 left 的代码封装起来,找右边界的时候只需找数组中比 target 大且最邻近的数字的左边界,最后将两结果相减并返回即可

代码:

class Solution {
private int rightLoc(int[] nums, int tar) {
int i = 0;
int j = nums.length - 1;
while(i <= j) {
int m = i + (j - i) / 2;
if(nums[m] <= tar) {
i = m + 1;
} else {
j = m - 1;
}
}
return i;
}
public int search(int[] nums, int target) {
return rightLoc(nums, target) - rightLoc(nums, target - 1);
}
}

本质上看, helper() 函数旨在查找数字 tar 在数组 nums 中的 插入点 ,且若数组中存在值相同的元素,则插入到这些元素的右边。

提交结果:

二分法解法2:

因为数组是已经排序好的数组,故如果直接找出来 nums[mid] = target,则通过左右边界点的值和 target 做判断,当:

左边界点 < target 时,左边界点右移

右边界点 > target 时,右边界点左移

移动左右边界点,直到左右边界点对应的值相等,最后返回 right - left + 1 即可。

代码:

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

提交结果:

 

4 剑指Offer53-在排序数组中查找数字的更多相关文章

  1. [剑指Offer]53-在排序数组中查找数字(二分查找)

    题目一 数字在排序数组中出现的个数 题目描述 统计一个数字在排序数组中出现的次数. 解决思路 写两个二分查找分别找第一个和最后一个该数字,然后可直接出计算有几个该数字.时间复杂度为O(logn). 这 ...

  2. [简单-剑指 Offer 53 - I. 在排序数组中查找数字 I]

    [简单-剑指 Offer 53 - I. 在排序数组中查找数字 I] 统计一个数字在排序数组中出现的次数. 示例 1: 输入: nums = [5,7,7,8,8,10], target = 8 输出 ...

  3. 剑指 Offer 53 - I. 在排序数组中查找数字 I + 二分法

    剑指 Offer 53 - I. 在排序数组中查找数字 I Offer_53_1 题目描述 方法一:使用HashMap package com.walegarrett.offer; /** * @Au ...

  4. 力扣 - 剑指 Offer 53 - I. 在排序数组中查找数字 I

    题目 剑指 Offer 53 - I. 在排序数组中查找数字 I 思路1 一般来说,首先想到的是使用一个变量,从头开始遍历整个数组,记录target数组出现的次数,但是这样的时间复杂度是O(n),还是 ...

  5. 每日一题 - 剑指 Offer 53 - I. 在排序数组中查找数字 I

    题目信息 时间: 2019-07-04 题目链接:Leetcode tag:二分查找 哈希表 难易程度:简单 题目描述: 统计一个数字在排序数组中出现的次数. 示例1: 输入: nums = [5,7 ...

  6. [LeetCode]面试题53 - I. 在排序数组中查找数字 I(二分);面试题53 - II. 0~n-1中缺失的数字(二分)

    ##面试题53 - I. 在排序数组中查找数字 I ###题目 统计一个数字在排序数组中出现的次数. 示例 1: 输入: nums = [5,7,7,8,8,10], target = 8 输出: 2 ...

  7. 【Java实现】剑指offer53.1——在排序数组中查找数字(LeetCode34:在排序数组中查找元素的起始位置)

    序数组中查找元素的起始位置):思路分享 <剑指offer>题目和LeetCode主站本质是一样的,想要找到target数目,也需要找到左右边界 题目解析: 在一个排序数组中,找到targe ...

  8. 剑指offer——56在排序数组中查找数字

    题目描述 统计一个数字在排序数组中出现的次数.   题解: 使用二分法找到数k然后向前找到第一个k,向后找到最后一个k,即可知道有几个k了 但一旦n个数都是k时,这个方法跟从头遍历没区别,都是O(N) ...

  9. 剑指offer-面试题53_1-在排序数组中查找数字-二分查找

    /* 题目: 统计一个数字在排序数组中出现的次数. */ /* 思路: 1.从前往后遍历,时间复杂度O(n). 2.二分查找到目标数字target,向前向后遍历,时间复杂度O(n). 3.利用二分法, ...

  10. 剑指 Offer 53 - I. 在排序数组中查找数字 I

    题目描述 统计一个数字在排序数组中出现的次数. 示例1: 输入: nums = [5,7,7,8,8,10], target = 8 输出: 2 示例2: 输入: nums = [5,7,7,8,8, ...

随机推荐

  1. 基于C#的socket编程的TCP同步实现

    该博客源著地址https://www.cnblogs.com/sunev/archive/2012/08/05/2604189.html 一.摘要 总结一下基于C#的TCP传输协议的涉及到的常用方法及 ...

  2. 11、gitlab和Jenkins整合(2)

    5.补充: (1)构建说明: 1)Jenkins会基于一些处理器任务后,构建发布一个稳健指数 (从0-100 ),这些任务一般以插件的方式实现. 2)它们可能包括单元测试(JUnit).覆盖率(Cob ...

  3. noi 162 post office dp

    大致题意: 有v个村庄,每个村庄有各自的位置,且每个位置互不相同.现在要在村庄上设立P个邮局,使每个村庄到最近的邮局的距离之和最小. 分析: 定义状态d[i][j]表示前i个村庄,在这i个村庄中设立j ...

  4. spring、springmvc、springboot、springcloud的联系与区别

    spring和springMvc: 1. spring是一个一站式的轻量级的java开发框架,核心是控制反转(IOC)和面向切面(AOP),针对于开发的WEB层(springMvc).业务层(Ioc) ...

  5. varnish配置语言(2)

    目录 1. Backend servers 2. 多个后端 3. Varnish 中的后端服务器和虚拟主机 4. 调度器 5. 健康检查 6. Hashing 7. 优雅模式 Grace mode 和 ...

  6. homestead

    前言 之前写过一篇文章(https://www.jianshu.com/p/5f30280a3c18),说不需要这玩意儿一样可以开发.是的,但是对于团队来说,使用统一的环境.开发工具.编码规范等,对于 ...

  7. kali2020安装中文界面

    1.安装中文字体:apt-get install xfonts-intl-chinese ttf-wqy-microhei 2.设置系统语言:dpkg-reconfigure locales 3.选择 ...

  8. Java基础00-基础语法3

    1. 注释 1.1 注释概述 1.2 注释分类 1.3 示例 2. 关键字 2.1 关键字概述 2.2 关键字的特点 3. 常量 3.1 常量的概述 3.2 常量分类 以上常量除了空常量都是可以直接输 ...

  9. hadoop源码_hdfs启动流程_3_心跳机制

    hadoop在启动namenode和datanode之后,两者之间是如何联动了?datanode如何向namenode注册?如何汇报数据?namenode又如何向datanode发送命令? 心跳机制基 ...

  10. Java基础之反射生成JDK动态代理

    在Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口.通过这个类和接口可以生成JDK动态代理类或动态代理对象. JDK动态代理例子: / ...