剑指offer--统计一个数字在排序数组中出现的次数

题目描述

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

class Solution {

public:

int GetNumberOfK(vector<int> data, int k) {

int length = data.size();//求数组长度

int i = 0;

int count = 0;

while (i < length)

{

if (data[i] == k)

{

count++;

}

i++;

}

return count;



}

};

这是我最初拿到题目的时候所写的程序,之后我们准备将程序修改成二分法来求,但是在想二分法的时候我突然感觉即使是我的这个程序也还是有很多可以改进的可能,因为这是一个有序的数组,所以当我在遍历的时候完全可以判断,如果我们进行一次判断

if (data[i]>k)

break;

算是对程序的一个小小优化,不过我在牛客网进行了一次测试,发现没修改前我的代码执行时间是2ms但是修改之后反而时间变成了4ms,我想可能是测试用例给的数组太小了,反而让这个判断语句影响了我们的性能,不过牛客网这种,跟服务器也是有一定的关系的。

 继续说我之后想要修改的方法,就是通过二分法,二分法是处理有序数组比较好的一种方法,这里我们用的思想就是我们先找我们的k的位置在哪,因为这既然是求次数那自然就可能有很多个k,到底找到哪个k这是说不准的,和数组内容,数组长度都是有一定关系的,可能是最左边的k也可能是最右边的k所以在找到k的位置之后还要往数组左边和数组右边遍历,看看还有几个k。

//利用二分法来求

class Solution {

public:

int GetNumberOfK(vector<int> data, int k) {

int length = data.size();//求数组长度

int mid = length/2;//中间的位置下标

int left = 0;

int right = length-1;

int count = 0;

while (left<=right)

{

mid = (left + right) / 2;

if (data[mid] == k)

break;

if (data[mid] > k)

{

right=mid-1; //如果说mid的数据比k要大说明k在mid 的左边

}

else

{

left=mid+1;

}

}

//退出循环的时候已经找到k了或者说里边没有k

if (left > right)

{

return 0;

}

else

{

int mid2 = mid;

while (data[mid] == k && mid>=0)

{

count++;

mid--;

}

while (data[mid2] == k&&mid2<=length)

{

count++;

mid2++;

}



}

return count - 1;

}

};

这里有几个需要注意的点:第一个就是int right = length-1;最右边的下标一定要是长度减去1,一定要减去1,不然很大几率就会出现数组越界的情况。第二个就是right=mid-1; left=mid+1;这里对right和left赋值的时候,这里并不是说无非就是把这个位置遍历两边的问题,如果不减去1或者加上1是可能让你的程序出现死循环的,

 

这里假设我们的mid是小于k这时候就应该让left等于mid-1,如果是等于mid,那left就移动到了绿色方块位置,假如这时候再去求mid,因为mid得出来是整数所以mid还是绿色方块位置,然后如果绿色方块小于k,那还是让left等于mid这时候mid就一直在绿色方块位置不会移动,并且不符合条件mid数值不等于k,并且left<=right,就变成了死循环了。所以这里是一定要减去和加上1的。

 这里我再牛客网上测试依然是两个程序运行时间不相上下,不过我们在实际情况中,肯定很少出现那种只有几个数组数值的情况所以还是要对二分法有个深刻的理解。

并且二分法是有递归和非递归两种写法的,因为递归比较简单大多数是要求大家去写非递归,这里我也给大家提供一个递归的写法。这里就要提到的是我们需要在class中加入一个函数,之前在编程的时候很少会自己加一个函数能放到一个函数里边解决的就放在一个函数里边解决,其实这不是一个好的习惯,自己之前还是怕出错才不敢那么写。但是现在出错总比以后面试或者工作中出错要好。 

class Solution {

public:

int GetNumberOfK(vector<int> data, int k) {

int left = 0;

int length = data.size() - 1;

int right = data.size() - 1;

int mid = FindK(data, left, right, k);

int mid2 = mid;

int count = 0;

if (mid == -1)

return 0;

while ( mid >= 0 && data[mid] == k)

{

count++;

mid--;

}

while (mid2 <= length&&data[mid2] == k)

{

count++;

mid2++;

}

return count - 1;



}

private:

int FindK(vector<int> data, int left, int right, int k)

{

int mid = (left + right) / 2;

if (left > right)

return -1;

if (data[mid] == k)

{

return mid;

}

if (data[mid] > k)

{

return FindK(data, left, mid - 1, k);



}

else

{

return FindK(data, mid + 1, right, k);

}

}

};

这是递归算法,不过在写递归的时候我又发现了一个问题

while ( mid >= 0 && data[mid] == k)

{

count++;

mid--;

}

while (mid2 <= length&&data[mid2] == k)

{

count++;

mid2++;

}

那就是这两个while循环的判断语句我们这里特地加了让mid>=0并且是mid2<=length的判断语句就是为了不让我们的数组越界,但是如果说是按我以前写的

while (data[mid] == k && mid>=0),while (data[mid2] == k&&mid2<=length)这种判断语句还是可能会有越界的可能性,因为我首先进行的判断是data[mid]是不是等于k这时候我们已经访问了data数组中mid位置的数据了,确实我们在后边的判断mid>=0的时候条件不符合不会进入我们的循环语句,但是我们这时候依然是有了越界访问的。如果是while ( mid >= 0 && data[mid] == k)这种情况我们就不存在越界访问了,这也是我们之前所介绍过的短路定理,如果有不明白的可以看我之前对短路定理的博客。

https://blog.csdn.net/Hanani_Jia/article/details/81709052

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值