在树结构中,一般有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也可看作最深的字节点;下面给出运行的截图: