面试题整理11 数字在排序数组中出现的次数

本文探讨了在排序数组中统计特定数字出现次数的问题。通过两种不同的二分查找方法对比,一种是直接递归查找所有匹配项,另一种是分别找出目标数字首次和末次出现的位置。后者在大多数情况下更高效。

《剑指offer》面试题38:

题目:统计一个数字在排序数组中出现的次数。例如输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于3在此数组中出现了4次,因此输出4。

分析:看到排序数组想到二分法解决问题,假设输入数字为k,当中间数大于k时,在前部分查找;当中间数小于k时在后部分查找;当中间数等于k时,前部分和后部分都有可能。

         此题之所以记录是因为关键在于中间数等于k时的处理方法的不同和效率的差异比较。

(1)自己写的代码,当中间数等于k时,递归调用前部分、后部分,前面部分k出现的次数+后面部分k出现的次数+1。

int GetNumberOfK1(int* data, int length, int k)
{
	if( data == NULL || length <= 0 )
		return 0;
	return FindKTimes(data,0,length-1,k);
}
int FindKTimes( int *data, int start, int end,int k)
{
	if( start > end || data[start] > k || data[end] < k )
	{
		return 0;
	}
	if( start == end )
	{
		if( data[start] == k )
		{
			return 1;
		}else
		{
			return 0;
		}
	}
	int middle = (start + end)/2;
	if(data[middle] > k)
	{
		return FindKTimes(data,start,middle-1,k);
	}else if( data[middle] < k )
	{
		return FindKTimes(data,middle+1,end,k);
	}else
	{
		return FindKTimes(data,start,middle-1,k)+ FindKTimes(data,middle+1,end,k)+1;
	}
}

(2)《剑指offer》中代码,采取二分法分别求k第一次和最后一次出现的位置,两者相减+1为总次数。

        此种方法相比于自己写的代码的优势在于,当k出现次数多时,递归次数少,极限整个数组都是k时,比(1)效率高很多;当然k没有出现或者出现一两次时递归次数相仿。在求第一次出现时,当中间数等于k时,只递归前面部分;求最后一次时,当中间数等于k时,只求后面部分。

应采用此种方法。但是示例代码中有一点不好,先求第一次出现,接着求最后一次出现,然后再进行判断,这样当第一次返回-1时还需要再求第二遍,下面将判断顺序改了。

int GetNumberOfK(int* data, int length, int k)
{
    int number = 0;

	/*if(data != NULL && length > 0)
	{
		int first = GetFirstK(data, length, k, 0, length - 1);
		int last = GetLastK(data, length, k, 0, length - 1);

		if(first > -1 && last > -1)
			number = last - first + 1;
	}*/
	
    if(data != NULL && length > 0)
    {
        int first = GetFirstK(data, length, k, 0, length - 1);
		if( first > -1)
		{
			int last = GetLastK(data, length, k, 0, length - 1);
        
			if(last > -1)
				number = last - first + 1;
		}
    }

    return number;
}

// 找到数组中第一个k的下标。如果数组中不存在k,返回-1
int GetFirstK(int* data, int length, int k, int start, int end)
{
    if(start > end )
        return -1;
	
    int middleIndex = (start + end) / 2;
    int middleData = data[middleIndex];
	
    if(middleData == k)
    {
        if((middleIndex > 0 && data[middleIndex - 1] != k) 
            || middleIndex == 0)
            return middleIndex;
        else
		  if( middleIndex == start)
			  return middleIndex;
           end  = middleIndex - 1;
    }
    else if(middleData > k)
        end = middleIndex - 1;
    else
        start = middleIndex + 1;

    return GetFirstK(data, length, k, start, end);
}

// 找到数组中最后一个k的下标。如果数组中不存在k,返回-1
int GetLastK(int* data, int length, int k, int start, int end)
{
    if(start > end)
        return -1;

    int middleIndex = (start + end) / 2;
    int middleData = data[middleIndex];

    if(middleData == k)
    {
        if((middleIndex < length - 1 && data[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(data, length, k, start, end);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值