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

示例 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. ES6深度解析3:Generators

    介绍ES6 Generators 什么是Generators(生成器函数)?让我们先来看看一个例子. function* quips(name) { yield "hello " ...

  2. HDU 4821 2013长春现场赛hash

    题意: 一个字符串S  问其中有几个子串能满足以下条件: 1.长度为M*L 2.可以被分成M个L长的小串  每个串都不一样 分析: hash方法,一个种子base,打表出nbase[i]表示base的 ...

  3. 使用命令行操作MySQL 及 语法

    在使用之前先要确保服务中的MySQL 已启动,否则会报错:ERROR 2003 (HY000): Can't connect to MySQL server on 'localhost' (10061 ...

  4. 谈谈Java事务

    事务具基本特征(ACID) ① Atomi(原子性):事务中包含的操作被看做一个整,要么完全部成功,要么全部失败. ② Consistency(一致性):事务在完成时,必须是所有的数据都保持一致状态, ...

  5. ESP32-简单OTA升级

    基于ESP-IDF4.1 1 #include "freertos/FreeRTOS.h" 2 #include "freertos/task.h" 3 #in ...

  6. XSS一些总结

    XSS一些总结 除了script以外大多标签自动加载触发JS代码大多用的都是on事件,以下标签都可以用下面的方法去打Cookie以及url等 常见标签 <img><input> ...

  7. Django基础08篇 filter&tag

    1.Django自带的过滤器filter views.py中代码 def template_tags(request): import datetime content = '三胖content三胖c ...

  8. 家庭账本开发day01

    --好长时间没有进行web应用的开发,手有些生疏了,知识点也有些遗忘了,不过抹油关系,边开发边复习边学习新的东西. 今天主要完成了,家庭记账本的项目梳理,如下: 利用layUI模板 修改layUImi ...

  9. 认识vue-cli脚手架

    ps:脚手架系列主要记录我自己(一名前端小白)对脚手架学习的一个过程,如有不对请帮忙指点一二! [抱拳] 作为一名前端开发工程师,平时开发项目大多都离不开一个重要的工具,那就是脚手架.下面让我们来了解 ...

  10. springMVC-7-数据处理转换

    我们为什么要对数据进行处理? 需求:在上个crud中我们如果需要每次修改的时候都要把时间也记录下来 解决:在jsp中新增一个input,在employee中新增一个Data字段 问题:input输出来 ...