Java中樹的存儲結構實現


一、樹

樹與線性表、棧、隊列等線性結構不同,樹是一種非線性結構。

一棵樹只有一個根節點,如果一棵樹有了多個根節點,那它已經不再是一棵樹了,而是多棵樹的集合,也被稱為森林。

二、樹的父節點表示法

樹中除根節點之外每個節點都有一個父節點,為了記錄樹中節點與節點之間的父子關系,可以為每個節點增加一個parent域,用以記錄該節點的父節點。

  1 package com.ietree.basic.datastructure.tree;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 
  6 /**
  7  * Created by ietree
  8  * 2017/4/30
  9  */
 10 public class TreeParent<E> {
 11 
 12     public static class Node<T> {
 13 
 14         T data;
 15         // 保存其父節點的位置
 16         int parent;
 17 
 18         public Node() {
 19 
 20         }
 21 
 22         public Node(T data) {
 23             this.data = data;
 24         }
 25 
 26         public Node(T data, int parent) {
 27             this.data = data;
 28             this.parent = parent;
 29         }
 30 
 31         public String toString() {
 32             return "TreeParent$Node[data=" + data + ", parent=" + parent + "]";
 33         }
 34 
 35     }
 36 
 37     private final int DEFAULT_TREE_SIZE = 100;
 38     private int treeSize = 0;
 39     // 使用一個Node[]數組來記錄該樹里的所有節點
 40     private Node<E>[] nodes;
 41     // 記錄樹的節點數
 42     private int nodeNums;
 43 
 44     // 以指定節點創建樹
 45     public TreeParent(E data) {
 46         treeSize = DEFAULT_TREE_SIZE;
 47         nodes = new Node[treeSize];
 48         nodes[0] = new Node<E>(data, -1);
 49         nodeNums++;
 50     }
 51 
 52     // 以指定根節點、指定treeSize創建樹
 53     public TreeParent(E data, int treeSize) {
 54         this.treeSize = treeSize;
 55         nodes = new Node[treeSize];
 56         nodes[0] = new Node<E>(data, -1);
 57         nodeNums++;
 58     }
 59 
 60     // 為指定節點添加子節點
 61     public void addNode(E data, Node parent) {
 62         for (int i = 0; i < treeSize; i++) {
 63             // 找到數組中第一個為null的元素,該元素保存新節點
 64             if (nodes[i] == null) {
 65                 // 創建新節點,並用指定的數組元素保存它
 66                 nodes[i] = new Node(data, pos(parent));
 67                 nodeNums++;
 68                 return;
 69             }
 70         }
 71         throw new RuntimeException("該樹已滿,無法添加新節點");
 72     }
 73 
 74     // 判斷樹是否為空
 75     public boolean empty() {
 76         // 根結點是否為null
 77         return nodes[0] == null;
 78     }
 79 
 80     // 返回根節點
 81     public Node<E> root() {
 82         // 返回根節點
 83         return nodes[0];
 84     }
 85 
 86     // 返回指定節點(非根結點)的父節點
 87     public Node<E> parent(Node node) {
 88         // 每個節點的parent記錄了其父節點的位置
 89         return nodes[node.parent];
 90     }
 91 
 92     // 返回指定節點(非葉子節點)的所有子節點
 93     public List<Node<E>> children(Node parent) {
 94         List<Node<E>> list = new ArrayList<Node<E>>();
 95         for (int i = 0; i < treeSize; i++) {
 96             // 如果當前節點的父節點的位置等於parent節點的位置
 97             if (nodes[i] != null && nodes[i].parent == pos(parent)) {
 98                 list.add(nodes[i]);
 99             }
100         }
101         return list;
102     }
103 
104     // 返回該樹的深度
105     public int deep() {
106         // 用於記錄節點的最大深度
107         int max = 0;
108         for (int i = 0; i < treeSize && nodes[i] != null; i++) {
109             // 初始化本節點的深度
110             int def = 1;
111             // m 記錄當前節點的父節點的位置
112             int m = nodes[i].parent;
113             // 如果其父節點存在
114             while (m != -1 && nodes[m] != null) {
115                 // 向上繼續搜索父節點
116                 m = nodes[m].parent;
117                 def++;
118             }
119             if (max < def) {
120                 max = def;
121             }
122         }
123         return max;
124     }
125 
126     // 返回包含指定值的節點
127     public int pos(Node node) {
128         for (int i = 0; i < treeSize; i++) {
129             // 找到指定節點
130             if (nodes[i] == node) {
131                 return i;
132             }
133         }
134         return -1;
135     }
136 
137 }

測試類:

 1 package com.ietree.basic.datastructure.tree;
 2 
 3 import java.util.List;
 4 
 5 /**
 6  * Created by ietree
 7  * 2017/4/30
 8  */
 9 public class treeParentTest {
10 
11     public static void main(String[] args) {
12 
13         TreeParent<String> tp = new TreeParent<String>("root");
14         TreeParent.Node root = tp.root();
15         System.out.println(root);
16         tp.addNode("節點1", root);
17         System.out.println("此樹的深度:" + tp.deep());
18         tp.addNode("節點2", root);
19         // 獲取根節點的所有子節點
20         List<TreeParent.Node<String>> nodes = tp.children(root);
21         System.out.println("根節點的第一個子節點:" + nodes.get(0));
22         // 為根節點的第一個子節點新增一個子節點
23         tp.addNode("節點3", nodes.get(0));
24         System.out.println("此樹的深度:" + tp.deep());
25 
26     }
27 }

程序輸出:

TreeParent$Node[data=root, parent=-1]
此樹的深度:2
根節點的第一個子節點:TreeParent$Node[data=節點1, parent=0]
此樹的深度:3

三、子節點鏈表示法

讓父節點記住它的所有子節點。

  1 package com.ietree.basic.datastructure.tree;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 
  6 /**
  7  * Created by ietree
  8  * 2017/4/30
  9  */
 10 public class TreeChild<E> {
 11 
 12     private static class SonNode {
 13         // 記錄當前節點的位置
 14         private int pos;
 15         private SonNode next;
 16 
 17         public SonNode(int pos, SonNode next) {
 18             this.pos = pos;
 19             this.next = next;
 20         }
 21     }
 22 
 23     public static class Node<T> {
 24         T data;
 25         // 記錄第一個子節點
 26         SonNode first;
 27 
 28         public Node(T data) {
 29             this.data = data;
 30             this.first = null;
 31         }
 32 
 33         public String toString() {
 34             if (first != null) {
 35                 return "TreeChild$Node[data=" + data + ", first=" + first.pos + "]";
 36             } else {
 37                 return "TreeChild$Node[data=" + data + ", first=-1]";
 38             }
 39         }
 40     }
 41 
 42     private final int DEFAULT_TREE_SIZE = 100;
 43     private int treeSize = 0;
 44     // 使用一個Node[]數組來記錄該樹里的所有節點
 45     private Node<E>[] nodes;
 46     // 記錄節點數
 47     private int nodeNums;
 48 
 49     // 以指定根節點創建樹
 50     public TreeChild(E data) {
 51         treeSize = DEFAULT_TREE_SIZE;
 52         nodes = new Node[treeSize];
 53         nodes[0] = new Node<E>(data);
 54         nodeNums++;
 55     }
 56 
 57     // 以指定根節點、指定treeSize創建樹
 58     public TreeChild(E data, int treeSize) {
 59         this.treeSize = treeSize;
 60         nodes = new Node[treeSize];
 61         nodes[0] = new Node<E>(data);
 62         nodeNums++;
 63     }
 64 
 65     // 為指定節點添加子節點
 66     public void addNode(E data, Node parent) {
 67         for (int i = 0; i < treeSize; i++) {
 68             // 找到數組中第一個為null的元素,該元素保存新節點
 69             if (nodes[i] == null) {
 70                 // 創建新節點,並用指定數組元素保存它
 71                 nodes[i] = new Node(data);
 72                 if (parent.first == null) {
 73                     parent.first = new SonNode(i, null);
 74                 } else {
 75                     SonNode next = parent.first;
 76                     while (next.next != null) {
 77                         next = next.next;
 78                     }
 79                     next.next = new SonNode(i, null);
 80                 }
 81                 nodeNums++;
 82                 return;
 83             }
 84         }
 85         throw new RuntimeException("該樹已滿,無法添加新節點");
 86     }
 87 
 88     // 判斷樹是否為空
 89     public boolean empty() {
 90         // 根結點是否為null
 91         return nodes[0] == null;
 92     }
 93 
 94     // 返回根節點
 95     public Node<E> root() {
 96         // 返回根節點
 97         return nodes[0];
 98     }
 99 
100     // 返回指定節點(非葉子節點)的所有子節點
101     public List<Node<E>> children(Node parent) {
102 
103         List<Node<E>> list = new ArrayList<Node<E>>();
104         // 獲取parent節點的第一個子節點
105         SonNode next = parent.first;
106         // 沿着孩子鏈不斷搜索下一個孩子節點
107         while (next != null) {
108             // 添加孩子鏈中的節點
109             list.add(nodes[next.pos]);
110             next = next.next;
111         }
112         return list;
113 
114     }
115 
116     // 返回指定節點(非葉子節點)的第index個子節點
117     public Node<E> child(Node parent, int index) {
118         // 獲取parent節點的第一個子節點
119         SonNode next = parent.first;
120         // 沿着孩子鏈不斷搜索下一個孩子節點
121         for (int i = 0; next != null; i++) {
122             if (index == i) {
123                 return nodes[next.pos];
124             }
125             next = next.next;
126         }
127         return null;
128     }
129 
130     // 返回該樹的深度
131     public int deep() {
132         // 獲取該樹的深度
133         return deep(root());
134     }
135 
136     // 這是一個遞歸方法:每棵子樹的深度為其所有子樹的最大深度 + 1
137     private int deep(Node node) {
138         if (node.first == null) {
139             return 1;
140         } else {
141             // 記錄其所有子樹的最大深度
142             int max = 0;
143             SonNode next = node.first;
144             // 沿着孩子鏈不斷搜索下一個孩子節點
145             while (next != null) {
146                 // 獲取以其子節點為根的子樹的深度
147                 int tmp = deep(nodes[next.pos]);
148                 if (tmp > max) {
149                     max = tmp;
150                 }
151                 next = next.next;
152             }
153             // 最后,返回其所有子樹的最大深度 + 1
154             return max + 1;
155         }
156     }
157 
158     // 返回包含指定值得節點
159     public int pos(Node node) {
160         for (int i = 0; i < treeSize; i++) {
161             // 找到指定節點
162             if (nodes[i] == node) {
163                 return i;
164             }
165         }
166         return -1;
167     }
168 
169 }

測試類:

 1 package com.ietree.basic.datastructure.tree;
 2 
 3 import java.util.List;
 4 
 5 /**
 6  * Created by ietree
 7  * 2017/4/30
 8  */
 9 public class TreeChildTest {
10 
11     public static void main(String[] args) {
12 
13         TreeChild<String> tp = new TreeChild<String>("root");
14         TreeChild.Node root = tp.root();
15         System.out.println(root);
16         tp.addNode("節點1", root);
17         tp.addNode("節點2", root);
18         tp.addNode("節點3", root);
19         System.out.println("添加子節點后的根結點:" + root);
20         System.out.println("此樹的深度:" + tp.deep());
21         // 獲取根節點的所有子節點
22         List<TreeChild.Node<String>> nodes = tp.children(root);
23         System.out.println("根節點的第一個子節點:" + nodes.get(0));
24         // 為根節點的第一個子節點新增一個子節點
25         tp.addNode("節點4", nodes.get(0));
26         System.out.println("此樹第一個子節點:" + nodes.get(0));
27         System.out.println("此樹的深度:" + tp.deep());
28 
29     }
30 
31 }

程序輸出:

TreeChild$Node[data=root, first=-1]
添加子節點后的根結點:TreeChild$Node[data=root, first=1]
此樹的深度:2
根節點的第一個子節點:TreeChild$Node[data=節點1, first=-1]
此樹第一個子節點:TreeChild$Node[data=節點1, first=4]
此樹的深度:3

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM