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

示例 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. HTTPD之二————HTTPD服务详解————httpd的配置文件常见设置

    HTTPD之二----HTTPD服务详解----httpd的配置文件常见设置 HTTP服务器应用 http服务器程序 httpd apache nginx lighttpd 应用程序服务器 IIS,a ...

  2. Linux中mail的用法

    简介:mail命令是命令行的电子邮件发送和接收工具.操作的界面不像elm或pine那么容易使用,但功能非常完整Red Hat上sendmail服务一般是自动启动的.可以通过下面的命令查看sendmai ...

  3. RNA

    原始地球 你会想,我们每一个细胞中都有一个遗传分子叫做DNA?那么,DNA之前有没有什么遗传分子呢?我的答案是:"有".在远古地球,那个海底有无数火山,喷发的火山口两侧都是喷涌出的 ...

  4. Mysql 中字符串的截取

    一.从左开始截取字符串 用法:left(str, length),即:left(被截取字符串, 截取长度) mysql> SELECT LEFT('hello,world',3); +----- ...

  5. 通过ajax方式在界面上加载loading状态(仅作记录)

    1 html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, 2 pre, a, ab ...

  6. Shell常用工具find,grep,sed,awk,xargs命令

    最近学习shell命令,对grep,sed,awk命令有点混乱,故小结一下,巩固一遍. 注意:find , grep , sed, awk可使用基本正则表达式字符,find,grep,awk也支持扩展 ...

  7. XXE学习(待更新)

    XXE基础 XXE(XMl External Injection),即XML外部实体注入漏洞. XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体得加载,导致可以加载恶意外部文件,造成文件读取 ...

  8. kali2020安装中文界面

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

  9. 「NOIP2017」宝藏

    「NOIP2017」宝藏 题解 博客阅读效果更佳 又到了一年一度NOIPCSP-S 赛前复习做真题的时间 于是就遇上了这道题 首先观察数据范围 \(1 \le n \le 12\) ,那么极大可能性是 ...

  10. 网页如何嵌套网页__HTML框架

    通过使用html框架,可以在一个浏览器窗口中展示多个页面.也就是一个html文件中可以引入多个html文件.在网页中框架使用比较少,但我们还是需要了解下. 方式1:iframe 使用iframe标签来 ...