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

示例 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. oracle 日常运维

    1.查询表或存储过程.函数异常 select * from user_errors where name ='TEST_TABLE' 2.查询表是否存在 select * from user_tabl ...

  2. Docker中容器的备份和恢复(可迁移)

    官方文档 备份容器 - save 查看镜像$ docker images 容器快照 - commit$ docker commit CONTAINER xxx/exampleimage-backup: ...

  3. flink数据广播场景总结

    数据集广播,主要分为广播变量,广播维表(数据集)两种,一种为变量,一种为常量(抽象的说法): 一.数据广播背景 对于小变量,小数据集,需要和大数据集,大流进行联合计算的时候,往往把小数据集广播出去,整 ...

  4. docker 创建各容器命令合集

    学习中...持续更新 docker-compose安装: curl -L https://github.com/docker/compose/releases/download/1.27.4/dock ...

  5. [转载] 笑话:Developer and product manager

    A man flying in a hot air balloon suddenly realizes he's lost. He reduces height and spots a man dow ...

  6. WPF教程十:如何使用Style和Behavior在WPF中规范视觉样式

    在使用WPF编写客户端代码时,我们会在VM下解耦业务逻辑,而剩下与功能无关的内容比如动画.视觉效果,布局切换等等在数量和复杂性上都超过了业务代码.而如何更好的简化这些编码,WPF设计人员使用了Styl ...

  7. python使用笔记004-冒泡排序

    冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法. 它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小.首字母从Z到A)错误就把他们交换过来.走访元素 ...

  8. C语言:位运算符总结

    位运算符:1.指对操作数以二进制位( bit)为单位进行的数据处理2.每一个二进制位只存放0或13. 取反:~  按位反 ~ 0变1 1变0 ~1=0 ~0=14.异或: ^ 相同为0,不相同为1 1 ...

  9. C语言:if条件写法

    if 语句的判断条件中不是必须要包含关系运算符,它可以是赋值表达式,甚至也可以是一个变量,常量 例如: //情况① if(b){ //TODO: } //情况② if(b=5){ //情况① //TO ...

  10. C语言变量 类型判断

    变量三要素: 一个变量有三个基本的要素,变量的名称,变量的类型,变量的值.所以int a = 10; 变量名为a,变量的存储类型为int型,变量的值为10. 变量还有一些属性如作用范围和存储类型. 变 ...