【字典树的应用之】百度2012实习生校园招聘笔试题

/--------MonkeyAndy原创,转载请注明出处 -----------/
http://blog.csdn.net/MonkeyAndy

/--------MonkeyAndy原创,转载请注明出处--------------/


学习了字典树后想起百度的笔试题可以使用这个方法解决,写下与大家分享,欢迎批评指正,共同进步


题目描述:

给一个单词a,如果通过交换单词中字母的顺序可以得到另外的单词b,那么b是a的兄弟单词,比如的单词army和mary互为兄弟单词。 现在要给出一种解决方案,对于用户输入的单词,根据给定的字典找出输入单词有哪些兄弟单词。请具体说明数据结构和查询流程,要求时间和空间效率尽可能地高。


分析:

要从大量的字典中查找某一个单词,采用普通的数据结构,只能采取顺序遍历的情况,这样效率是非常低下的,所以只能采用字典树来存储。字典树采取了由空间换时间的策略,其优势在于插入和查找可以同时进行。使用trie:因为当查询如字符串abc是否为某个字符串的前缀时,显然以b,c,d....等不是以a开头的字符串就不用查找了。所以建立trie的复杂度为O(n*len),而建立+查询在trie中是可以同时执行的,建立的过程也就可以成为查询的过程。所以总的复杂度为O(n*len),实际查询的复杂度也只是O(len)。(说白了,就是Trie树的平均高度h为len,所以Trie树的查询复杂度为O(h)=O(len)。好比一棵二叉平衡树的高度为logN,则其查询,插入的平均时间复杂度亦为O(logN))。

关于字典树的更多介绍请参考

http://blog.csdn.net/v_july_v/article/details/6897097


所用的数据结构为

#define MAX 26
typedef struct Trie{
     char * data;
     struct Trie *childs[MAX];
}Trie, *pTrie;

思路:

(1) 根据给定的字典,构造出字典中单词的字典树,根节点为 pTrie root;

(2)根据输入的单词,将其字母顺序进行排列组合,得到所有可能的单词组合,长度为n的单词有n!种组合情况(不考虑重复情况)char * words[n!];


勘误:

     应改为:根据输入的单词,求其全排列,将结果存放于 char * words[n!] 中.

(3)遍历words,以words[i]为关键字,在字典树中进行查找,若找到,则返回true,并加入到 brothers[ ]中;否则返回false;


从字典树中查找单词的流程如下:

(1)遍历单词中的所有字母,若 word[i] 在字典树中相应的节点处存在( ptr->child[index(word[i])]->data == word[i]),则指针 ptr=ptr->child[index(words[i])] ; 

(2)否则,查找失败,返回false;

(3)遍历结束后,ptr此时指向word[len-1]所在的节点,此时遍历ptr->childs[] 节点,查找是否存在非空的节点,若存在说明word只是字典中某个单词的一个前缀,则返回false;否则返回true;


解释:

words数组中存放的长度为n的单词的所有肯能的兄弟单词,而word为char *类型的变量, word中存放的是words中的某个单词words[i]. 



所用到的函数

//初始化函数
void init(pTrie & root)
{
    root->data = '';
    for(int i=0; i<MAX; i++)
        root->childs[i] = NULL;
}


//构造字典树
void creat(pTrie root, char *word)
{
    pTrie ptr = root;
    for(int i=0; i<strlen(word); i++)
    {
         if(ptr->childs[getIndex(word[i])] == NULL)
         {
               ptr->childs[getIndex(word[i])]->data = word[i];
               ptr->childs[getIndex(word[i])] = (pTrie)malloc(sizeof(Tire));
               for(int j=0; j<MAX; j++)
                   ptr->childs[getIndex(word[i])]->childs[j] = NULL;
          }
          ptr = ptr->childs[getIndex(word[i])];
    }
}
//勘误 :  构造字典树
void creat(pTrie root, char *word)
{
    pTrie ptr = root;
    for(int i=0; i<strlen(word); i++)
    {
         if(ptr->childs[getIndex(word[i])] == NULL)
         {
               ptr->childs[getIndex(word[i])] = (pTrie)malloc(sizeof(Tire));
                //应先开辟内存空间再进行赋值操作
                ptr->childs[getIndex(word[i])]->data = word[i];
               for(int j=0; j<MAX; j++)
                   ptr->childs[getIndex(word[i])]->childs[j] = NULL;
          }
          ptr = ptr->childs[getIndex(word[i])];
    }
}


//查找单词
bool search(pTrie root, char *word)
{
    ptr=root;
    bool flag = true;
    for(int i=0; i<strlen(word); i++)
    {
          if(ptr->childs[getIndex(word[i])]->data == word[i])
               ptr=ptr->childs[getIndex(word[i])];
           else{
                flag = false;
                break;
            }
    }
    for(int j=0;j<MAX; j++){
       if(ptr->childs[j] != NULL){
            flag = false;
            break;
       }
   }
    return flag;
}




补充:
全排列算法原理和其递归实现
 全排列是将一组数按一定顺序进行排列,如果这组数有n个,那么全排列数为n!个。现以{1, 2, 3, 4, 5}为例说明如何编写全排列的递归算法。1、首先看最后两个数4, 5。 它们的全排列为4 5和5 4, 即以4开头的5的全排列和以5开头的4的全排列。由于一个数的全排列就是其本身,从而得到以上结果。2、再看后三个数3, 4, 5。它们的全排列为3 4 5、3 5 4、 4 3 5、 4 5 3、 5 3 4、 5 4 3 六组数。即以3开头的和4,5的全排列的组合、以4开头的和3,5的全排列的组合和以5开头的和3,4的全排列的组合.从而可以推断,设一组数p = {r1, r2, r3, ... ,rn}, 全排列为perm(p),pn = p - {rn}。因此perm(p) = r1perm(p1), r2perm(p2), r3perm(p3), ... , rnperm(pn)。当n = 1时perm(p} = r1。为了更容易理解,将整组数中的所有的数分别与第一个数交换,这样就总是在处理后n-1个数的全排列。



void Swap(char* a, char* b)
{// 交换a和b
    char temp = *a;
    *a = *b;
    *b = temp;
}


void Perm(char list[], int k, int m)
{ //生成list [k:m ]的所有排列方式
    int i;
    if (k == m) {//输出一个排列方式
        for (i = 0; i <= m; i++)
            putchar(list[i]);
        putchar('\n');
    }
    else // list[k:m ]有多个排列方式
        // 递归地产生这些排列方式
        for (i=k; i <= m; i++) {
            Swap (&list[k], &list[i]);
            Perm (list, k+1, m);
            Swap (&list [k], &list [i]);
        }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值