1. 程式人生 > >資料結構——————————————寫avl樹

資料結構——————————————寫avl樹

這個樹折磨了我兩天,中間都不想寫了,但是想想開始了,不寫完好像缺點什麼,所以就覺得還是寫完吧

寫一個二叉樹難點旋轉

麻煩點:獲取其不平衡節點的父節點

以及不平衡節點


接下來寫這個平衡二叉樹

avl就是一顆平衡二叉樹

什麼是平衡二叉樹呢?

繼續先百度一波定義:

在AVL樹中任何節點的兩個子樹的高度最大差為1。

這便是一個平衡二叉樹,它每個節點的左右子樹最大差在2以內

 

下來這個為不平衡的二叉樹,紅色節點的右子樹深2,右子樹為0,差值超過了1,所以不為平衡二叉樹

為什麼有平衡二叉樹這個概念?

如果一個二叉查詢樹不改變,那麼它會可能會變成一個連結串列,查詢的時候時間複雜度為N,在使用二叉樹時可能廢掉大量時間,如果是一個平衡二叉樹,那麼查詢起來它時間複雜度知只是大於logN,可以節省大量的時間。

造成不平衡的插入情況

當一個二叉查詢樹中插入一個數據時期使其成為不平衡的二叉樹一共有4中可能

1.對a的左兒子左子樹進行一次插入

插入1

10的左子樹深度為3,右子樹深度為1,差2

2.對a的左兒子的右子樹進行一次插入

插入8

10的左子樹深度為3,右子樹深度為1,差2

3.對a的右兒子的左子樹的進行一次插入

插入12

10的左子樹深度為1,右子樹深度為3,差2

4.對a的右兒子的右子樹一次插入

插入25

15的左子樹深度為0,右子樹深度為2,差2

藍色為插入的資料

a為不平衡的節點

1和4的情況相同,關於a是對稱,所以只需要弄懂一種情況就可以了

2.3情況相同,也是關於a對稱,只需要弄懂其中一個就可以了

不平衡的情況進行調節

1.單旋轉

單旋轉是來解決1和4情況的。

圖:

我們主要說一下情況一

如圖此時k1不平衡點,此時k2太深,那麼我們應該如何做呢?

因為左邊的樹深右邊淺,所以我們可以把右邊加深,左變淺

這個時候我們可以把k2往上移一層,代替k1位置,

k1>k2,所以將k1變為k2右節點,

Y大於k2小於k1所以Y變為k1的左節點 。

 

變平衡後

1.4原理相同所以我也就不在寫另外一個了

2.雙旋轉

雙旋轉是用來解決2和3問題的

圖:

遇到2和3情況使用單旋轉已經做不到了。

這個時候我們需要將k3往上移,我們把k3可以看成新的根

當我們把k3移到k1的位置,此刻我們應該如何變化呢?

k1<k3所以k3的左孩子為k1

k2>k3所以k3的左孩子為k2

B大於k1小於k3,所以B為k1的右子樹

C大於3小於k2,所以C為k2的左子樹

(B和C中有一個是null,不過這並不影響)

雙旋轉後:

2和3的思路是相同的,所以就不另外介紹了

程式碼

這裡沒有寫刪除節點如何調節平衡

刪除和插入沒有多少區別,先刪除某一個節點,然後在判斷這個樹是否平衡

樹節點程式碼:

package Tree;


public class TreeNode<T> {
	T t;//資料
	TreeNode<T> left;//左孩子
	TreeNode<T> right;//右孩子
    public TreeNode(T t){
		this.t=t;
	}
}

 調節平衡程式碼:

package Tree;

import java.util.Stack;

public class AVLTree {
	private TreeNode<Integer> treeRoot;
	// 插入在節點的左孩子的左子樹
	public void LL(TreeNode<Integer> treeFather, TreeNode<Integer> node) {
		TreeNode<Integer> nodeLeftChild = node.left;
		TreeNode<Integer> nodeLeftChildRight = nodeLeftChild.right;

		nodeLeftChild.right = node;
		node.left = nodeLeftChildRight;
		if (treeFather == treeRoot) {
			treeRoot = nodeLeftChild;
		} else if (treeFather.left == node) {
			treeFather.left = nodeLeftChild;
		} else {
			treeFather.right = nodeLeftChild;
		}
	}

	// 插入右孩子右節點
	public void RR(TreeNode<Integer> treeFather, TreeNode<Integer> node) {
		TreeNode<Integer> nodeRightChild = node.right;
		TreeNode<Integer> nodeRightChildLeft = nodeRightChild.left;

		nodeRightChild.left = node;
		node.right = nodeRightChildLeft;

		if (treeFather == treeRoot) {
			treeRoot = nodeRightChild;
		} else if (treeFather.left == node) {
			treeFather.left = nodeRightChild;
		} else {
			treeFather.right = nodeRightChild;
		}
	}

	// 新增到左孩子的右子樹
	public void LR(TreeNode<Integer> treeFather, TreeNode<Integer> node) {
		TreeNode<Integer> nodeLeft = node.left;
		TreeNode<Integer> nodeLeftChildRight = nodeLeft.right;
		TreeNode<Integer> nodeLeftChildRightChildLeft = nodeLeftChildRight.left;
		TreeNode<Integer> nodeLeftChileRightChildRight = nodeLeftChildRight.right;

		nodeLeftChildRight.left = nodeLeft;
		nodeLeftChildRight.right = node;
		nodeLeft.right = nodeLeftChildRightChildLeft;
		node.left = nodeLeftChileRightChildRight;

		if (treeFather == treeRoot) {
			treeRoot = nodeLeftChildRight;
		} else if (treeFather.left == node) {
			treeFather.left = nodeLeftChildRight;
		} else {
			treeFather.right = nodeLeftChildRight;
		}
	}

	// 新增到右孩子左子樹
	public void RL(TreeNode<Integer> treeFather, TreeNode<Integer> node) {
		TreeNode<Integer> nodeRight = node.right;
		TreeNode<Integer> nodeRightChildLeft = nodeRight.left;
		TreeNode<Integer> nodeRightChildLeftRight = nodeRightChildLeft.right;
		TreeNode<Integer> nodeRightChildLeftLeft = nodeRightChildLeft.left;

		nodeRightChildLeft.right = nodeRight;
		nodeRightChildLeft.left = node;
		node.right = nodeRightChildLeftLeft;
		nodeRight.left = nodeRightChildLeftRight;

		if (treeFather == treeRoot) {
			treeRoot = nodeRightChildLeft;
		} else if (treeFather.left == node) {
			treeFather.left = nodeRightChildLeft;
		} else {
			treeFather.right = nodeRightChildLeftRight;
		}
	}

	// 判斷節點樹的深度
	private int length(TreeNode<Integer> node) {
		if (node == null) {
			return 0;
		}
		int left = length(node.left);
		int right = length(node.right);

		return 1 + ((left > right) ? left : right);
	}

	// 判斷是否平衡
	private int balance(TreeNode<Integer> node) {
		int left = length(node.left);
		int right = length(node.right);
		return left - right;
	}

	// 查詢節點的父節點
	//修改
	private TreeNode<Integer> findNodeFather(TreeNode<Integer> nodefather, TreeNode<Integer> node) {
		Stack<TreeNode<Integer>> stack1 = new Stack<>();
		    Stack<Integer> stack2 = new Stack<>();
		    int i = 1;
		    while(nodefather != null || !stack1.empty())
		    {
		        while (nodefather != null)
		        {
		            stack1.push(nodefather);
		            stack2.push(0);
		            nodefather = nodefather.left;
		        }
		        while(!stack1.empty() && stack2.peek() == i)
		        {
		            stack2.pop();
		            TreeNode<Integer> node1=stack1.pop();
		            if(node1.left==node||node1.right==node) {
		            	return node1;
		            }
		        }

		        if(!stack1.empty())
		        {
		            stack2.pop();
		            stack2.push(1);
		            nodefather = stack1.peek();
		            nodefather = nodefather.right;
		        }
		    }
		    return null;
	}

	// 使用哪一個旋轉
	private void madeBalance(TreeNode<Integer> node) {
		TreeNode<Integer> nodeFather =treeRoot;
		int n = balance(node);//判斷是哪個孩子過長導致不平衡
		int m;//判斷是左(右)孩子哪個子樹過長
		if (n>-2&&n<2) {
			return;
		} else if (n > 1) {
			
			if (node != treeRoot) {
				nodeFather=findNodeFather(nodeFather, node);
			}
			m = balance(node.left);
			
			if (m > 0) {
				LL(nodeFather, node);
			} else {
				LR(nodeFather, node);
			}
		} else {
		
			if (node != treeRoot) {
				nodeFather=findNodeFather(nodeFather, node);
			}
			m = balance(node.right);
			if (m > 0) {
				RL(nodeFather, node);
			} else {
				RR(nodeFather, node);
			}

		}
	}
	//找出不平衡的節點
	private void findNOBlance(TreeNode<Integer> node) {
		if(node==null) {
			return;
		}
		findNOBlance(node.left);
		findNOBlance(node.right);
		int m=balance(node);
		if((m>1||m<-1)) {
			madeBalance(node);
			return;
		}
	}
	//插入資料
	public void insert(Integer t) {
		//第一次插入資料時如果treeroot沒有定義首先定義
		if(treeRoot==null) {
			treeRoot=new TreeNode<Integer>(t);
			return;
		}
		//in插入借點
		//tree指標
		TreeNode<Integer> in = new TreeNode<>(t);
		TreeNode<Integer> tree = treeRoot;
		insert(tree, in);
		//插入資料後就判斷是否平衡
		TreeNode<Integer> node=treeRoot;
		findNOBlance(node);
	}
	private void insert(TreeNode<Integer> tree, TreeNode<Integer> in) {
		//如果插入資料與樹中資料相同則直接返回,二叉查詢樹中不能有重複的資料
		if (in.t == tree.t) {
			return;
		}
		//如果大於此節點數,則在右子樹中查詢
		if (tree.t > in.t) {
			if (tree.left == null) {
				tree.left = in;
			} else {
				insert(tree.left, in);
			}
			//否則在左子樹中查詢
		} else {
			if (tree.right == null) {
				tree.right = in;
			} else {
				insert(tree.right, in);
			}
		}
	}

		// 判斷此樹是否為空
		public boolean isEmpty() {
			return treeRoot == null;
		}

		// 將此樹置為空
		public void makeEmpty() {
			treeRoot = null;
		}
		// 遍歷輸入
		public String toString() {

			Tree<Integer> t = new Tree<Integer>();
			t.layer(treeRoot);
			
			return null;
		}
}

測試程式碼:

package Tree;

public class Test {
	public static void main(String[] args) {
	AVLTree tree=new AVLTree();
	tree.insert(10);
	tree.insert(5);
	tree.insert(15);
	tree.insert(3);
	tree.insert(6);
	tree.insert(7);
	tree.toString();

	}
}

考科二又掛了希望下一次能過