这是一个我刚刚毕业时候面试腾讯的面试题,当时我的答案是先排序,然后取中间的数,那么该数就是我们需要的数,但是,这种方法的复杂度不是线性的,所以说就被out了。从那之后,我一直在思考这个问题该如何被线性解决。慢慢地有一个想法,就是制作表,表的大小就是L* 2 (L是数组的长度)。表的每一行第一个元素是数组中的数字,第二个元素是该数字出现的次数。但是,接着想下去,就发现其实在loop数组的同时,我们需要loop该表,因为当我们loop到数组某一元素时,我们要找到表中该数字的出现的次数,然后加一。于是,复杂度一下子就上去了,然后一直被block住。然后竟然没有百度,罪过呀 ^.^ ^.^ ^.^

前几天和一个朋友吃饭聊到这个题目,他说可以根据“出现次数超过一半”这个特性去解题。如果我们同时移除两个不同的数,那么剩下的数中,该数在剩下的数中出现的次数也是超过一半。 这样的话,我们可以按照这个思路做下去,唯一的问题是,如果一个数连续出现很多次,我们该如何去做移除两个不同的数这个动作。 我们可以用一个count来记录,如果出现多次,我们就把它加1,不同的数就减一。代码如下:

#include <stdio.h>
int half_search(int *inPtr, int len);
int half_search(int *inPtr, int len){
  if (inPtr == NULL && len <= 0)
   return -1;
 
  int i, result;
  int count = 0;
  for (i=0;i<len;i++){
      if (count==0){
         result = inPtr[i];
  count = 1;
      }else{
         if(result == inPtr[i])
     count++;
  else
     count--;    
      }
  }
  return result;
}

int main(){
  int a[10] = {1,4,2,5,4,3,4,4,2,4};
  int target_data;
 
  target_data = half_search(a,10); 
  printf("target half mid data: %d. \n",target_data);
 
  return 0;
}
 
 
当然,上面的做法可以网上搜索到,另外,看到网上用哈希表可以解决我之前想用制作表的方法的困境。之前我们想的是把数组中的数字顺序排列到表中,现在我们可以通过数组中的数字,用一定的规则(就是哈希函数)对应到一个地址上,这样,遇到相同的数字时,不要遍历,直接call函数就可以找到它在表中的位置。是不是看起来很合理。但是这样的话,一个有效的哈希函数就是必须的了,否者会极大浪费内存。关于哈希表的简介,我参照https://blog.csdn.net/dreamcs/article/details/5764444
 https://www.cnblogs.com/xiekeli/archive/2012/01/16/2323391.html
[1]hash函数定义
   根据key,计算出key对应记录的储存位置
   position = f(key)
   函数f就是hash函数

[2]hash冲突定义
    不同的key可能得到相同的position。原因为key值的范围过大。如int.
如果构造一个完全没有冲突的表。那么对应的地址空间就是4G,这是根本不
实现的。为了处理这个问题。就允许不同的key可以有相同的position。

[3]hash表定义
   映射的集合

[4]设计一个好的hash函数非常重要
   由[2]可知,一个好的hash函数可以使positions集中分布在某一段内存地址范围内。
   并且position在这段地址范围内存尽量均匀。这样每个key对应的position就尽可能
  少了,即hash冲突少。查找就会快一点。

[5]所谓的hash桶定义
   就是预分配的一段连续的储存空间

[6]处理冲突的方法
 (1)开放定址法:就是先指定一个桶,然后position = f(key,d),如果postion已经存在了,改变参数d,
     继续计算position = f(key,d),直到postion不存在
    d的不同取值的算法有:线性探测再散列,二次探测再散列,伪随机探测再散列。
     听这名字觉得好高深哦。实际上, 就是为了更高效地计算出没有使用的position
 (2)链地址法
     就是把position相同的item按插入的先后顺序组成一个链表
 
 
 具体代码如下:
 

#include <stdio.h>
#include <string.h>
#define HASHSIZE 10
int hash_function(int key);
typedef struct LINE_NODE{
int key;
int value;
struct LINE_NODE *next_node; // used to solve collision
}node;

node hashtable[HASHSIZE];
int hash_function(int key){
int table_idx;
table_idx = key % 4; // A simple hash rule
return table_idx;
}

int main(){
int i;
int table_idx;
int a[10] = {1, 4, 2, 4, 4, 3, 5, 4, 4, 4};

memset(hashtable, 0x0, HASHSIZE * sizeof(node));

for(i=0;i<10;i++){
table_idx = hash_function(a[i]);

if (hashtable[table_idx].key == a[i])
{
hashtable[table_idx].value++;
}else {
if (hashtable[table_idx].value == 0)
{
hashtable[table_idx].key = a[i];
hashtable[table_idx].value++;
}else {
hashtable[table_idx].next_node = &hashtable[HASHSIZE-1]; // simple allocation.
hashtable[table_idx].next_node->key = a[i];
hashtable[table_idx].next_node->value++;
}
}
printf("i: %d, a[i]: %d, hashtable[%d].key: %d. \n",i, a[i], table_idx, hashtable[table_idx].key);
}

for (i=0;i<10;i++){
printf("key: %d, value: %d. \n",hashtable[i].key, hashtable[i].value);
}
return 0;
}

 
扩展题:如何找到次数超过1/3的数
 
 思路是:相比于之前每次删除两个不同的数,这次我们每次删除三个不同的数,剩下的数一定是剩下两个数中的一个数。
 
 
 
 
 
posted on 2018-10-08 04:49  飞行的俊哥  阅读(203)  评论(0编辑  收藏  举报