算法——二叉樹


定義:

二叉樹(Binary Tree)是n(n>=0)個節點的有限集合,該集合或者空集(稱為空二叉樹),或者由一個根節點和兩棵互不相交的,分別稱為根節點的左子樹和右子樹的二叉樹組成。

 

特點:

  • 每個結點最多有兩棵子樹,所以二叉樹中不存在度大於2的結點。注意不是只有兩棵子樹,而是最多有。沒有子樹或者有一棵子樹都是可以的。
  • 左子樹和右子樹是有順序的,次序不能任意顛倒。就像人是雙手、雙腳,但顯然左手、左腳和右手、右腳是不一樣的,右手戴左手套、右腳穿左鞋都會極其別扭和難受。
  • 即使樹中某結點只有一棵子樹,也要區分它是左子樹還是右子樹。

 

二叉樹的五種形態:

  1. 空二叉樹
  2. 只有一個根節點
  3. 根節點只有左子樹
  4. 根節點只有右子樹
  5. 根節點既有左子樹又有右子樹

 

特殊二叉樹:

  • 斜樹:所有的節點都只有左子樹的二叉樹叫做左斜樹,所有的節點都只有右子樹的二叉樹叫做右斜樹。這兩者統稱為斜樹。
  • 滿二叉樹:在一棵二叉樹中,如果所有分支節點都存在左子樹和右子樹,並且所有葉子都在同一層,這樣的二叉樹稱為滿二叉樹
  • 完全二叉樹:對一棵具有n個結點的二叉樹按層序編號,如果編號為i (1<=i<=n)的結點與同樣深度的滿二叉樹中編號為i的結點在二叉樹中位置完全相同,則這棵二叉樹稱為完全二叉樹。

 

二叉樹性質:

  • 性質1:在二叉樹的第i層上至多有2^(i-1)個節點(i>=1)
  • 性質2:深度為k的二叉樹至多有2^k-1個節點(k>=1)
  • 性質3:對任何一棵二叉樹T,如果其終端節點數為n0,度為2的節點數為n2,則n0=n2+1

 

二叉樹遍歷:

二叉樹的遍歷( traversing binary tree )是指從根結點出發,按照某種次序依次訪問二叉樹中所有結點,使得每個結點被訪問一次且僅被訪問一次。

 

二叉樹遍歷方法:

  • 前序遍歷:規則是若二叉樹為空,則空操作返回,否則先訪問根結點,然后前序遍歷左子樹,再前序遍歷右子樹。如圖所示,遍歷的順序為:ABDGHCEIF。

 

 

  • 中序遍歷:規則是若樹為空,則空操作返回,否則從根結點開始(注意並不是先訪問根結點),中序遍歷根結點的左子樹,然后是訪問根結點,最后中序遍歷右子樹。如圖所示,遍歷的順序為:GDHBAEICF。

 

 

  • 后序遍歷:規則是若樹為空,則空操作返回,否則從左到右先葉子后結點的方式遍歷訪向左右子樹,最后是訪問根結點。如圖所示,遍歷的順序為:GHDBIEFCA。

 

 

  • 層序遍歷:規則是若樹為空,則空操作返回,否則從樹的第一層, 也就是根結點開始訪問,從上而下逐層遍歷,在同一層中,按從左到右的順序對結點逐個訪問。如圖所示,遍歷的順序為:ABCDEFGHI。

 

 

 

二叉搜索樹的實現:

定義:

若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; 它的左、右子樹也分別為二叉排序樹。如下圖所示:

 

 

 

代碼如下:

二叉樹的節點類:

/**
 * 	二叉樹的結點類
 * @author wydream
 *
 */

public class Node {

	int data;//節點數據
	Node leftChild;//左子節點的引用
	Node rightChild;//右子節點的引用
	boolean isDelete;//表示節點是否被刪除
	
	public Node(int data) {
		this.data=data;
	}
	
	//打印節點內容
	public void display() {
		System.out.println(data);
	}
	
}

  

二叉樹的接口:

/**
 * 	二叉樹的具體方法
 * @author wydream
 *
 */

public interface Tree {

	//查找節點
    public Node find(int key);
    //插入新節點
    public boolean insert(int data);
     
    //中序遍歷
    public void infixOrder(Node current);
    //前序遍歷
    public void preOrder(Node current);
    //后序遍歷
    public void postOrder(Node current);
     
    //查找最大值
    public Node findMax();
    //查找最小值
    public Node findMin();
     
    //刪除節點
    public boolean delete(int key);	
}

  

二叉樹的具體實現:

import org.junit.jupiter.api.Test;

public class BinaryTree implements Tree {
	
	private Node root;//根節點

	@Override
	public Node find(int key) {
		Node current=root;
		while(current!=null) {
			if(current.data>key) {//當前值比查找值大,搜索左子樹
				current=current.leftChild;
			}else if(current.data<key) {//當前值比查找值小,搜索右子樹
				current=current.rightChild;
			}else {
				return current;
			}
		}
		return null;//遍歷完整個樹沒找到,返回null
	}

	@Override
	public boolean insert(int key) {
		
		Node newNode=new Node(key);
		if(root==null) {//當前樹為空樹,沒有任何節點
			root=newNode;
			return true;
		}else {
			Node current=root;
			Node parentNode=null;
			while(current!=null) {
				parentNode=current;
				if(current.data>key) {//當前值比插入值大,搜索左子節點
					current=current.leftChild;
					if(current==null) {//左孩子為空,則插入該節點到左孩子
						parentNode.leftChild=newNode;
						return true;
					}
				}else {//當前值比插入值小,搜索右子節點
					current=current.rightChild;
					if(current==null) {//右孩子為空,則插入該節點到右孩子
						parentNode.rightChild=newNode;
						return true;
					}
				}
			}
		}
		return false;
	}

	//刪除節點
	@Override
	public boolean delete(int key) {
		Node current=root;
		Node parent=root;
		boolean isLeftChild=false;
		//查找刪除值,找不到直接返回false
		while(current.data!=key) {
			parent=current;
			if(current.data>key) {
				isLeftChild=true;
				current=current.leftChild;
			}else {
				isLeftChild=false;
				current=current.rightChild;
			}
			if(current==null) {
				return false;
			}
		}
		
		//如果當前節點沒有子節點
		if(current.leftChild==null&&current.rightChild==null) {
			if(current==root) {
				root=null;
			}else if(isLeftChild) {
				parent.leftChild=null;
			}else {
				parent.rightChild=null;
			}
			return true;
		}else if(current.leftChild==null&&current.rightChild!=null){//當前節點有一個子節點,右子節點
			if(current==root) {
				root=current.rightChild;
			}else if(isLeftChild) {
				parent.leftChild=current.rightChild;
			}else {
				parent.rightChild=current.rightChild;
			}
			return true;
		}else if(current.rightChild==null&&current.leftChild!=null) {//當前節點有一個子節點,左子節點
			if(current==root) {
				root=current.leftChild;
			}else if(isLeftChild) {
				parent.leftChild=current.leftChild;
			}else {
				parent.rightChild=current.leftChild;
			}
			return true;
		}else {//當前節點存在兩個子節點
			Node successor = getSuccessor(current);
            if(current == root){
                root= successor;
            }else if(isLeftChild){
                parent.leftChild = successor;
            }else{
                parent.rightChild = successor;
            }
            successor.leftChild = current.leftChild;
		}
		
		
		return false;
	}
	
	//中序遍歷:左子樹——》根節點——》右子樹
	public void infixOrder(Node current) {
		if(current!=null) {
			infixOrder(current.leftChild);
			System.out.println(current.data);
			infixOrder(current.rightChild);
		}
	}
	
	//前序遍歷:根節點——》左子樹——》右子樹
	public void preOrder(Node current) {
		if(current!=null) {
			System.out.println(current.data);
			preOrder(current.leftChild);
			preOrder(current.rightChild);
		}
	}
	

	//后序遍歷:左子樹——》右子樹——》根節點
	public void postOrder(Node current) {
		if(current!=null) {
			postOrder(current.leftChild);
			postOrder(current.rightChild);
			System.out.println(current.data);
		}
	}
	
	//查找最小值
	public Node findMin() {
		Node current=root;
		Node minNode=current;
		while(current!=null) {
			minNode=current;
			current=current.leftChild;
		}
		return minNode;
	}
	
	
	//查找最大值
	public Node findMax() {
		Node current=root;
		Node maxNode=current;
		while(current!=null) {
			maxNode=current;
			current=current.rightChild;
		}
		return maxNode;
	}
	
	public Node getSuccessor(Node delNode) {
		Node successorParent=delNode;
		Node successor=delNode;
		Node current=delNode.rightChild;
		while(current!=null) {
			successorParent=successor;
			successor=current;
			current=current.leftChild;
		}
		//后繼節點不是刪除節點的右子節點,將后繼節點替換刪除節點
		if(successor!=delNode.rightChild) {
			successorParent.leftChild=successor.rightChild;
			successor.rightChild=delNode.rightChild;
		}
		return successor;
	}
	
	//測試
	@Test
	public void test() {
		BinaryTree bt = new BinaryTree();
        bt.insert(50);
        bt.insert(20);
        bt.insert(80);
        bt.insert(10);
        bt.insert(30);
        bt.insert(60);
        bt.insert(90);
        bt.insert(25);
        bt.insert(85);
        bt.insert(100);
        bt.delete(10);//刪除沒有子節點的節點
        bt.delete(30);//刪除有一個子節點的節點
        bt.delete(80);//刪除有兩個子節點的節點
        System.out.println(bt.findMax().data);
        System.out.println(bt.findMin().data);
        System.out.println(bt.find(100));
        System.out.println(bt.find(200));
        System.out.println("=====中序遍歷=====");
		infixOrder(bt.root);
		System.out.println("=====前序遍歷=====");
		preOrder(bt.root);
		System.out.println("=====后序遍歷=====");
		postOrder(bt.root);
		
	}
	
}

  

測試結果:

100
20
com.alibaba.test11.tree.Node@ed7f8b4
null
=====中序遍歷=====
20
25
50
60
85
90
100
=====前序遍歷=====
50
20
25
85
60
90
100
=====后序遍歷=====
25
20
60
100
90
85
50

  

本博客代碼參考:https://www.cnblogs.com/ysocean/p/8032642.html#_label9

 


免責聲明!

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



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