数组中计算某个元素出现次数的问题

我这篇文章要讲的一道算法题,一点都不难,可以说是小白级别的,是关于一个数组中计算某个元素的重复个数的问题。

具体问题:看如下一个无序的数组,求其中数字2出现的次数。怎么样,是不是超级简单:)

[0,2,1,1,2,3,5,2,3,3,4,5,7,6,2]

能够想到的解法,直接一趟for循环,碰到元素等于2了就开始计数加一,2的个数就是计数值。

挺好。这肯定能解决问题。

下面给出上面思路的实现,使用c++代码。值得注意的是,这里应该使用c++中的模板,也就是通常说的泛型。因为,我们并不确定给定数组元素的具体类型,除了上面给出的数组元素是整数外,也可能是字符串,或者自定义对象。

template <typename T>
int countOccurrences(vector<T> arr, T target){
    
    int result = 0;
    for (int i = 0; i < arr.size(); i++) {
        if (target == arr[i]) {
            result++;
        }
    }
    
    return result;
}

还有没有其他方法呢?这个时候就可以想一下,对了,我们也可以使用查找表来解决这个问题,以元素值为键,值为数组中相同元素的个数。需要一趟for 循环来统计相同元素的个数,最后返回这个查找表中对应元素的值就好了。

给出代码:

template <typename T>
int countOccurrences(vector<T> arr, T target){
    
    unordered_map<T, int> map;
    for (int i = 0; i < arr.size(); i++) {
        map[arr[i]]++;
    }
    
    return map[target];
}

想一想,如果这个数组变成有序了?可以想到其他方法来解决这个问题吗?

[0,1,1,2,2,2,2,3,3,3,4,5,5,6,7]

此时就可以使用二分查找了,二分查找主要解决的就是有序集合的查找问题。可是问题来了,这里有4个2,二分查找只能找到其中的一个2,这....好像使用二分查找也不行吧。

如果有这个疑问,可以先按照二分查找的思路,停下来先思考下如何使用二分查找来解决这个问题。

继续。

其实,我们可以使用二次二分查找,第一次寻找的是查找值的左边界,第二次寻找的是查找值的右边界。

先给出代码,然后根据代码进行讲解。

template <typename T>
int countOccurrences(vector<T> arr, T target){
    
    int low_left = 0;
    int high_left = arr.size();
    
    while (low_left < high_left) {
        int middle = low_left + (high_left - low_left) / 2;
        if (arr[middle] < target) {
            low_left = middle + 1;
        } else {
            high_left = middle;
        }
    }
    
    int low_right = 0;
    int high_right = arr.size();
    
    while (low_right < high_right) {
        int middle = low_right + (high_right - low_right) / 2;
        if (arr[middle] > target) {
            high_right = middle;
        } else {
            low_right = middle  + 1;
        }
    }
    
    return low_right - low_left;
}

我准备根据程序运行的逻辑,一步步进行讲解。

首先,这是我们传入的数组,寻找2的重复元素

vector<int> vec = vector<int>{0,1,1,2,2,2,2,3,3,3,4,5,5,6,7};
int result = countOccurrences(vec, 2);

第一个循环的作用是寻找左边界,也就是2起始的索引,在第一个while循环前,设置两个变量,low_left,high_left,我对这两个值的定义是在区间 [low_left,high_left) 中寻找目标值,注意区间是前闭后开的,所以这两个值的初始值设置为 low_left = 0,high_left = arr.size()。

经过第一个循环,中间值middle = 7,arr[7] = 3, 3大于2,所以此时 high_right = 7,数组如下所示,high_left此时指向3。为了便于查看,#号表示low_left的位置,*号表示high_left的位置。

[0,1,1,2,2,2,2,3,3,3,4,5,5,6,7]
 #             *

继续第二次循环,中间值middle = 3,arr[3] = 2,2等于目标值,所以此时high_left = 3,所以在这里也可以看出这里用到的二分查找与一般的二分查找的区别了,一般的二分查找在找到目标值后就会结束循环,这里却不然,会继续向左边部分查找。因为我们要找的是2第一次出现的位置。

[0,1,1,2,2,2,2,3,3,3,4,5,5,6,7]
 #     *

继续第三次循环,中间值middle = 1,arr[1] = 1,1小于2,所以此时low_left = middle + 1,就应该等于2。

[0,1,1,2,2,2,2,3,3,3,4,5,5,6,7]
     # *

继续第四次循环,middle = 2 + (3 - 2) / 2 = 2, arr[2] = 1, 1小于2,所以此时low_left继续加一,low_left = 3,结束循环。这个时候,我们就找到了2第一次出现的位置,如图。

[0,1,1,2,2,2,2,3,3,3,4,5,5,6,7]
       #
       *

寻找右边界与左边的逻辑类似,给出图示,就不一一讲解了,与寻找左边界不同的是,在找到2之后,会继续向右查找,2结束的位置。

[0,1,1,2,2,2,2,3,3,3,4,5,5,6,7]
 #             *
 
[0,1,1,2,2,2,2,3,3,3,4,5,5,6,7]
         #     *
         
[0,1,1,2,2,2,2,3,3,3,4,5,5,6,7]
           #   *
           
[0,1,1,2,2,2,2,3,3,3,4,5,5,6,7]
             # *
             
[0,1,1,2,2,2,2,3,3,3,4,5,5,6,7]
               #
               *

查找到的结果右边界是索引在7的位置,所以2出现的次数是右边界减去左边界,也就是7减去3等于4,求出结果。

接下来讨论一种特殊情况。

当前数组中不包括需要查找的元素。那么此时low_right 和 low_left 会返回相同的数,两数相减等于零,是满足的,

总结一下,在这个方法中,查找到值后,并不会停止查找,而是继续向左或向右,找到目标值的起始位置和结束位置。可以看到,在掌握基本知识的基础上,能灵活运用是非常重要的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容