數據結構 No.11 刪除二叉樹

引言

       刪除節點是二叉樹操作中最複雜的。在刪除之前首先要查找要刪除的點。找到節點後,這個要刪除的節點可能會有三種情況需要考慮。

1、這個節點是葉子節點,沒有子節點。
	這個時候要刪除葉子節點,只需要改變改節點的父節點的引用值,將指向該節點的引用設置爲null。就可以了
2、該節點有一個子節點。
	改變父節點的引用,將其直接指向要刪除節點的子節點
3、該節點有兩個子節點
	要刪除有兩個子節點的節點。就需要使用的它的中序後繼來替代該節點

正文

       下面提到的關於插入、查找、中序遍歷方法這裏就不過多的闡述了,需要了解的,請參考我的上幾篇文章。這裏主要講的就是二叉樹的刪除。

        刪除節點:

       有點類似於查找的方法。current = node,也就是從根節點開始遍歷,current變量用於記錄當前正在使用的節點,parent爲當前節點的父節點,主要用於刪除節點後維護被刪除節點的父子關係。isLeftChild用於記錄當前節點是根節點的左子節點還是右子節點。

        下面的while方法的作用就是查找到要刪除的節點。循環的條件就是當前節點的數據域是否等於要刪除的值,首先把當前節點的保存到parent父節點。然後比較,因爲是二叉樹,所有比這個節點小的數,在左子樹,比這個節點大的數,在右子樹。所有如果目標值大於當前值,就將右子節點也就是比當前值大的值賦值給當前值。反之,將左子節點賦值給當前值。然後將對應的isLeftChild賦值。找不到返回false。

         找到這個刪除的節點之後,接下來就是刪除了。刪除分爲三種情況,

  1. 刪除葉子節點:也就是該節點沒有子節點。刪除葉子節點又分爲三種情況:
    是否爲根節點如果是根節點只需要將根節點置爲null。
    是否爲左子節點如果是左子節點,就將父節點的左孩子置爲null,脫離整個樹結構即可
    是否爲右子節點如果是右子節點,就將父節點的右孩子置爲null,脫離樹結構。注意,這裏爲刪除葉子節點
  2. 刪除只有一個葉子節點的情況,這裏分爲只有左子節點和只有右子節點兩種情況,每種情況又分爲三小種情況。這裏只說明,單葉子節點爲左子節點,也就是右子節點爲null的情況,右子節點的情況和這種相同,只需要將“左”字換成“右”字。
    是否爲根節點如果是根節點,就將此節點的左孩子節點賦值給根節點
    是否爲左子節點如果是左子節點,就將此節點的左子節點賦值給當前節點的左子節點
    是否爲右子節點如果是右子節點,就將此節點的右子節點賦值給當前節點的右子節點
  3. 刪除兩個子節點的情況。這裏情況比較特殊,刪除這個節點之後需要使用這個節點的中序後繼節點來替代它,具體中序後繼的方法就是找它的右子節點的左孩子的左孩子的左孩子。。。也就是找此二叉樹中比當前節點大的最少的那個節點。這裏也分爲三種情況
    是否爲根節點如果是根節點,就將此節點的中序後繼節點賦值給此節點
    是否爲左子節點如果是左子節點,就將此節點的中序後繼節點賦值給此節點的左子節點
    是否爲右子節點如果是右子節點,就將此節點的中序後繼節點賦值給此節點的右子節點
/*
 * 二叉樹類
 */
public class Tree {
	// 根節點
	public Node root;

	/**
	 * 插入節點
	 * 
	 * @param value
	 */
	public void insert(long value, String sValue) {
		// 封裝節點
		Node newNode = new Node(value, sValue);
		// 引用當前節點
		Node current = root;
		// 引用父節點
		Node parent;
		// 如果root爲null,也就是第一插入的時候
		if (root == null) {
			root = newNode;
			return;
		} else {
			while (true) {
				// 父節點指向當前節點
				parent = current;
				// 如果當前指向的節點數據比插入的要大,則向左走
				if (current.data > value) {
					current = current.leftChild;
					if (current == null) {
						parent.leftChild = newNode;
						return;
					}
				} else {
					current = current.rightChild;
					if (current == null) {
						parent.rightChild = newNode;
						return;
					}
				}
			}
		}
	}

	/**
	 * 查找節點
	 * 
	 * @param value
	 */
	public Node find(long value) {
		// 引用當前節點,從根節點開始
		Node current = root;
		// 循環,只要查找值不等於當前節點的數據項
		while (current.data != value) {
			// 進行比較,比較查找值和當前節點的大小
			if (current.data > value) {
				current = current.leftChild;
			} else {
				current = current.rightChild;
			}
			// 如果查找不到
			if (current == null) {
				return null;
			}
		}
		return current;
	}

	/**
	 * 刪除節點
	 * 
	 * @param value
	 */
	public boolean delete(long value) {
		// 引用當前節點,從根節點開始
		Node current = root;

		// 應用當前節點的父節點
		Node parent = root;
		// 是否爲左節點
		boolean isLeftChild = true;

		while (current.data != value) {
			parent = current;
			// 進行比較,比較查找值和當前節點的大小
			if (current.data > value) {
				current = current.leftChild;
				isLeftChild = true;
			} else {
				current = current.rightChild;
				isLeftChild = false;
			}
			// 如果查找不到
			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;
			}
		} else if (current.rightChild == null) {
			// 刪除只有一個葉子節點的情況
			if (current == root) {
				root = current.leftChild;
			} else if (isLeftChild) {
				parent.leftChild = current.leftChild;
			} else {
				parent.rightChild = current.leftChild;
			}
		} else if (current.leftChild == null) {
			if (current == root) {
				root = current.rightChild;
			} else if (isLeftChild) {
				parent.leftChild = current.rightChild;
			} else {
				parent.rightChild = current.rightChild;
			}
		} 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 true;
	}

	/**
	 * 中序遍歷
	 */
	public void inOrder(Node localNode) {
		if (localNode != null) {
			// 中序遍歷左子樹
			inOrder(localNode.leftChild);
			// 訪問根節點
			System.out.println(localNode.data + ", " + localNode.sData);
			// 中旬遍歷右子樹
			inOrder(localNode.rightChild);
		}
	}

	/**
	 * 獲得中序後繼節點
	 * 
	 * @return
	 */
	private Node getSuccessor(Node delNode) {
		Node successor = delNode;
		Node successorParent = 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;
	}
}

測試類

public class TestTree {
	public static void main(String[] args) {
		Tree tree = new Tree();
		tree.insert(10,"James");
		tree.insert(20,"YAO");
		tree.insert(15,"Kobi");
		tree.insert(3,"Mac");
		tree.insert(4, "Zhangsan");
		tree.insert(90, "Lisi");
		
		tree.delete(3);
		tree.inOrder(tree.root);
	}
}

判斷是否爲根節點

發佈了40 篇原創文章 · 獲贊 55 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章