Day37:数字在排序数组中出现的次数

时间:2022-07-24
本文章向大家介绍Day37:数字在排序数组中出现的次数,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

剑指Offer_编程题——数字在排序数组中出现的次数

题目描述:

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

具体要求:

时间限制: C/C++ 1秒,其他语言2秒 空间限制: C/C++32M,其他语言64M

具体思路:

1、背景知识介绍:   我们在做题之前首先介绍一种数据结构中常用的查找算法——二分查找。在维基百科中是这样介绍二分查找算法的:在计算机科学中,二分查找算(binary search algorithm),也称折半搜索算法(half-interval search algorithm)、对数搜索算法(logarithmic search algorithm),是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。   二分查找算法在情况下的复杂度是对数时间,进行O(logn)次近行比较。二分查找算法使用常数空间,无论对任何大小的输入数据,算法使用的空间都是一样的。除非输入数据数量很少,否则二分查找算法比线性搜索更快,但数组必须事先被排序。尽管特定的、为了快速搜索而设计的数据结构更有效(比如哈希表),二分查找算法应用面更广。二分查找算法有许多中变种。比如分散层叠可以提升在多个数组中对同一个数值的搜索。分散层叠有效的解决了计算几何学和其他领域的许多搜索问题。指数搜索将二分查找算法拓宽到无边界的列表。二叉搜索树和B树数据结构就是基于二分查找算法的。   二分搜索只对有序数组有效。二分搜索先比较数组中比特素和目标值。如果目标值与中比特素相等,则返回其在数组中的位置;如果目标值小于中比特素,则搜索继续在前半部分的数组中进行。如果目标值大于中比特素,则搜索继续在数组上部分进行。由此,算法每次排除掉至少一半的待查数组。首先给定一个包含n个带值元素数组A或者记录A0,A1,……An-1,使得A0<=A1<=,……,<=An-1,以及目标值T,还有下列用来查找T在 A中的位置的子程序:具体算法步骤如下: 1、令L为0,R为n-1. 2、如果L>R,z则查找以失败告终 3、令m为ceil((L+R)/2) 4、如果Am < T,令L为m+1,并返回步骤二 5、如果Am > T,令R为m-1,并返回步骤二 6、如果Am=T,查找结束,返回值m。   这个迭代步骤会持续透过两个变量追踪搜索的边界。有些实际应用会在算法的最后放入相等比较,让比较循环更快,但平均而言会多一层迭代。以上程序只适用于完全匹配,也就是查找一个目标值的位置。不过,因为有序数组的顺序性,将二分搜索算法扩展到能适用大致匹配并不是很重要。举例来说,二分搜索算法可以用来计算一个赋值的排名(或称,比它更小的元素的数量)、前趋(下一个最小元素)、后继(下一个最大元素)以及最近邻。查找两个值之间的元素数目的范围查询可以借由两个排名查询(又称秩查询)来运行。 a、排名查询可以使用调整版的二分搜索来运行。借由在成功的搜索回传m,以及在失败的搜索回传L,就会取而代之地回传了比起目标值小的元素数目。 b、前趋和后继查询可以借由排名查询来运行。一旦知道目标值的排名,其前趋就会是那个位于其排名位置的元素,或者排名位置的上一个元素(因为它是小于目标值的最大元素)。其后继是(数组中的)下一个元素,或是(非数组中的)前趋的下一个元素。目标值的最近邻可能是前趋或后继,取决于何者较为接近。 c、范围查询也是直接了当的。一旦知道两个值的排名,不小于第一个值且小于第二个值的元素数量就会是两者排名的差。这个值可以根据范围的端点是否算在范围内,或是数组是否包含其端点的对应键来增加或减少1。折半搜索每次把搜索区域减少一半,时间复杂度为O(n),空间复杂度为O(1)。以下就是二分查找算法查找过程:

  我们在通过动态图来看一个案例,来进一步理解二分查找的过程。

  其静态图如图所示:

2、具体实现 思路一:   根据我们题意可知,该数组是一个基本有序的序列,在根据刚才给大家介绍的二分查找的特征,可见本题最佳选择就是二分查找算法。具体我们可以利用二分查找找到一个数值可能在该数组中的第一个最合适的位置,然后判断该位置上元素是否是是该元素,如果不是,说明这个数组中不存在这一元素,返回0即可。如果该位置上为该元素,那么这个位置就是该元素在数组中存在的第一个位置,同理可求出k+1的合适位置(k+1可能存在,也可能不存在于这个数组中),两个位置只差即为k出现的次数。我们接下来分别用java和python将其实现。 1、首先我们用Java实现:

public class Solution{
	public int GetNumberOfK(int[] array, int k){
		if(array == null || array.length == 0)
			return 0;
		if(k < array[0])
			return 0;
		int first = binarySearch(array, k);
		int last = binarySearch(array, k + 1);
		return (first == array.length || array[first] != k)?0:last-first;
	}
	public static int binarySearch(int []nums, int k){
		int left = 0, right = nums.length;
		while(left < right){
			int mid = left + (right - left) / 2;
			if(nums[mid] >= k){
				right = mid;
			}else{
				left = mid + 1;
			}
		}
		return left;
	}
}

代码效果图如图所示:

2、接下来用Python将其实现:

class Solution:
	def GetNumberOfK(self, data, k):
		left = 0
		right = len(data) - 1
		leftK = self.getleftK(data, k, left, right)
		rightK = self.getrightK(data, k, left, right)
		return rightK - leftK + 1
	def getleftK(self, data, k, left, right):
		while left <= right:
			middle = (left + right) // 2
			if data[middle] < k:
				left = middle + 1
			else:
				right = middle - 1
		return left
	def getrightK(self, data, k, left, right):
		while left <= right:
			middle = (left + right) // 2
			if data[middle] <= k:
				left = middle + 1
			else:
				right = middle - 1
		return right

代码效果图如图所示:

思路二   除了以上的二分查找,我们还可以用java中的HashMap来解决该题。这种方法方法即使不是排序数组也可以使用。具体用java实现如下:

import java.util.HashMap;
public class Solution{
	public int GetNumberOfK(int[] array, int k){
		if(array == null || array.length == 0)
			return 0;
		HashMap<Integer, Integer>map = new HashMap<>();
		int result = 0;
		for(int i = 0; i < array.length; i++){
			int a = array[i];
			if(map.containsKey(a)){
				map.put(a, map.get(a) + 1);
			}else{
				map.put(a, 1);
			}
		}
		if(map.get(k) != null)
			result = map.get(k);
		return result;
	}
}

代码效果图如图所示:

  接下来给大家介绍两种及其简单的解法来解决此题,首先是直接利用Python中的count来直接计数,一行代码即可实现。第二种方法就是暴力法,遍历整个数组,用一个数字和这个数组的元素相比较,如果相等就加1,最后返回这个数即可。具体实现如下:

class Solution:
	def GetNumberOfK(self, data, k):
		return data.count(k)

代码效果图如图所示:

  第二种方法用python实现:

class Solution:
	def GetNumberOfK(self, data, k):
		num = 0 
		for i in data:
			if i == k:
				num += 1
		return num

代码效果图如图所示:

总结

  本道题主要考察数字在数组排序后出现的次数,由于本题很明显有排好序的数组,因此很显然是用到的是二分查找算法,因此在文章开头给大家详细介绍了二分查找,并且我们分别用java和python将其实现,随后我们有用Java中的HashMap容器来解决此题,发现时间复杂度也就O(n),空间复杂度也是O(1),最后我们介绍了两种及其简单的方法,只需要用三四行或者仅仅需要一行代码即可实现。因此,我们在做题的时候,应该多次尝试各种方法,扩展自己的思维,写出优质的代码。总之,我们要继续加油,争取早日找到工作,Good Luck!!!

参考文献

[1] 二分查找 [2] 夏臻Rock [3] jiayk2016