剑指offer面试题38 数字在排序数组中出现的次数

解题思路:

如果用顺序查找,这道题的时间复杂度是O(n),肯定不符合面试官的要求。看到排序数组,并且在里面查找一个数出现的次数,很自然的可以想到利用二分查找,因此这道题是二分查找的一个应用。假设待查找元素为k,如果要是能找到第一个k出现的位置和最后一个k出现的位置,两者相减在加上1即为k出现的次数。

具体来说,现在可以先对整个数组进行一次二分查找,记中间元素的索引为midIndex,对应的元素为midData。若midData和k相等,则比较midIndex前面一个数即midIndex-1指向的元素是否是元素k,若不是,则证明此时的midIndex为k第一次出现的位置,若是,则对midIndex之前的元素进行一次查找第一次k出现的位置(递归调用),直到找到k第一次出现的位置。对于查找k最后一次出现的位置,思路与上面类似。对整个数组进行二分查找,比较midIndex后面一个数即midIndex+1指向的元素是否为k,若不是,则证明此时的midIndex为k最后一次出现的位置,若是,则对midIndex之后的元素进行一次查找最后一个k出现的位置(递归调用),直到找到k最后一次出现的位置。

public class Solution {

	public int GetNumberOfK(int[] array, int k) {
		
		int number = 0;
		
		if (array != null && array.length != 0) {
			int first = getFirstK(array, 0, array.length - 1, k);
			int last = getLastK(array, 0, array.length - 1, k);
			
			if (first > -1 && last > -1) {
				number = last - first + 1;
			}
		}
		
		return number;

	}
	
	//在数组中找到k出现的第一个位置
	public int getFirstK(int[] array, int start, int end, int k) {
		
		if (start > end) {
			//此时表明未找到元素k
			return -1;
		}
		
		int midIndex = (start + end) / 2;
		int midData = array[midIndex];
		
		//利用二分查找
		if (midData == k) {
			//当中间元素与待查找元素相等时,判断前面一个元素是不是和k相等
			if (midIndex > 0 && array[midIndex - 1] != k || midIndex == 0) {
				//如果前一个元素不等于k,则此时midIndex是k出现第一次的位置
				return midIndex;

			} else {
				//如果前一个元素等于k,则此时需要在midIndex前面继续寻找第一个k出现的位置
				end = midIndex - 1;
			}
		} else if (midData > k) {
			//如果中间元素比k大,则k只可能出现在中间元素的前面,则下一次在数组的左半部分继续寻找k
			end = midIndex - 1;
			
		} else {
			//如果中间元素比k小,则k只可能出现在中间元素的后面,则下一次在数组的右半部分继续寻找k
			start = midIndex + 1;
		}
		return getFirstK(array, start, end, k);
	}
	
	public int getLastK(int[] array, int start, int end, int k) {
		
		if (start > end) {
			//表明在数组中未找到元素k
			return -1;
		}
		
		int midIndex = (start + end) / 2;
		int midData = array[midIndex];
		
		//利用二分查找
		if (midData == k) {
			//当中间元素与待查找元素相等时,判断后面一个元素是不是和k相等
			if (midIndex < array.length - 1 && array[midIndex + 1] != k 
					|| midIndex == array.length - 1) {
				//如果前一个元素不等于k,则此时midIndex是k出现第一次的位置
				return midIndex;

			} else {
				//如果后一个元素等于k,则此时需要在midIndex后面继续寻找第一个k出现的位置
				start = midIndex + 1;
			}
		} else if (midData > k) {
			//如果中间元素比k大,则k只可能出现在中间元素的前面,则下一次在数组的左半部分继续寻找k
			end = midIndex - 1;
			
		} else {
			//如果中间元素比k小,则k只可能出现在中间元素的后面,则下一次在数组的右半部分继续寻找k
			start = midIndex + 1;
		}
		return getLastK(array, start, end, k);
	}
	
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值