熟悉hash运算的人可能很快能够想到应用hash映射来解决这个问题。就是建立key-value表,统计每个单词出现的次数,这样只需要遍历所有单词一遍就能够精确的统计出每个单词出现的次数。然后在遍历hash表,找到value值大于(注意不是等于)n/2的那个key值。算法的时间负责度和空间复杂度都为O(N),从不严格的意义上讲。找单词和找数字是一个道理,索性就给出了找数字的例子,因为找数字还有另外一种算法,所以应用找数字的方便进行对比。
#include<stdio.h>
#include<stdlib.h>
#define HASHLEN 71
typedef struct hash_node{
int key;
int value;
struct hash_node *next;
}hash_node, *p_hash_node;
p_hash_node bin[HASHLEN] = {NULL};
unsigned int hash(int key) {
return key % HASHLEN;
}
void insert_data(int key) {
unsigned index = hash(key);
hash_node *p = bin[index];
while(p) {
if(p->key == key) {
(p->value)++;
}
p = p->next;
}
p = (hash_node*)malloc(sizeof(hash_node));
p->value = 1;
p->key = key;
p->next = bin[index];
bin[index] = p;
}
int find_num(int *a, int len) {
int i;
hash_node *p;
for(i = 0; i < len; i++) {
insert_data(a[i]);
}
i = 0;
while(i < HASHLEN) {
for(p = bin[i]; p != NULL; p = p->next) {
if(p->value >= (len / 2)) {
return p->key;
}
}
i++;
}
}
void main() {
int a[] = {0, 1, 3, 2, 1, 1, 1, 2, 3, 3, 3, 3, 3, 3};
int len = sizeof(a) / sizeof(int);
int result = find_num(a, len);
printf("%d\n", result);
}
还有第二种方法,一次在数组中取出两个数字,比较这两个数字,对他们进行操作。因为我们不知道数组中哪个数字出现的次数大于数组元素个数的一半,所以取出的这两个数字有三种情况:1,两个数字都不是要找的数字x,但是两个数字不相等,或者两个数字相等;2,两个数字一个是要找的元素,一个不是要找的元素;3, 两个数字都是要找的元素。同时,还要设定一个记录要寻找数的候选元素candidate和元素出现的次数times。我们假设数组的第一个元素为我们要找的数字,然后循环遍历数组。取数组的第二个元素,如果此元素和candidate相等,那么times++,如果不相等times--,然后把第二个数假设为candidate。有人也许会问到,如果数组中某一个元素出现很多次,但是没有达到数组的一半,怎么办呢,放心,这样的数字肯定能找到和它出现次数对等个数的数字和它不同,最终times--为0。
#include<iostream>
using namespace std;
int get_num(int *a, int len) {
int result;
int times;
int i;
if(a == NULL || len <= 0) {
return 0;
}
result = a[0];
times = 1;
for(i = 1; i < len; i++) {
if(a[i] == result) {
times++;
} else {
times--;
}
if(times == 0) {
times =1;
result = a[i];
}
}
times = 0;
for(i = 0; i < len; i++) {
if(a[i] == result) {
times++;
}
}
if(times * 2 > len) {
return result;
} else {
return 0;
}
}
void main() {
int a[] = {1, 1, 1, 1, 2, 2, 2};
int len = sizeof(a) / sizeof(int);
int result = get_num(a, len);
cout << result << endl;
}
可以自己验证次程序的正确性,但是此问题只能解决大于一半的情况。
如果想对单词统计,还有一种方法就是用trie树。花费的时间和空间大约是O(n * le),O(n), le为单词的平均长度。