数据结构树节点深度和广度关系递归记录

在树结构中,一般有GetChild和GetNextSibing的函数提供给我们使用,通过这两个接口和递归能够遍历得到树的所有的节点的信息。而在某些开发情况下,我们需要快速地搜寻到特定节点的父节点(父节点没有直接的指向该节点,需要我们将间接的关系挖掘表示出来)和前一兄弟节点(PrevSibling),那么我们需要在遍历之前对逻辑的信息进行记录,下面给出演示代码:

typedef struct NODEINFO {
    NODEINFO() {
    pParent = nullptr;
    pBefore = nullptr;
    pAfter = nullptr;
    pChild = nullptr;
}
    NODEINFO(FPD_Bookmark Parent,FPD_Bookmark Before,FPD_Bookmark After,
    FPD_Bookmark Child)
    {
        pParent = Parent;
        pBefore = Before;
        pAfter = After;
        pChild = Child;
    }
    FPD_Bookmark pParent;
    FPD_Bookmark pBefore;
    FPD_Bookmark pAfter;
    FPD_Bookmark pChild;
}*LPNODEINFO;

std::map<FPD_Bookmark, NODEINFO>g_mapTreeData;

//获得上层的父节点,父节点可能没有直接指向子节点
//是为了UI的便捷性
void SetParent(FPD_Bookmark pCurrentBM, FPD_Bookmark pParent)
{
    g_mapTreeData[pCurrentBM].pParent = pParent;
}

void SetPrevSibling(FPD_Bookmark pCurrentBM, FPD_Bookmark pBefore)
{
    g_mapTreeData[pCurrentBM].pBefore = pBefore;
}

//
//
//void SetChild(FPD_Bookmark pCurrentBM, FPD_Bookmark pChild)
//{
//    g_mapTreeData[pCurrentBM].pChild = pChild;
//}



void GetTreeTopologyInfo(FPD_Document m_fpdDoc, FPD_Bookmark pCurrentBM)
{
    //if (!pCurrentBM)
    //{
    //    //跟节点的记录
    //}

    if (pCurrentBM)
    {
        FS_WideString wsTitle = FSWideStringNew();
        FPDBookmarkGetTitle(pCurrentBM, &wsTitle);
        auto title = FSWideStringCastToLPCWSTR(wsTitle);
        std::cout << title << std::endl;
        
        NODEINFO  nodeInfo;
        g_mapTreeData.insert(std::pair<FPD_Bookmark, NODEINFO>(pCurrentBM, nodeInfo));
    }

    FPD_Bookmark pdf_BM1 = FPDBookmarkNew(nullptr);
    FPDBookmarkGetFirstChild(m_fpdDoc, pCurrentBM, &pdf_BM1);

    if (!FPDBookmarkGetDictionary(pdf_BM1))
    {
        return;
    }
    else
    {
        SetParent(pdf_BM1,pCurrentBM);
        GetTreeTopologyInfo(m_fpdDoc, pdf_BM1);
    }

    //FPD_Bookmark  temp = pdf_BM1;
    while (1)
    {
        FPD_Bookmark pdf_BM2 = FPDBookmarkNew(nullptr);
        FPDBookmarkGetNextSibling(m_fpdDoc, pdf_BM1, &pdf_BM2);
        if (!FPDBookmarkGetDictionary(pdf_BM2))
        {
            return;
        }
        else {
            SetParent(pdf_BM2, pCurrentBM);
            SetPrevSibling(pdf_BM2, pdf_BM1);
            GetTreeTopologyInfo(m_fpdDoc, pdf_BM2);
            pdf_BM1 = pdf_BM2;
        }

    }
}

FS_BOOL PIInit(void)
{
    FPD_Document fpdDoc = FPDDocOpen(L"C:/Users/jingj/Desktop/compare/bookmark/zyq.pdf", "");
    GetTreeTopologyInfo(fpdDoc, nullptr);
    FPDDocClose(fpdDoc);
	return true;
}

对上面的代码进行优化:

typedef struct NODEINFO {
    NODEINFO() {
    pParent = nullptr;
    pBefore = nullptr;
    pAfter = nullptr;
    pChild = nullptr;
}
    NODEINFO(FPD_Bookmark Parent,FPD_Bookmark Before,FPD_Bookmark After,
    FPD_Bookmark Child)
    {
        pParent = Parent;
        pBefore = Before;
        pAfter = After;
        pChild = Child;
    }
    FPD_Bookmark pParent;
    FPD_Bookmark pBefore;
    FPD_Bookmark pAfter;
    FPD_Bookmark pChild;
}*LPNODEINFO;

std::map<FPD_Bookmark, NODEINFO>g_mapTreeData;
int g_mapTreeNodeCount = 0;

//获得上层的父节点,父节点可能没有直接指向子节点
//是为了UI的便捷性
void SetParent(FPD_Bookmark pCurrentBM, FPD_Bookmark pParent)
{
    g_mapTreeData[pCurrentBM].pParent = pParent;
}

void SetPrevSibling(FPD_Bookmark pCurrentBM, FPD_Bookmark pBefore)
{
    g_mapTreeData[pCurrentBM].pBefore = pBefore;
}

std::map<int,bool> delte0;
std::vector<int> newa0;
std::map<int, bool> delte1;
std::vector<int> newa1;

void GetTreeTopologyInfo(FPD_Document m_fpdDoc, FPD_Bookmark pCurrentBM)
{
    if (pCurrentBM)
    { 
        NODEINFO  nodeInfo;
        g_mapTreeData.insert(std::pair<FPD_Bookmark, NODEINFO>(pCurrentBM, nodeInfo));
        g_mapTreeNodeCount++;
    }

    FPD_Bookmark pdf_BM1 = FPDBookmarkNew(nullptr);
    newa0.push_back((int)pdf_BM1);
    delte0.insert(std::pair<int, bool>((int)pdf_BM1, true));
    FPDBookmarkGetFirstChild(m_fpdDoc, pCurrentBM, &pdf_BM1);

    if (!FPDBookmarkGetDictionary(pdf_BM1))
    {
        return;
    }
    else
    {
        SetParent(pdf_BM1,pCurrentBM);
        GetTreeTopologyInfo(m_fpdDoc, pdf_BM1);
    }

    FPD_Bookmark  temp1 = pdf_BM1;
    while (1)
    {
        FPD_Bookmark pdf_BM2 = FPDBookmarkNew(nullptr);
        //if (delte.find((int)pdf_BM2) != delte.end())
        newa1.push_back((int)pdf_BM2);
        delte1.insert(std::pair<int, bool>((int)pdf_BM2, true));

       

        FPDBookmarkGetNextSibling(m_fpdDoc, temp1, &pdf_BM2);
        if (temp1&&temp1 != pdf_BM1)
        {
            FPDBookmarkDestroy(temp1);
        }
        if (!FPDBookmarkGetDictionary(pdf_BM2))
        {
            FPDBookmarkDestroy(pdf_BM2);
            FPDBookmarkDestroy(pdf_BM1);
            return;
        }
        else {
            SetParent(pdf_BM2, pCurrentBM);
            SetPrevSibling(pdf_BM2, temp1);
            GetTreeTopologyInfo(m_fpdDoc, pdf_BM2);
            temp1 = pdf_BM2;
        }

    }
    FPDBookmarkDestroy(pdf_BM1);
}

其中带有赋值调试信息,下面进行优化后的简要分析:

1.使用Vector记录了分配的内存的地址,会包含重复部分,使用Map记录分配的内存,不包含重复部分;我们可以看到不管是外层深度和while循环里面广度的FPDBookmarkNew都会使得后来分配的地址和之前的一样,也即之前某一块释放后,第二次会再次拿到之地址和之前一样;因此一定要注意,不能根据分配的指针,来统计一共有多少个Node,而是要根据分配地址中的附加数据来得到真实的节点数。也可以根据g_mapTreeNodeCount来初步的统计Node的数目;当然如果第一部分不释放内存的话,是可以正常根据Map中记录的指针地址来判断所有地址,但是显然这样是不严谨,存在内存泄露问题的。总结,申请的内存未释放,不会再申请到这块,而申请的内存已释放,可能还会申请得到之前的地址,记录分配的地址要小心谨慎,最好不要用地址作为唯一的ID,有隐患,而要使用具体特定的数值

2.第二段势力给出类释放内存Destroy的处理,如果是手动释放的话,在GetTreeTopologyInfo这个函数中不管是自然退出即函数尾部和里面的return提前退出都需要对pdf_BM1进行释放,pdf_BM2的上次值是由temp1记录的,每次循环释放的是temp1,而最后一次是pdf_BM2本身,这点需要注意。手动释放的话,随着函数的不断复杂化,如加了Return,或者新分配对象,维护起来也会比较麻烦些,因此最后能使用智能指针会简化些,以此提高效率;可以使用std::shared_ptr,std::unique_ptr去配合空结构去定义自己的删除器,也可以自己进行简单的二次封装。

3.关于树形节点的数目,做一个分析,newa0 相当于一共有多少个节点加上跟节点,根节点为NULL,也要算上,因为没进一次代表了有一个节点,甚至第一次是空的也会计算在内;newa0相当于所有的节点数目,不包含空的跟节点,应为while中相当于把所有的节点统计一遍,及时说没有循环直接退出,那么第一个的BM1也可看作最深的字节点;下面给出运行的截图:

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值