基於順序儲存實現的多叉樹(1):深度優先儲存
在資料結構中,樹有兩種儲存方式,一種是鏈式儲存,另一種是順序儲存。前者就是使用指標來記錄樹結點間的關係,在新增結點或刪除結點時,只需改變與父結點或兄弟結點的指標值即可,實現較為簡單;後者就是使用陣列來儲存,可以用相對偏移量來記錄樹結點間的關係,在新增結點或刪除結點時,則不僅是改變與父結點或兄弟結點的相對偏移量,還需要改變其它結點的相對偏移量,實現較為複雜。近來在專案中,對一個普通文字檔案進行分析提取資料,而這個檔案內的資料從內容看,具有層次巢狀關係,要將這樣的資料傳送到伺服器去處理,我考慮了兩種如下方法:
(1)自定義XML格式,在本地使用XML庫,如libxml2、tinyxml等,將資料寫到XML臨時檔案或記憶體中,再將這個XML臨時檔案或記憶體發過去,在伺服器那邊使用XML庫來解析。這種方法比較通用而且跨平臺,如果XML庫不支援將其所儲存的資料轉儲到一塊連續記憶體,那麼就只能先存到XML臨時檔案,再將這個檔案發過去,這樣一來,就存在磁碟IO操作,效率較低。否則,就可以先將資料轉儲到一塊連續記憶體,再將這塊記憶體發過去,這樣一來,這塊連續記憶體就需要另外開闢,因此多了一套記憶體管理操作,但是比用臨時檔案方式,沒有磁碟IO,效率要高些。
(2)實現基於順序儲存的樹,而且還是多叉樹,因為實際資料具有多層次巢狀關係,將資料放進這顆樹中,再直接將這顆樹發過去,在伺服器那邊直接解析這顆樹,這樣一來,不用臨時檔案,沒有磁碟IO,無須另外開闢記憶體,充分利有現有空間,效率較高。
設計開發
從伺服器效率至上的觀點考慮,我選擇了第2種方法,並實現了基於順序儲存的多叉樹,關於順序儲存,又有兩種如下方式:
(1)深度優先儲存,按照自上而下從左到右儲存樹的所有結點,先儲存結點及它的孩子,再儲存它的兄弟。因此結點的孩子和兄弟都不一定是連續的,當一個結點的所有孩子都是葉子結點時,則所有孩子是連續存放的。結點和它的第一個孩子(若有)是連續的,如下圖所示
(2)廣度優先儲存,按照從左到右自上而下儲存樹的所有結點,先儲存結點及它的兄弟,再儲存它的孩子,因此結點的孩子和兄弟都是連續存放的,孩子與其父親之間不一定是連續的,如下圖所示
本文描述第1種儲存方式實現的多叉樹,介紹三種主要操作:設定根結點、增加結點和刪除結點,為簡單起見,使用vector作為內部動態陣列,使用索引而非迭代器作為外部介面,來訪問結點,索引0表示空索引,有效索引從1開始。關於迭代器的設計,有諸多考慮,如前序、後序、廣度優先、指定深度、葉子結點等各種遍歷方法,因時間和篇幅原因,不能一一講述,待後面有時間會陸續補充完善。
3{
4 size_t parent_;
5 size_t first_child_;
6 size_t last_child_;
7 size_t prev_sibling_;
8 size_t next_sibling_;
9 T data_;
10
11 order_tree_node();
12 order_tree_node(const T& data);
13}; 為了方便,定義了order_tree_node的兩個建構函式,其實現如下 1 template<typename T> 2 order_tree_node<T>::order_tree_node()
3 :parent_(0)
4 ,first_child_(0)
5 ,last_child_(0)
6 ,prev_sibling_(0)
7 ,next_sibling_(0)
8 {
9 }10 template<typename T>11 order_tree_node<T>::order_tree_node(const T& data)
12 :parent_(0)
13 ,first_child_(0)
14 ,last_child_(0)
15 ,prev_sibling_(0)
16 ,next_sibling_(0)
17 ,data_(data)
18 {
19 } 2)設定根結點,為方便實現,根結點固定存放在陣列中第1個位置,對應下標為0,C++程式碼描述如下 1 template<typename T> 2 inline typename mtree<T,false>::iterator_base mtree<T,false>::set_root(const T& val)
3 {
4 if (!base_type::empty())
5 {
6 *(get_root()) = val;
7 } 8 else 9 {
10 tree_node node(val);
11 push_back(node);
12 }13 return iterator_base(this,0);
14 } 這裡用到了get_root函式來獲取根結點,其實現如下 1 template<typename T> 2 inline typename mtree<T,false>::iterator_base mtree<T,false>::get_root()
3 {
4 return iterator_base(this,0);
5 } 6 template<typename T> 7 inline typename mtree<T,false>::const_iterator_base mtree<T,false>::get_root() const 8 {
9 return const_iterator_base(this,0);
10 } 3)增加結點,這裡要分為三步,第一步要找到插入位置,第二步插入結點,第三步改變相關結點的相對偏移量,這裡相關結點包括當前所插結點、所插結點兄弟結點、父結點、祖先結點及其右兄弟結點;注意,這裡可以作一些異常安全考慮,即如果第二步操作失敗了,則可直接返回,這樣就可保證整顆樹不受影響。為了簡單起見,以下C++程式碼對異常安全沒有作處理,描述如下 1 template<typename T> 2 template<typename tree_iterator> 3 inline tree_iterator mtree<T,false>::append_child(tree_iterator iter,const T& val)
4 {
5 assert(!iter.is_null());
6 size_t off = append_child(iter.off_,val);
7 tree_iterator it(iter);
8 it.off_ = off;
9 return it;
10 }11 template<typename T>12 inline typename mtree<T,false>::fd_iterator mtree<T,false>::append_child(fd_iterator iter,const T& val)
13 {
14 assert(!iter.is_null());
15 size_t off = append_child(iter.off_,val);
16 fd_iterator it(iter);
17 it.off_ = off; ++it.depth_;
18 return it;
19 } 以上模板成員函式及其深度迭代器的特化版本都呼叫了內部append_child(size_t)函式,該函式實現如下: 1 template<typename T> 2 inline size_t mtree<T,false>::append_child(size_t index,const T& val)
3 {
4 size_t parent = index, pos;
5 tree_node *p_parent =&(*this)[parent],*p_node, *p_child;
6
7 //找到插入位置 8 pos = parent; p_node = p_parent;
9 while (p_node->last_child_)
10 {
11 pos += p_node->last_child_;
12 p_node =&(*this)[pos];
13 }14 size_t child =++pos;
15 //插入結點16 tree_node node(val);
17 if (child >=this->size())
18 push_back(node);
19 else20 base_type::insert(begin()+child,node);
21
22 //更新當前結點的prev_sibling值和其左兄弟結點的next_sibling值23 p_parent =&(*this)[parent];
24 p_child =&(*this)[child];
25 if (p_parent->last_child_)
26 {
27 pos = parent+p_parent->last_child_;
28 (*this)[pos].next_sibling_ = p_child->prev_sibling_ = child-pos;
29 }30 //從父結點開始,向上更新當前結點所有右邊結點的偏移量31 size_t next;
32 tree_node* p_next;
33 pos = parent;
34 do35 {
36 p_node =&(*this)[pos];
37 if (p_node->next_sibling_)
38 {
39 if (p_node->parent_)
40 ++(*this)[pos-p_node->parent_].last_child_;
41 //
相關推薦
基於順序儲存實現的多叉樹(1):深度優先儲存
需求分析 在資料結構中,樹有兩種儲存方式,一種是鏈式儲存,另一種是順序儲存。前者就是使用指標來記錄樹結點間的關係,在新增結點或刪除結點時,只需改變與父結點或兄弟結點的指標值即可,實現較為簡單;後者就是使用陣列來儲存,可以用相對偏移量來記錄樹結點間的關係,在新增結點或刪除結點時,則不僅是改變
基於順序儲存實現的多叉樹(7):深度遍歷
1 template<typename T> 2 template<bool is_const,bool is_reverse> 3 inline typename mtree<T,false>::template fd_iterator_impl<
基於順序儲存實現的多叉樹(6):葉子遍歷
1 template<typename T> 2 template<bool is_const,bool is_reverse> 3 inline typename mtree<T,false>::template leaf_iterator_impl&
LeetCode-105.從前序與中序遍歷序列構造二叉樹(相關話題:深度優先搜尋)
根據一棵樹的前序遍歷與中序遍歷構造二叉樹。 注意: 你可以假設樹中沒有重複的元素。 例如,給出 前序遍歷 preorder = [3,9,20,15,7] 中序遍歷 inorder = [9,3,15,20,7] 返回如下的二叉樹: 3 / \ 9
LeetCode-106.從中序與後序遍歷序列構造二叉樹(相關話題:深度優先搜尋)
根據一棵樹的中序遍歷與後序遍歷構造二叉樹。 注意: 你可以假設樹中沒有重複的元素。 例如,給出 中序遍歷 inorder = [9,3,15,20,7] 後序遍歷 postorder = [9,15,7,20,3] 返回如下的二叉樹: 3 / \
多叉樹(森林)轉二叉樹
本來不怎麼想寫這個,但發現網上的都是“殘疾”部落格,講得不是很詳細,所以我還是要寫一下。 多叉轉二叉有“左兒子右兄弟”的說法,然而對於什麼都不知道的小白,這句話沒有任何用…… 思路 大體就兩步,很好理解,如圖是用來舉慄的多叉樹: 兄弟連 將
資料結構-二叉樹(1)以及前序、中序、後序遍歷(python實現)
上篇文章我們介紹了樹的概念,今天我們來介紹一種特殊的樹——二叉樹,二叉樹的應用很廣,有很多特性。今天我們一一來為大家介紹。 二叉樹 顧名思義,二叉樹就是隻有兩個節點的樹,兩個節點分別為左節點和右節點,特別強調,即使只有一個子節點也要區分它是左節點還是右節點。 常見的二叉樹有一般二叉樹、完全二叉樹、滿二叉樹、線
二叉樹(1)-----遍歷
not nor tree tle pri 遞歸 分享 idt image 一、前序遍歷: 遞歸方式: def preorder(tree): if tree: print(tree.val) preorder(tree.getLef
多執行緒(1):繼承Thread類和實現Runnable介面
多執行緒的兩種實現方法: 1.繼承Thread類 繼承Thread類,重寫run()方法。建立多執行緒的時候,需要建立物件例項,然後呼叫start()方法。類物件的屬性屬於執行緒私有,執行緒之間互不影響。 public class ClassExtendT
遞迴(recursion)演算法與二叉樹(1)
筆者按:曾經剛開始學習資料結構和演算法時,總會為簡潔雋永的遞迴程式碼而驚歎,也想寫出如此優雅的程式碼,但是思考過程真的實屬不易!!!那時候遞迴都會盡量用顯式棧來規避。 生活中的遞迴! 首先,對遞迴要有一個類似盜夢空間或者平行世界的認識,就
LeetCode-109.有序連結串列轉換二叉搜尋樹(相關話題:深度優先)
給定一個單鏈表,其中的元素按升序排序,將其轉換為高度平衡的二叉搜尋樹。 本題中,一個高度平衡二叉樹是指一個二叉樹每個節點 的左右兩個子樹的高度差的絕對值不超過 1。 示例: 給定的有序連結串列: [-10, -3, 0, 5, 9], 一個可能的答案是:[0, -3,
基於雙端堆實現的優先順序佇列(1):原理
前言 眾所周知,stl中的優先順序佇列是基於最大堆實現的,能夠在對數時間內插入元素和獲取優先順序最高的元素,但如果要求在對數時間內還能獲取優先順序最低的元素,而不只是獲取優先順序最高的元素,該怎麼實現呢?可以用最大堆-最小堆或雙端堆資料結構來實現,最大堆-最小堆和雙端堆都是支援雙端優先佇列
自定義樹(1):二叉樹
通過學習自定義樹,瞭解與樹相關的資料結構。 1)樹:n(n>=0)個結點的有限集。 結點的度:結點擁有的子樹的數目 葉子結點(終端結點):度為0的結點 分支結點(非終端結點):度不為0的結點 樹的度:樹中各結點的度的最大值 層次:根結點的層次
SSO 基於Cookie+fliter實現單點登入(SSO):工作原理
SSO的概念: 單點登入SSO(Single Sign-On)是身份管理中的一部分。SSO的一種較為通俗的定義是:SSO是指訪問同一伺服器不同應用中的受保護資源的同一使用者,只需要登入
(十二)Hibernate中的多表操作(1):單向多對一
art 保存 int gen round t對象 情況 映射文件 拋出異常 由“多”方可知“一”方的信息,比如多個員工使用同一棟公寓,員工可以知道公寓的信息,而公寓無法知道員工的信息。 案例一: pojo類 public class Department {
java多執行緒(1):執行緒的建立和多執行緒的安全問題
前言 java多執行緒多用於服務端的高併發程式設計,本文就java執行緒的建立和多執行緒安全問題進行討論。 正文 一,建立java執行緒 建立java執行緒有2種方式,一種是繼承自Thread類,另一種是實現Runnable介面。由於java只支援單
python簡易圖片處理(1):開啟\顯示\儲存圖片
一提到數字影象處理,可能大多數人會想到matlab,但是matlab有自身的一些缺點: 1.不開源,價格貴; 2.軟體容量大。一般3GB以上,高版本甚至達到5GB以上。 3.只易做研究,不易轉化成軟體。 因此,我傾向於學習python來進行影象的處理工作
二叉樹(0)——二叉樹的實現與二叉樹的遍歷
0.二叉樹的實現(C++) 未完,待補充 #include <iostream> #include<iostream> #include<queue> #include<stack> using namespace std; //二叉樹結點的
Multihedgehog(多叉樹找root)
題意: K層樹定義:上面K層節點都有至少三個兒子,第K+1層全是葉子。給出一棵樹結構和K,問這棵樹是不是K叉(不知道root) 解析: 首先是dfs得出每個節點的度,度為1的是葉子。然後葉子開始一層一層往上剪,直到剩下一個節點便是root 找root的時候可
BZOJ 3684 大朋友和多叉樹(生成函式+FFT)
Description 我們的大朋友很喜歡電腦科學,而且尤其喜歡多叉樹。對於一棵帶有正整數點權的有根多叉樹,如果它滿足這樣的性質,我們的大朋友就會將其稱作神犇的:點權為1的結點是葉子結點;對於任一點權大於1的結點u,u的孩子數目deg[u]屬於集合D,且u的點