【软工面试】一个文件有N个单词,每行一个,快速找出一个单词x,已知该单词出现的次数大于N/2

【软工面试】一个文件有N个单词,每行一个,快速找出一个单词x,已知该单词出现的次数大于N/2


熟悉hash运算的人可能很快能够想到应用hash映射来解决这个问题。就是建立key-value表,统计每个单词出现的次数,这样只需要遍历所有单词一遍就能够精确的统计出每个单词出现的次数。然后在遍历hash表,找到value值大于(注意不是等于)n/2的那个key值。算法的时间负责度和空间复杂度都为O(N),从不严格的意义上讲。找单词和找数字是一个道理,索性就给出了找数字的例子,因为找数字还有另外一种算法,所以应用找数字的方便进行对比。

[cpp]  view plain  copy
  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3. #define HASHLEN 71  
  4. typedef struct hash_node{  
  5.     int key;  
  6.     int value;  
  7.     struct hash_node *next;  
  8. }hash_node, *p_hash_node;  
  9.   
  10. p_hash_node bin[HASHLEN] = {NULL};  
  11.   
  12. unsigned int hash(int key) {  
  13.     return key % HASHLEN;  
  14. }  
  15.   
  16. void insert_data(int key) {  
  17.     unsigned index = hash(key);  
  18.     hash_node *p = bin[index];  
  19.     while(p) {  
  20.         if(p->key == key) {  
  21.             (p->value)++;  
  22.         }  
  23.         p = p->next;  
  24.     }  
  25.   
  26.     p  = (hash_node*)malloc(sizeof(hash_node));  
  27.     p->value = 1;  
  28.     p->key = key;  
  29.     p->next = bin[index];  
  30.     bin[index] = p;  
  31. }  
  32.   
  33. int find_num(int *a, int len) {  
  34.     int i;  
  35.     hash_node *p;  
  36.     for(i = 0; i < len; i++) {  
  37.         insert_data(a[i]);  
  38.     }  
  39.     i = 0;  
  40.     while(i < HASHLEN) {  
  41.         for(p = bin[i]; p != NULL; p = p->next) {  
  42.             if(p->value >= (len / 2)) {  
  43.                 return p->key;  
  44.             }  
  45.         }  
  46.         i++;  
  47.     }  
  48. }  
  49.   
  50. void main() {  
  51.     int a[] = {0, 1, 3, 2, 1, 1, 1, 2, 3, 3, 3, 3, 3, 3};  
  52.     int len = sizeof(a) / sizeof(int);  
  53.     int result = find_num(a, len);  
  54.     printf("%d\n", result);  
  55. }  

还有第二种方法,一次在数组中取出两个数字,比较这两个数字,对他们进行操作。因为我们不知道数组中哪个数字出现的次数大于数组元素个数的一半,所以取出的这两个数字有三种情况:1,两个数字都不是要找的数字x,但是两个数字不相等,或者两个数字相等;2,两个数字一个是要找的元素,一个不是要找的元素;3, 两个数字都是要找的元素。同时,还要设定一个记录要寻找数的候选元素candidate和元素出现的次数times。我们假设数组的第一个元素为我们要找的数字,然后循环遍历数组。取数组的第二个元素,如果此元素和candidate相等,那么times++,如果不相等times--,然后把第二个数假设为candidate。有人也许会问到,如果数组中某一个元素出现很多次,但是没有达到数组的一半,怎么办呢,放心,这样的数字肯定能找到和它出现次数对等个数的数字和它不同,最终times--为0。

[cpp]  view plain  copy
  1. #include<iostream>  
  2. using namespace std;  
  3.   
  4. int get_num(int *a, int len) {  
  5.     int result;  
  6.     int times;  
  7.     int i;  
  8.     if(a == NULL || len <= 0) {  
  9.         return 0;  
  10.     }  
  11.     result = a[0];  
  12.     times = 1;  
  13.     for(i = 1; i < len; i++) {  
  14.         if(a[i] == result) {  
  15.             times++;  
  16.         } else {  
  17.             times--;  
  18.         }  
  19.   
  20.         if(times == 0) {  
  21.             times =1;  
  22.             result = a[i];  
  23.         }  
  24.     }  
  25.   
  26.     times = 0;  
  27.     for(i = 0; i < len; i++) {  
  28.         if(a[i] == result) {  
  29.             times++;  
  30.         }  
  31.     }  
  32.     if(times * 2 > len) {  
  33.         return result;  
  34.     } else {  
  35.         return 0;  
  36.     }  
  37. }  
  38.   
  39. void main() {  
  40.     int a[] = {1, 1, 1, 1, 2, 2, 2};  
  41.     int len = sizeof(a) / sizeof(int);  
  42.     int result = get_num(a, len);  
  43.     cout << result << endl;  
  44. }  
可以自己验证次程序的正确性,但是此问题只能解决大于一半的情况。

如果想对单词统计,还有一种方法就是用trie树。花费的时间和空间大约是O(n * le),O(n), le为单词的平均长度。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值