题目:数字在排序数组中出现的次数
程序运行结果:
统计一个数字在排序数组中出现的次数。例如输入排序数组{1,2,3,3,3,3,4,5}和数字3,则输出次数4.
算法分析:
既然输入的数组是排序的,那么我们很自然的想到利用二分查找算法。在题目给出的例子中,我们可以先用二分查找算法找到第一个3.由于3可能出现多次,因此我们找到的3的左右两遍可能都是3,于是我们在找到3的左右两边顺序扫描,分别找出第一个3和最后一个3.因为要查找的数字在长度为n的数组中可能很出现O(n)次,所以顺序扫描的时间复杂度为O(n)。因此这种算法的效率和直接从头到尾顺序扫描整个数组统计3出现的次数的方法是一样的。显然,面试官是不会满意这种算法,它会提示我们还有更快的算法。
接下来我们思考如何更好的利用二分查找算法。假设我们统计数字k在排序数组中出现的次数。在前面的算法的时间主要消耗在如何确定重复出现的第一个k和最后一个k的位置上,有没有可以利用的二分查找算法直接找到第一个k和最后一个k。
我们先分析如何利用二分查找在数组中找到第一个k,二分查找算法总是先拿数组的中间的数字和k做比较。如果中间的数字比k大,那么k只能出现在数组的前半段,下一轮我们旨在数组的前半段查找就可以了。如果中间的数字比k小,那么k只能出现在数组的后半段,下一轮我们只在数组的后半段查找就可以了。如果中间的数字和k相等呢?我们先判断这个数字是不是第一个k。如果位于中间数字的前面一个数字不是k,此时中间的数字刚好就是第一个k。如果中间的数字的前面一个数字也是k,也就是说第一个k肯定在数组的前半段,下一轮我们仍然需要在数组的前半段查找。
同理我们利用上面的思路找到最后一个k。
找到第一个k和最后一个k后就可以知道k出现的次数了,
算法源程序:
/**************************************************************
* Copyright (c) 2016,
* All rights reserved.
* 版 本 号:v1.0
* 题目描述:数字在排序数组中出现的次数
* 统计一个数字在排序数组中出现的次数。例如输入排序数组{1,2,3,3,3,3,4,5}和数字3,则输出次数4.
* 输入描述:请输入一个升序数组(以空格隔开):
* 1 2 3 3 3 3 5 7
* 请输入要查找的数字K:
* 3
* 程序输出:查找到的数字3一共出现的次数是:4
* 问题分析: 无
* 算法描述:利用二分查找在数组中找到第一个k,二分查找算法总是先拿数组的中间的数字和k做比较。如果中间的数字比k大,
* 那么k只能出现在数组的前半段,下一轮我们旨在数组的前半段查找就可以了。如果中间的数字比k小,
* 那么k只能出现在数组的后半段,下一轮我们只在数组的后半段查找就可以了。如果中间的数字和k相等呢?
* 我们先判断这个数字是不是第一个k。如果位于中间数字的前面一个数字不是k,此时中间的数字刚好就是第一个k。
* 如果中间的数字的前面一个数字也是k,也就是说第一个k肯定在数组的前半段,下一轮我们仍然需要在数组的前半段查找。
* 同理我们利用上面的思路找到最后一个k。
*
* 完成日期:2016-09-24
***************************************************************/
package org.marsguo.offerproject38;
import java.util.Scanner;
class CountNumberOfK{
private int getFirstK(int[] array,int length,int k,int start,int end){
if(start > end)
return -1;
int middleIndex = (start + end)/2;
int middleData = array[middleIndex];
/*
start始终为0,end不断前移或后移,直到找到第一个k
*/
if(middleData == k){ //中间数两侧都有要找的k
if((middleIndex > 0 && array[middleIndex - 1] != k) || middleIndex ==0)
return middleIndex;
else{
end = middleIndex - 1;
}
}
else if(middleData > k){ //若中间数大于K,则要找的k在中间数前面,
end = middleIndex - 1; //把中间数前面一个数字赋给end,从这里开始找
}
else{ //要找的k在中间数右边
start = middleIndex + 1;
}
return getFirstK(array, length, k, start, end);
}
private int getLastK(int[] array,int length,int k ,int start,int end){
if(start > end)
return -1;
int middleIndex = (start + end)/2;
int middleData = array[middleIndex];
/*
star不断前移或后移,直到找到第一个k
*/
if(middleData == k){
if((middleIndex < length - 1 && array[middleIndex + 1] != k)||
middleIndex == length - 1){
return middleIndex;
}
else{
start = middleIndex + 1;
}
}else if(middleData < k)
start = middleIndex + 1;
else
end = middleIndex - 1;
return getLastK(array, length, k, start, end);
}
public int getNumberOfK(int[] array,int length,int k){
int number = 0;
if(array != null && length > 0){
/*
first为第一个K所在位置,last为最后一个k所在位置
用last-first + 1即为k的个数
*/
int first = getFirstK(array, length, k, 0, length - 1);
int last = getLastK(array, length, k, 0, length - 1);
if(first > -1 && last > -1)
number = last - first + 1;
}
return number;
}
}
public class CountNumberMethod1 {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个升序数组(以空格隔开):");
String str = scanner.nextLine();
System.out.println("请输入要查找的数字K:");
int k = scanner.nextInt();
scanner.close();
String[] temp = str.split(" ");
int[] numarray = new int[temp.length];
for(int i = 0; i < temp.length; i++){
numarray[i] = Integer.parseInt(temp[i]);
}
CountNumberOfK countnumberofk = new CountNumberOfK();
System.out.println("查找到的数字" + k + "一共出现的次数是:");
System.out.println(countnumberofk.getNumberOfK(numarray, numarray.length, k));
}
}
程序运行结果: