题目:
解法1:
可以把这个问题转化为一个排序和一个搜索问题.先把列表转化为一个有序列表,然后再遍历整个列表有序列表.排序算法不在主题之内,暂时就避而不谈.在有序列表寻找出现次数最多的元素这个简单的算法跟thunder出的笔试题一模一样.当时的要求是在一个有序的字符里,寻找最多的元素,并统计次数.看看这段简单的code吧
#include "stdafx.h"
#include <string.h>
#include <stdio.h>
int ThunderSearch(char * array, unsigned int number)
{
unsigned int i, prev , curr , j;
prev = curr = 1;
for (i = 0; i < number; i++)
{
if (array[i+1] == array[i])
{
curr++;//找到两个相同的,记录变量加一
}
else
{
curr = 1;//重置当前搜索出现的次数
}
if (curr >= prev)//当发现当前连续出现的次数大于之前搜索到得连续次数,重置
{
prev = curr;
j = i;//记录下标
}
}
printf("the frequency char is %c\n", array[j]);
return prev;
}
int main(int argc, char* argv[])
{
char * string = "aasssdddffffggggggg";
int result;
result = ThunderSearch(string, strlen(string));
printf("the number is %d\n", result);
return 0;
}
回到刚才的Tango寻找水王的问题,上面这段算法的时间复杂度为O(N),如果算上排序的时间复杂度Nlog2(N),那么整个算法的时间复发度则为O(N+Nlog2(N)).当N很大的时候,太影响效率.
解法2:
这里的解法用到了分治的策略.如果存在一个ID列表,并且这个列表内有一个ID的次数超过了总数的一半.那么我们每次删除两个不同的ID之后剩下的也肯定能满足有某一个ID的次数超过ID总数的一半.由于这这解法不需要排序,所以时间复杂度为O(N)
// TangoSearch.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
char TangoFind(char * array, unsigned int number)
{
char candidate;
int nTimes=0;
for(int i=0; i < number; i++)
{
//nTimes==0 则说明当前猜测的ID已经,再找下一个ID开始比较,重复之前操作
if(nTimes == 0)
{
//存储下一个ID
candidate = array[i];
nTimes = 1;
}
else
{
//若相同,则nTimes++.
if(candidate == array[i])
nTimes ++;
else
nTimes --;
}
}
return candidate;
}
int main(int argc, char* argv[])
{
char * string ="3333313234323786393634383";
char result;
result = TangoFind(string, strlen(string));
printf("TangoFind result is %c\n", result);
return 0;
}
具体编程的时候,使用一个candidate记录当前猜测的水王ID.一个count 记录其累计次数,然后遍历整个ID列表.对于当前所考查的ID,如果和candidate相同,那么count++,如果不同,那么count--.这个“count--;”的动作就是“删除两个不同的ID”在程序中的体现.当count==0时,则更新candidate.这样呢,每次count--都相当于删除了两个不同的ID(可能包含水王的,也可能不包含,而每考察一个ID,要么会做count--的动作,要么会做count++的动作,两者必居其一,而由于“水王ID超过一半”这一事实,因此count--的次数一定比count++少,因此最后count一定是个正整数,且此时的candidate一定记录着水王的ID.
拓展问题:
如果没有超级水王了,可是有三个ID在列表中出现的次数都超过了1/4,怎么找出这三个ID?
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
char TangoFind(char * array, unsigned int number)
{
char candidate;
int nTimes=0;
for(unsigned int i=0; i < number; i++)
{
//nTimes==0 则说明两个ID不同了,删了,然后再找下一个ID开始比较,重复之前操作
if(nTimes == 0)
{
//存储下一个ID
candidate = array[i];
nTimes = 1;
}
else
{
//若相同,则nTimes++.
if(candidate == array[i])
nTimes ++;
else
nTimes --;
}
}
return candidate;
}
void TangoFind2(char * array, unsigned int number)
{
char candidate[3]= {0};
int nTimes[3] = {0};
unsigned int i, j;
bool initcomplete = false;
for (i=0; i < number; i++)
{
if (initcomplete == false)//初始化,选取3个不同的候选数
{
if (candidate[0] == 0 || array[i] == candidate[0])
{
candidate[0] = array[i];
nTimes[0]++;
continue;
}
else if ((candidate[1] == 0 || array[i] == candidate[1])
{
candidate[1] = array[i];
nTimes[1]++;
continue;
}
else if (candidate[2] == 0)
{
candidate[2] = array[i];
nTimes[2]++;
initcomplete = true;
continue;
}
}
for (j = 0; j < 3; j++)
{
if (nTimes[j] == 0)
{
switch(j)
{
case 0:
if (array[i] != candidate[j+1] && array[i] != candidate[j+2])
{
candidate[j] = array[i];
nTimes[j] = 1;
j = 4;
}
break;
case 1:
if (array[i] != candidate[j+1] && array[i] != candidate[j-1])
{
candidate[j] = array[i];
nTimes[j] = 1;
j = 4;
}
break;
case 2:
candidate[j] = array[i];
nTimes[j] = 1;
break;
}
}
else
{
if(candidate[j] == array[i])
nTimes[j] ++;
else
nTimes[j] --; //如果array[i]跟candidate数组中的都不相等 则执行一次删除4个ID
}
}
}
}
int main(int argc, char* argv[])
{
char * string = "3333313234323786393634383";
char * string2 = "12341234123412312312312312355512312312311";
char result;
result = TangoFind(string, strlen(string));
TangoFind2(string2, strlen(string2));
printf("TangoFind result is %c\n", result);
return 0;
}
思路其实是类似的,我们每次删除4个不同的ID.不影响“那三个ID在剩余ID中出现仍然超过1/4”这一事实.直到剩下3个ID为止.具体编程中怎么体现“删除四个不同ID”这一动作呢?用candidate[3]记录三个候选ID,用count[3]记录它们的累积次数.然后遍历整个ID列表,每处理一个ID,若与candidate[i]中的某一个相同,则count[i]++,若与三个都不同,则说明找到了四个互不相同的ID,将三个count[i]--.也就相当于“删除了四个不同ID”,若某一个count[i]==0,则更新.