1. 程式人生 > >普通的二叉搜尋樹總結+java實現

普通的二叉搜尋樹總結+java實現

目錄

二叉搜尋樹的概念

結構

節點類

二叉搜尋樹類

實現-增刪查

插入節點

建立二叉搜尋樹

搜尋節點

刪除節點

應用

搜尋

排序

完整程式碼



二叉搜尋樹的概念

二叉搜尋樹(Binary Search Tree)節點放置:任何節點的鍵值一定大於其左子樹的每一個節點的鍵值,並小於其右子樹的每一個節點的鍵值。

因此,找出BST樹的最大元素和最小元素,就是從根節點一直往左走,直至無左路可走,即得最小元素;從根節點一直往右走,直至無右路可走,即得最大元素。


結構

節點類

有左右節點和值,還有一個列印節點的方法,方便使用

package datastructure.tree.binarysearchtree;

import java.util.LinkedList;
import java.util.Queue;

public class TreeNode {

	int val;
	public TreeNode left;
	public TreeNode right;
	public TreeNode(int x) {
		val = x; 
		left=null;
		right=null;
	}
	
	//通過佇列 當前下層節點個數 列印每層的節點和null,為了方便列印
		public static void printTree(TreeNode root){
	        if(root == null)
	            return;
	        Queue<TreeNode> queue = new LinkedList<TreeNode>();
	        
	        int current;//當前層 還未列印的結點個數
	        int next;//下一層結點個數
	        
	        queue.offer(root);
	        current = 1;
	        next = 0;
	        while(!queue.isEmpty()){
	            TreeNode currentNode = queue.poll();
	            if (currentNode!=null) {
	            	System.out.print(currentNode.val+" ");
	                current--;
	               
				}
	            else{
	            	System.out.print("null ");
	            	 current--;
	            	 queue.offer(null);
	                 next++;
	                 queue.offer(null);
	                 next++;                
	                 if(current ==0){
	                     System.out.println();
	                     current = next;
	                     next = 0;
	                     int temp=0;
	                     for (TreeNode treeNode : queue) {
							if(treeNode==null){
								temp++;
							}
						}
	                     if(temp==current){
	                    	 System.out.println("end");
	                         break;
	                     }
	                     
	                 }
	                continue;
	            }
	            
	            if(currentNode.left != null){
	                queue.offer(currentNode.left);
	                next++;
	            }
	            else{
	            	queue.offer(null);
	                next++;
	            }
	            if(currentNode.right != null){
	                queue.offer(currentNode.right);
	                next++;
	            }
	            else{
	            	queue.offer(null);
	                next++;
	            }
	            if(current ==0){
	                System.out.println();
	                current = next;
	                next = 0;
	                int temp=0;
	                for (TreeNode treeNode : queue) {
						if(treeNode==null){
							temp++;
						}
					}
	                if(temp==current){
	               	 System.out.println("end");
	                    break;
	                }
	                
	            }
	            
	        }
	    }
}

二叉搜尋樹類

一個tree中有一個節點root,其他全是方法

public class BinarySearchTree {

	public TreeNode root;
	

實現-增刪查

插入節點

BST樹插入新元素時,從根節點開始,遇到鍵值較大的值就向左,遇到較小的就向右,一直到尾端,就是插入點。

插入節點,如果已存在相同的樹,則不插入,返回已有的節點,否則返回插入的節點
將now=root 將now的值與x比較,相同返回now,x大,則如果now無右節點,建立右節點x並返回,否則now=now.right
x小則反之亦然

//插入節點,如果已存在相同的樹,則不插入,返回已有的節點,否則返回插入的節點
	//將now=root 將now的值與x比較,相同返回now,x大,則如果now無右節點,建立右節點x並返回,否則now=now.right
	//x小則反之亦然
	public TreeNode insertNode(int x){		
		if(root==null){
			root=new TreeNode(x);
			return root;
		}
		TreeNode now=root;
		while(true){
			if(now.val==x){
				return now;
			}
			if(now.val>x){
				if(now.left==null){
					now.left=new TreeNode(x);
					return now.left;
				}
				else{
					now=now.left;
					continue;
				}
			}
			else{
				if(now.right==null){
					now.right=new TreeNode(x);
					return now.right;
				}
				else{
					now=now.right;
					continue;
				}
			}
		}
		
	}

建立二叉搜尋樹

只有一個int,則root為x的節點

x為陣列,如果長度為0,初始化root為0的節點
否則逐個插入x的元素

//只有一個int,則root為x的節點
	public BinarySearchTree(int x){
		root=new TreeNode(x);
	}	
	
	//x為陣列,如果長度為0,初始化root為0的節點
	//否則逐個插入x的元素
	public BinarySearchTree(int[] x){		
		int length=x.length;
		if(length==0){
			root=new TreeNode(0);
			return;
		}
		for(int i=0;i<length;i++){
			insertNode(x[i]);
		}		
	}

搜尋節點

根據now與x的大小,進入now的左右子節點,直到找到now.val==x或者now==null

	
	//根據int x查詢二叉搜尋樹的節點,如果沒找到 返回null,找到了,返回該節點
	//now=root 進入true的迴圈 如果now=null,說明沒有返回null,now的值=x,說明找到,返回now
	//否則根據now與x的大小,now=now的左右子節點
	public TreeNode findNode(int x){
		if(root==null){
			return null;
		}
		TreeNode now=root;
		while(true){
			if(now==null){
				return null;
			}
			if(now.val==x){
				return now;
			}
			if(now.val<x){
				now=now.right;
			}
			else {
				now=now.left;
			}
		}				
	}

刪除節點

刪除節點首先要找到節點,還要找到他的父親節點,並記錄下是父親節點的左還是右節點

		TreeNode now=root;
		TreeNode parent=root;
		boolean isLeft=true;
		while(true){
			if(now==null){
				return;
			}
			if(now.val==x){
				break;
			}
			if(now.val<x){
				parent=now;
				now=now.right;
				isLeft=false;
			}
			else {
				parent=now;
				now=now.left;
				isLeft=true;
			}
		}	

 

刪除節點分兩種大情況,每種各有三種情況

如果刪除的是普通節點

如果該節點沒有左右節點

直接parent的左右節點為null即可

		if(now.left==null&&now.right==null){
			if(isLeft){
				parent.left=null;
				return;
			}
			else{
				parent.right=null;
				return;
			}
		}

如果只有左或者右節點

如果A只有一個子節點,就直接將A的子節點連至A的父節點,並將A刪除;

		//如果now沒有左右子節點,則parent的子節點為null,根據isLeft判斷now是parent的左右節點
		if(now.left==null&&now.right==null){
			if(isLeft){
				parent.left=null;
				return;
			}
			else{
				parent.right=null;
				return;
			}
		}
		//如果now只有左節點,則根據isleft parent的左右節點為now.left
		if(now.left!=null&&now.right==null){
			if(isLeft){
				parent.left=now.left;
				return;
			}
			else{
				parent.right=now.left;
				return;
			}
		}

節點左右節點都有

就以右子樹的最小節點(右子樹的最左節點,我選的是這個)或左子樹的最大節點取代A即可。

有兩種方法,一種是修改值,一種是節點left,right改變(我選的是這個),前一個應該簡單一點

		//如果now左右節點都有,則根據isleft parent的左右節點為now的右節點的最左邊的那個mostLeft
		//相當於把mostLeft替換掉now
		if(now.left!=null&&now.right!=null){
			TreeNode mostLeft=now.right;
			TreeNode mostLeftParent=now;
			while(mostLeft.left!=null){
				mostLeftParent=mostLeft;
				mostLeft=mostLeft.left;		
			}
			if(mostLeft==now.right){
				if(isLeft){
					parent.left=now.right;
				}
				else{
					parent.right=now.right;
				}
				now.right.left=now.left;				
			}
			else{
				mostLeftParent.left=mostLeft.right;
				if(isLeft){
					parent.left=mostLeft;
				}
				else{
					parent.right=mostLeft;
				}
				mostLeft.right=now.right;
				mostLeft.left=now.left;
			}
			
		}				
	}

如果是root,說明它沒有parent,需要特殊處理,但是邏輯與上面的相同,只是沒有parent的操作

		//如果now是root,說明它沒有parent,需要特殊處理,但是邏輯與下面的相同,只是沒有parent的操作
		if(now==root){
			if(now.left==null&&now.right==null){
				root=null;
				return;
			}
			if(now.left!=null&&now.right==null){
				root=now.left;
				return;
			}
			if(now.left==null&&now.right!=null){
				root=now.right;
				return;
			}
			if(now.left!=null&&now.right!=null){
				TreeNode mostLeft=now.right;
				TreeNode mostLeftParent=now;
				while(mostLeft.left!=null){
					mostLeftParent=mostLeft;
					mostLeft=mostLeft.left;		
				}
				if(mostLeft==now.right){					
					now.right.left=root.left;	
					root=now.right;	
					return;
				}
				else{					
					mostLeftParent.left=mostLeft.right;
					root=mostLeft;
					mostLeft.right=now.right;
					mostLeft.left=now.left;
					return;
				}
				
			}
			
		}	

應用

搜尋

只要o(logn)時間即可

排序

中序遍歷即可,o(n)時間

 

完整程式碼

package datastructure.tree.binarysearchtree;

public class Main {

	public static void main(String[] args) {

		int[] x=new int[]{1,3,2,-4,0,4};	
		BinarySearchTree tree=new BinarySearchTree(x);
		//tree.insertNode(3);
		BinarySearchTree.printTree(tree.root);
		//BinarySearchTree.inOrder(tree.root);
		//BinarySearchTree.breadthFirstSearch(tree.root);
		//BinarySearchTree.depthFirstSearch(tree.root);
		//TreeNode now=tree.findNode(2);
		//now.printTree(now);
		tree.deleteNode(1);
		BinarySearchTree.printTree(tree.root);
	}

}
package datastructure.tree.binarysearchtree;

import java.util.LinkedList;
import java.util.Queue;

public class TreeNode {

	int val;
	public TreeNode left;
	public TreeNode right;
	public TreeNode(int x) {
		val = x; 
		left=null;
		right=null;
	}
	
	//通過佇列 當前下層節點個數 列印每層的節點和null,為了方便列印
		public static void printTree(TreeNode root){
	        if(root == null)
	            return;
	        Queue<TreeNode> queue = new LinkedList<TreeNode>();
	        
	        int current;//當前層 還未列印的結點個數
	        int next;//下一層結點個數
	        
	        queue.offer(root);
	        current = 1;
	        next = 0;
	        while(!queue.isEmpty()){
	            TreeNode currentNode = queue.poll();
	            if (currentNode!=null) {
	            	System.out.print(currentNode.val+" ");
	                current--;
	               
				}
	            else{
	            	System.out.print("null ");
	            	 current--;
	            	 queue.offer(null);
	                 next++;
	                 queue.offer(null);
	                 next++;                
	                 if(current ==0){
	                     System.out.println();
	                     current = next;
	                     next = 0;
	                     int temp=0;
	                     for (TreeNode treeNode : queue) {
							if(treeNode==null){
								temp++;
							}
						}
	                     if(temp==current){
	                    	 System.out.println("end");
	                         break;
	                     }
	                     
	                 }
	                continue;
	            }
	            
	            if(currentNode.left != null){
	                queue.offer(currentNode.left);
	                next++;
	            }
	            else{
	            	queue.offer(null);
	                next++;
	            }
	            if(currentNode.right != null){
	                queue.offer(currentNode.right);
	                next++;
	            }
	            else{
	            	queue.offer(null);
	                next++;
	            }
	            if(current ==0){
	                System.out.println();
	                current = next;
	                next = 0;
	                int temp=0;
	                for (TreeNode treeNode : queue) {
						if(treeNode==null){
							temp++;
						}
					}
	                if(temp==current){
	               	 System.out.println("end");
	                    break;
	                }
	                
	            }
	            
	        }
	    }
}
package datastructure.tree.binarysearchtree;

import java.nio.channels.NetworkChannel;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;


public class BinarySearchTree {

	public TreeNode root;
	
	//只有一個int,則root為x的節點
	public BinarySearchTree(int x){
		root=new TreeNode(x);
	}	
	
	//x為陣列,如果長度為0,初始化root為0的節點
	//否則逐個插入x的元素
	public BinarySearchTree(int[] x){		
		int length=x.length;
		if(length==0){
			root=new TreeNode(0);
			return;
		}
		for(int i=0;i<length;i++){
			insertNode(x[i]);
		}		
	}
	
	//插入節點,如果已存在相同的樹,則不插入,返回已有的節點,否則返回插入的節點
	//將now=root 將now的值與x比較,相同返回now,x大,則如果now無右節點,建立右節點x並返回,否則now=now.right
	//x小則反之亦然
	public TreeNode insertNode(int x){		
		if(root==null){
			root=new TreeNode(x);
			return root;
		}
		TreeNode now=root;
		while(true){
			if(now.val==x){
				return now;
			}
			if(now.val>x){
				if(now.left==null){
					now.left=new TreeNode(x);
					return now.left;
				}
				else{
					now=now.left;
					continue;
				}
			}
			else{
				if(now.right==null){
					now.right=new TreeNode(x);
					return now.right;
				}
				else{
					now=now.right;
					continue;
				}
			}
		}
		
	}
	
	//根據int x查詢二叉搜尋樹的節點,如果沒找到 返回null,找到了,返回該節點
	//now=root 進入true的迴圈 如果now=null,說明沒有返回null,now的值=x,說明找到,返回now
	//否則根據now與x的大小,now=now的左右子節點
	public TreeNode findNode(int x){
		if(root==null){
			return null;
		}
		TreeNode now=root;
		while(true){
			if(now==null){
				return null;
			}
			if(now.val==x){
				return now;
			}
			if(now.val<x){
				now=now.right;
			}
			else {
				now=now.left;
			}
		}				
	}
	
	
	//如果樹中沒有值為x,則啥都不幹,否則刪除這個節點
	//先找到這個x,與查詢的方法不同,還要找到它的父節點
	//如果now是root,說明它沒有parent,需要特殊處理,但是邏輯與下面的相同,只是沒有parent的操作
	//如果now沒有左右子節點,則parent的子節點為null,根據isLeft判斷now是parent的左右節點
	//如果now只有左節點,則根據isleft parent的左右節點為now.left
	//如果now只有右節點,則根據isleft parent的左右節點為now.right
	public void deleteNode(int x){
		if(root==null){
			return;
		}
		TreeNode now=root;
		TreeNode parent=root;
		boolean isLeft=true;
		while(true){
			if(now==null){
				return;
			}
			if(now.val==x){
				break;
			}
			if(now.val<x){
				parent=now;
				now=now.right;
				isLeft=false;
			}
			else {
				parent=now;
				now=now.left;
				isLeft=true;
			}
		}	
		//如果now是root,說明它沒有parent,需要特殊處理,但是邏輯與下面的相同,只是沒有parent的操作
		if(now==root){
			if(now.left==null&&now.right==null){
				root=null;
				return;
			}
			if(now.left!=null&&now.right==null){
				root=now.left;
				return;
			}
			if(now.left==null&&now.right!=null){
				root=now.right;
				return;
			}
			if(now.left!=null&&now.right!=null){
				TreeNode mostLeft=now.right;
				TreeNode mostLeftParent=now;
				while(mostLeft.left!=null){
					mostLeftParent=mostLeft;
					mostLeft=mostLeft.left;		
				}
				if(mostLeft==now.right){					
					now.right.left=root.left;	
					root=now.right;	
					return;
				}
				else{					
					mostLeftParent.left=mostLeft.right;
					root=mostLeft;
					mostLeft.right=now.right;
					mostLeft.left=now.left;
					return;
				}
				
			}
			
		}				
		//如果now沒有左右子節點,則parent的子節點為null,根據isLeft判斷now是parent的左右節點
		if(now.left==null&&now.right==null){
			if(isLeft){
				parent.left=null;
				return;
			}
			else{
				parent.right=null;
				return;
			}
		}
		//如果now只有左節點,則根據isleft parent的左右節點為now.left
		if(now.left!=null&&now.right==null){
			if(isLeft){
				parent.left=now.left;
				return;
			}
			else{
				parent.right=now.left;
				return;
			}
		}
		//如果now只有右節點,則根據isleft parent的左右節點為now.right
		if(now.left==null&&now.right!=null){
			if(isLeft){
				parent.left=now.right;
				return;
			}
			else{
				parent.right=now.right;
				return;
			}
		}
		//如果now左右節點都有,則根據isleft parent的左右節點為now的右節點的最左邊的那個mostLeft
		//相當於把mostLeft替換掉now
		if(now.left!=null&&now.right!=null){
			TreeNode mostLeft=now.right;
			TreeNode mostLeftParent=now;
			while(mostLeft.left!=null){
				mostLeftParent=mostLeft;
				mostLeft=mostLeft.left;		
			}
			if(mostLeft==now.right){
				if(isLeft){
					parent.left=now.right;
				}
				else{
					parent.right=now.right;
				}
				now.right.left=now.left;				
			}
			else{
				mostLeftParent.left=mostLeft.right;
				if(isLeft){
					parent.left=mostLeft;
				}
				else{
					parent.right=mostLeft;
				}
				mostLeft.right=now.right;
				mostLeft.left=now.left;
			}
			
		}				
	}
	
	
	//遞迴
	public static void preOrder(TreeNode root){
		if(root==null){
			return;
		}
		System.out.print(root.val+" ");
		preOrder(root.left);
		preOrder(root.right);
	}
	
	public static void inOrder(TreeNode root){
		if(root==null){
			return;
		}
		inOrder(root.left);
		System.out.print(root.val+" ");
		inOrder(root.right);
	}
	
	public static void postOrder(TreeNode root){
		if(root==null){
			return;
		}
		postOrder(root.left);
		postOrder(root.right);
		System.out.print(root.val+" ");
	}
	//通過佇列 當前下層節點個數 列印每層的節點和null
	public static void printTree(TreeNode root){
        if(root == null)
            return;
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        
        int current;//當前層 還未列印的結點個數
        int next;//下一層結點個數
        
        queue.offer(root);
        current = 1;
        next = 0;
        while(!queue.isEmpty()){
            TreeNode currentNode = queue.poll();
            if (currentNode!=null) {
            	System.out.print(currentNode.val+" ");
                current--;
               
			}
            else{
            	System.out.print("null ");
            	 current--;
            	 queue.offer(null);
                 next++;
                 queue.offer(null);
                 next++;                
                 if(current ==0){
                     System.out.println();
                     current = next;
                     next = 0;
                     int temp=0;
                     for (TreeNode treeNode : queue) {
						if(treeNode==null){
							temp++;
						}
					}
                     if(temp==current){
                    	 System.out.println("end");
                         break;
                     }
                     
                 }
                continue;
            }
            
            if(currentNode.left != null){
                queue.offer(currentNode.left);
                next++;
            }
            else{
            	queue.offer(null);
                next++;
            }
            if(currentNode.right != null){
                queue.offer(currentNode.right);
                next++;
            }
            else{
            	queue.offer(null);
                next++;
            }
            if(current ==0){
                System.out.println();
                current = next;
                next = 0;
                int temp=0;
                for (TreeNode treeNode : queue) {
					if(treeNode==null){
						temp++;
					}
				}
                if(temp==current){
               	 System.out.println("end");
                    break;
                }
                
            }
            
        }
    }
	
	//寬度優先遍歷 是簡化的按層遍歷,沒有了current和next和列印null
	public static void breadthFirstSearch(TreeNode root){
		Queue<TreeNode> queue = new LinkedList<TreeNode>();
		if(root==null){
			return;
		}
		queue.offer(root);
		while(!queue.isEmpty()){
			TreeNode now=queue.poll();
			System.out.print(now.val+" ");
			if(now.left!=null){
				queue.offer(now.left);
			}
			if(now.right!=null){
				queue.offer(now.right);
			}
		}
		System.out.println();
	}

	//深度優先遍歷
	//用棧 彈出自身 先加入右節點 再加入左節點,這樣先彈出左節點,左節點的左右子節點又塞進去,在原右節點上面
	public static void depthFirstSearch(TreeNode root){
		Stack<TreeNode> stack=new Stack<TreeNode>();
		if(root==null){
			return;
		}
		stack.push(root);
		while(!stack.isEmpty()){
			TreeNode now=stack.pop();
			System.out.print(now.val+" ");
			if(now.right!=null){
				stack.push(now.right);
			}
			if(now.left!=null){
				stack.push(now.left);
			}
			
		}
		System.out.println();
	}
	
	
	
	
}