DFS 1253 解题报告

题目的大致意思是,对于类似 xml 或者 json 格式的数据,进行增、删、改、输出。可见需要用到的算法便是 DFS。
大致思路如下:

  1. 对初始数据利用 DFS 建立一颗树,方便后续进行增、删、修改、输出。
  2. 利用哈希算法对其降低运算维度。
  3. 操作完成后,对步骤 1 进行反向 DFS,输出答案。

建树

根据题意,我们要建的树如下图:
这里写图片描述
很显然,我们需要利用 DFS 来建立它。每个节点都包含左孩子,右兄弟,当然,每个节点必须有一个父节点。指向父节点的指针没有画出来,自己脑补。
我在建立这颗树的时候,浪费了太多的时间,以致于后面没有时间进行调试。所以,必须熟练的写这个建树过程,达到能够快速码代码。

建立哈希表

一开始我只用了哈希函数,把字符串转化成一个对应的哈希值,然后单单用来进行判断而已。代码如下:

int getHashCode(char s[])
{
   ULL ret = 1;
   rint i = 0;
   while(s[i] != '\0')
   {
         ret += ((ret << 7) + s[i++] + 3); //(左移7 并且 +2 = 131)
   }
   return (ret % MAX_NODE_NUM);
}

恩,没错,我只做到这一步。那么每次我去找节点的时候,都得把所有的节点寻找一遍。如下代码:

int findNode(ULL code)
{
       for(rint i = 0; i < cnt; i++)//遍历一遍,code为计算出来的哈希值
       {
             if(node[i].isDelete)
                    continue;
             if(code == node[i].keyCode)
             {
                    return i;
             }
       }
       return -1;
}

实际上,可以利用哈希值创建一个哈希表,根据 key 直接取出结果,而不必遍历,更加优化。每次取值只需调用get()便可,建立哈希表如下:

NODE* hashMap[MAX_NODE_NUM];
//添加节点,key - value
void put(char str[], NODE* node)
{
    int key = getHashCode(str);

    if(hashMap[key] == 0)
    {
        hashMap[key] = node;
    }
    else//哈希冲突链表
    {
        node->next = hashMap[key];
        hashMap[key] = node;
    }
}

//获取节点
NODE* get(char str[])
{
    int key = getHashCode(str);

    NODE* cur = hashMap[key];

    if(cur && cur->next == 0)
        return cur;

    while(cur)//解决哈希冲突
    {
        if(isSameStr(cur->keyStr, str) == true)//直接比较字符串是否相等
            break;
        else
            cur=cur->next;
    }

    return cur;
}

增加,删除节点

这一部分,看似没什么好讲,实际上删除节点这部分,我还是踩坑了。一开始我没有采用哈希表的方法,所以删除一个节点后,没有把他的子节点全部标识为删除状态。那么在

findNode(ULL code)

的时候,会去遍历到已经删除的子节点,会导致错误。这需要递归去把他的子节点标识为删除状态。代码如下:

void markForDelete(NODE* tar)
{
    if(tar == 0)
        return;

    tar->isDelete = true;

    if(tar->leftChind)//递归遍历这颗子树
    {
        NODE* rb = tar->leftChind;
        markForDelete(rb);
        while(rb->rightBro)
        {
            rb = rb->rightBro;
            markForDelete(rb);
        }
    }
}

当然,改用哈希表的方法不需要这么处理。

总结和完整源码

这到题目的感觉就是,有思路可以做出来啊,但是就是码出不来。原因还是不熟,功力不够,针对这类型的题目还得多写代码。

1)整个核心就是建树过程,添加节点过程,删除过程。这个是最重要的。

2)一定先读懂题意,比如这道题就明确说了最多节点也就400个,要先知道题目确切的意思。确切的数据量大小。

3)先整理思路(要清晰)和搭好框架,然后填代码。


//user code

#define STRINGTYPE         0x01
#define SETTYPE            0x02
#define MAX_KEY_LENGTH     15
#define MAX_VALUE_LENGTH   127
#define MAX_MESSAGE_LENGTH 81920
#define MAX_NODE_NUM (401)
#define ULL unsigned long long
#define rint unsigned register

typedef struct NODE_
{
   int type;

   char keyStr[MAX_KEY_LENGTH];
   int keyLen;
   int keyOne;
   int keySec;

   char valueStr[MAX_VALUE_LENGTH];
   int valueLen;
   int valueOne;
   int valueSec;

   NODE_ *parent;
   NODE_ *leftChind;
   NODE_ *leftBro;
   NODE_ *rightBro;
   NODE_ *next;
}NODE;

NODE* hashMap[MAX_NODE_NUM];
NODE node[MAX_NODE_NUM];

int cnt=0;
int start = 0;
int target = 0;
NODE *Root;

//tool function
int getHashCode(char s[]);
void ztrDecodecpy(char dest[], const char src[], const int offset, const int length);
void ztrEncodeCpy(char dest[], const char src[], const int offset, const int length);
int ztrlen(const char str[]) ;
void init();
void put(char str[], NODE* node);
NODE* get(char str[]);
bool isSameStr(char s1[], char s2[]);

//build tree and revert tree
void buildSetTree(NODE *par, char in[]);
void revertSetTree(NODE* tar, char out[MAX_MESSAGE_LENGTH]);

//add & del & find node operation
void addChild(NODE* par, NODE* node, bool flag = false);
void delChild(NODE* cur);
void markForDelete(NODE* tar);
//int findNode(ULL code);

bool isSameStr(char s1[], char s2[])
{
    bool ret = false;
    int len1 = ztrlen(s1);
    int len2 = ztrlen(s2);
    if(len1 == len2)
    {
        ret = true;
        while(len1--)
        {
            if(s1[len1] != s2[len1])
            {
                ret = false;
                break;
            }
        }
    }
    return ret;
}

void put(char str[], NODE* node)
{
    int key = getHashCode(str);

    if(hashMap[key] == 0)
    {
        hashMap[key] = node;
    }
    else//哈希冲突链表
    {
        node->next = hashMap[key];
        hashMap[key] = node;
    }
}

NODE* get(char str[])
{
    int key = getHashCode(str);

    NODE* cur = hashMap[key];

    if(cur && cur->next == 0)
        return cur;

    while(cur)
    {
        if(isSameStr(cur->keyStr, str) == true)
            break;
        else
            cur=cur->next;
    }

    return cur;
}

int getHashCode(char s[])
{
   ULL ret = 1;
   rint i = 0;
   while(s[i] != '\0')
   {
         ret += ((ret << 7) + s[i++] + 3);
   }
   return (ret % MAX_NODE_NUM);
}

void init()
{
   for(rint i = 0; i < MAX_NODE_NUM; i++)
   {
        hashMap[i] = 0;
        node[i].type = 0;
        node[i].keyOne = 0;
        node[i].keyLen = 0;
        node[i].keySec = 0;
        node[i].valueLen = 0;
        node[i].valueOne = 0;
        node[i].valueSec = 0;
        for(rint j = 0; j < MAX_KEY_LENGTH; j++)
        {
            node[i].keyStr[j] = 0;
        }
        for(rint j = 0; j < MAX_VALUE_LENGTH; j++)
        {
            node[i].valueStr[j] = 0;
        }
        node[i].leftChind = 0;
        node[i].rightBro = 0;
        node[i].parent = 0;
        node[i].leftBro = 0;
        node[i].next = 0;
   }
   cnt = 0;
   start = 0;
   target = 0;
   Root = &node[cnt++];
}

void ztrDecodecpy(char dest[], const char src[], const int offset, const int length) 
{
    int pos = 0;
    while (pos < length && src[pos + offset] != '\0') {
        dest[pos] = src[pos + offset];
        ++pos;
    }
    dest[pos] = '\0';
}

void ztrEncodeCpy(char dest[], const char src[], const int offset, const int length) {
    int pos = 0;
    while (pos < length && src[pos] != '\0') {
        dest[offset + pos] = src[pos];
        ++pos;
    }
}

int ztrlen(const char str[]) 
{  
    int pos = 0;
    while(str[pos] != '\0')
        ++pos;
    return pos;
}

void addChild(NODE* par, NODE* node, bool flag)
{
    if(par == 0 || node == 0)
        return;

    put(node->keyStr, node);
    node->parent = par;

    if(!par->leftChind)
    {
        par->leftChind = node;
    }
    else
    {
        NODE* rb = par->leftChind;
        while(rb->rightBro)
        {
            rb = rb->rightBro;
        }
        rb->rightBro = node;
    }

    if(flag == true)
    {
        par->valueLen++;
        par->valueSec = (par->valueLen & 0xff);
        par->valueOne = (par->valueLen >> 8);
    }
}

void delChild(NODE* cur)
{
    if(!cur)
        return;

    NODE* par = cur->parent;

    if(!par)
        return;

    NODE* rb = par->leftChind;
    bool isFind = false;
    if(rb == cur)
    {
        par->leftChind = rb->rightBro;
        rb->leftBro = 0;
        isFind = true;
    }
    else
    {
        while(rb->rightBro)
        {
            if(rb->rightBro == cur)
            {
                isFind = true;
                break;
            }
            else
            {
                rb = rb->rightBro;
            }
        }
    }

    if(isFind)
    {
        rb->rightBro = cur->rightBro;
        par->valueLen--;
        par->valueOne = par->valueLen >> 8;
        par->valueSec = par->valueLen & 0xff;
    }
}
void buildSetTree(NODE *par, char in[])
{

    int type = in[start];
    start++;

    NODE* cur = &node[cnt++];
    cur->type = type;

    //key
    cur->keyOne = in[start++];
    cur->keySec = in[start++];
    cur->keyLen = (cur->keyOne >> 8) + cur->keySec;
    ztrDecodecpy(cur->keyStr, in, start, cur->keyLen);

    //value
    start += cur->keyLen;
    cur->valueOne = in[start++];
    cur->valueSec = in[start++];
    cur->valueLen = (cur->valueOne >> 8) + cur->valueSec;

    if(type == STRINGTYPE)
    {
        ztrDecodecpy(cur->valueStr, in, start, cur->valueLen);
        start += cur->valueLen;
    }
    else
    {
        //build child
        for(rint i = 0; i < cur->valueLen; i++)
        {
            buildSetTree(cur, in);
        }
    }

    addChild(par, cur);

}

void revertSetTree(NODE* tar, char out[MAX_MESSAGE_LENGTH])
{
    int type = tar->type;
    out[target++] = type;
    out[target++] = tar->keyOne;
    out[target++] = tar->keySec;
    ztrEncodeCpy(out, tar->keyStr, target, tar->keyLen);
    target += tar->keyLen;

    out[target++] = tar->valueOne;
    out[target++] = tar->valueSec;
    if(type == STRINGTYPE)
    {
        ztrEncodeCpy(out, tar->valueStr, target, tar->valueLen);
        target += tar->valueLen;
    }
    else
    {
        if(tar->leftChind)
        {
            NODE* rb = tar->leftChind;
            revertSetTree(rb, out);
            while(rb->rightBro)
            {
                rb=rb->rightBro;
                revertSetTree(rb, out);
            }
        }
    }
}

void parse(char in[MAX_MESSAGE_LENGTH], int size)
{
    init();
    buildSetTree(Root, in);
}

void set(char targetkey[MAX_KEY_LENGTH + 1], char newvalue[MAX_VALUE_LENGTH + 1])
{
    int len = ztrlen(newvalue);

    NODE* cur = get(targetkey);

    if(cur == 0)
        return;

    cur->valueLen = len;
    cur->valueOne = len >> 8;
    cur->valueSec = len & 0xff;
    ztrDecodecpy(cur->valueStr, newvalue, 0, len);
}

void add(char parentkey[MAX_KEY_LENGTH + 1], char childkey[MAX_KEY_LENGTH + 1], char childvalue[MAX_VALUE_LENGTH + 1])
{
    int keyLen = ztrlen(childkey);
    int valueLen = ztrlen(childvalue);

    NODE* par = get(parentkey);
    NODE* cur = &node[cnt++];
    cur->type = STRINGTYPE;

    cur->keyOne = keyLen >> 8;
    cur->keySec = keyLen & 0xff;
    cur->keyLen = keyLen;
    ztrDecodecpy(cur->keyStr, childkey, 0, cur->keyLen);

    cur->valueOne = valueLen >> 8;
    cur->valueSec = valueLen & 0xff;
    cur->valueLen = valueLen;
    ztrDecodecpy(cur->valueStr, childvalue, 0, cur->valueLen);
    addChild(par, cur, true);
}

void erase(char targetkey[MAX_KEY_LENGTH + 1])
{
    NODE* cur = get(targetkey);

    if(cur == 0)
        return;

    delChild(cur);
}

int generate(char targetkey[MAX_KEY_LENGTH + 1], char out[MAX_MESSAGE_LENGTH])
{

    target = 0;

    NODE* tar = get(targetkey);

    if(tar == 0)
        return target;

    revertSetTree(tar, out);
    out[target] = '\0';

    return target;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值