二叉樹的鏈式儲存實現及遍歷
阿新 • • 發佈:2019-02-02
關於二叉樹鏈式實現,常見的為如下兩種:
- 二叉連結串列儲存: 每個節點保留一個left,right域,指向左右孩子
- 三叉連結串列儲存: 每個節點保留一個left, right, parent域,指向左右孩子和父親
故對於每個二叉樹的節點大致有如下的定義:
class Node{
T data;
Node left;
Node right;
Node parent; //非必要
}
不難看出對於該節點新增子節點非常容易,只需要讓left或right指向子節點即可。
使用鏈式儲存方式實現二叉樹的簡易程式碼如下:
public class LinkBinaryTree<T> {
public class TreeNode{
T data;
TreeNode left;
TreeNode right;
public TreeNode() {
}
public TreeNode(T data) {
this.data = data;
}
public TreeNode(T data, TreeNode left, TreeNode right) {
this.data = data;
this.left = left;
this .right = right;
}
@Override
public String toString() {
return "data:"+data+";left:"+left.data+";right:"+right.data;
}
}
private TreeNode root;
public LinkBinaryTree() {
this.root = new TreeNode();
}
//以指定根元素建立二叉樹
public LinkBinaryTree(T data) {
this .root = new TreeNode(data);
}
/**
* 為指定節點新增子節點
* @param parent 需要新增子節點的父節點
* @param data 新節點的資料
* @param isLeft 是否為父節點的左孩子
* @return 新的節點
*/
public TreeNode addNode(TreeNode parent, T data, Boolean isLeft) {
TreeNode treeNode = new TreeNode(data);
if(parent == null) {
throw new RuntimeException("節點為null,無法新增子節點");
}
if(isLeft && parent.left !=null) {
throw new RuntimeException("已存在左孩子,無法新增");
}
if(!isLeft && parent.right !=null) {
throw new RuntimeException("已存在右孩子,無法新增");
}
TreeNode newNode = new TreeNode(data);
if(isLeft) {
parent.left = newNode;
}else {
parent.right = newNode;
}
return newNode;
}
public TreeNode root() {
return root;
}
//返回某個節點的父節點
public TreeNode parent(TreeNode temp) {
List<T> list = new ArrayList<T>();
Queue<TreeNode> stack = new LinkedList<TreeNode>();
stack.add(this.root());
while(!stack.isEmpty()) {
TreeNode node = stack.poll();
if(node.left == temp || node.right == temp) {
return node;
}
if(node.left!=null) {
stack.add(node.left);
}
if(node.right!=null) {
stack.add(node.right);
}
}
System.out.println("父節點不存在");
return null;
}
//返回某個節點的左孩子
public T left(TreeNode parent) {
if(parent == null) {
throw new RuntimeException("父節點為空");
}
return parent.left ==null ?null : parent.left.data;
}
//返回某個節點的右孩子
public T right(TreeNode parent) {
if(parent == null) {
throw new RuntimeException("父節點為空");
}
return parent.right ==null ?null : parent.right.data;
}
public int deep() {
return this.deep(this.root());
}
//獲取某個節點的深度
//某個節點的深度為其最深子樹的深度+1
public int deep(TreeNode node) {
if(node == null) {
return 0;
}
if(node.left == null && node.right == null) {
return 0;
}
int leftDeep = this.deep(node.left);
int rightDeep = this.deep(node.right);
int max = leftDeep>rightDeep? leftDeep: rightDeep;
System.out.println("max:"+max);
return max+1;
}
//先序遍歷
public List<T> travPreRecursion() {
return this.preRecursion(this.root());
}
private List<T> preRecursion(TreeNode node) {
List<T> list = new ArrayList<T>();
list.add(node.data);
if (node.left!=null) {
list.addAll(preRecursion(node.left));// 左兒子
}
if (node.right!=null) {
list.addAll(preRecursion(node.right));// 右兒子
}
return list;
}
// 中序遍歷(遞迴)
public List<T> travInRecursion() {
return this.inRecursion(this.root());
}
public List<T> inRecursion(TreeNode node) {
List<T> list = new ArrayList<T>();
if (node.left!=null) {
list.addAll(inRecursion(node.left));// 左兒子
}
list.add(node.data);
if (node.right!=null) {
list.addAll(inRecursion(node.right));// 右兒子
}
return list;
}
// 後序遍歷(遞迴)
public List<T> travPostRecursion() {
return postRecursion(this.root());
}
public List<T> postRecursion(TreeNode node) {
List<T> list = new ArrayList<T>();
if (node.left !=null) {
list.addAll(postRecursion(node.left));// 左兒子
}
if (node.right !=null) {
list.addAll(postRecursion(node.right));// 右兒子
}
list.add(node.data);
return list;
}
}
現在根據該二叉樹類來建立一個二叉樹:
public static void linkBinaryTree() {
//首節點
LinkBinaryTree<String> binaryTree = new LinkBinaryTree<String>("a");
//首節點a的左子數為b
LinkBinaryTree<String>.TreeNode rootL = binaryTree.addNode(binaryTree.root(), "b", true);
//首節點a的左子數為c
LinkBinaryTree<String>.TreeNode rootR = binaryTree.addNode(binaryTree.root(), "c", false);
System.out.println(binaryTree.root());
LinkBinaryTree<String>.TreeNode rootLL = binaryTree.addNode(rootL, "d", true);
LinkBinaryTree<String>.TreeNode rootLR = binaryTree.addNode(rootL, "e", false);
System.out.println(rootL);
LinkBinaryTree<String>.TreeNode rootRL = binaryTree.addNode(rootR, "f", true);
LinkBinaryTree<String>.TreeNode rootRR = binaryTree.addNode(rootR, "g", false);
//獲取樹的深度
System.out.println(binaryTree.deep());
}
遍歷
與之前介紹的順序儲存一樣,對於迭代式遍歷不做過多的介紹,重點在迴圈式遍歷,至於具體的遍歷過程與筆者的另一篇關於二叉樹的順序儲存的實現和遍歷中介紹的很情況,這裡只介紹廣度優先遍歷
廣度優先遍歷即按二叉樹的層來遍歷,先第一層,第二層,…最後一層,比深度優先遍歷簡單程式碼也很容易的理解
已上一個binaryTree類為例:
public List<T> bfs(){
List<T> list = new ArrayList<T>();
//定義一個佇列(先進先出) LinkedList可以作為佇列使用
Queue<TreeNode> stack = new LinkedList<TreeNode>();
//首節點入佇列
stack.add(this.root());
while(!stack.isEmpty()) {
//取佇列首節點,
TreeNode node = stack.poll();
list.add(node.data);
//新增其孩子入佇列
if(node.left !=null )
stack.add(node.left);
if(node.right!=null)
stack.add(node.right);
}
return list;
}
關於二叉樹儲存的實現,如果二叉樹是完全二叉樹,則建議使用順序儲存,順序儲存的二叉樹進行廣度優先遍歷非常容易,但如果不是完全二叉樹則使用順序儲存空間浪費較大,不建議。