二叉樹基礎:
剛看到堆排序,順便記錄一下關於樹的一些基本概念:
前言
前面介紹的棧、隊列都是線性結構(linear structure)。而樹是非線性結構(non-linear structure)。因此,樹中的元素之間一般不存在類似於線性結構的一對一的關係,更多地表現爲多對多的關係。直觀地看,它是數據元素(在樹中稱爲節點)按分支關係組織起來的結構。顯然,樹形結構是比線性結構更復雜的一種數據結構類型。
一、樹
樹的定義:樹是含有n個節點的有窮集合,其中有一個節點比較特殊稱爲根節點。在圖示樹時,用一條邊連接兩個有邏輯關係的節點,這個關係被稱爲父子關係。
二、二叉樹
二叉樹(Binary Tree)由節點的有限集合構成。這個集合或者爲空集,或者爲由一個根節點(root)和兩棵互不相交,分別稱爲這個根的左子樹(left subtree)和右子樹(right subtree)的二叉樹組成的集合。
一棵二叉樹的示意圖:
三、樹和二叉樹的主要區別
- 樹中節點的最大度數沒有限制,而二叉樹節點的度不超過2。
- 樹中節點的孩子節點,無左右之分,而二叉樹中是有區分的,即孩子是有區別的:左孩子、右孩子,且次序不可顛倒。
- 樹的結點個數至少爲1,而二叉樹的結點個數可以爲0。
四、常見概念
- 節點的度:某節點的度定義爲該節點孩子節點的個數。
- 葉子節點:度爲0的節點。
- 樹的度:一棵樹中,最大的節點的度稱爲樹的度。
- 節點的高度:從該節點起到葉子節點的最長簡單路徑的邊數。(簡單路徑:無重複邊的路徑)
- 樹的高度:根節點的高度。
- 節點的層數:從根開始定義起,根爲第1層,根的子節點爲第2層,以此類推。
- 數的層數:根節點的層數。
- 節點的深度:即該節點的層數。
- 樹的深度:根節點的深度。
- 外節點:葉子節點。
- 內節點:除葉子節點之外的節點。
- 滿二叉樹:二叉樹中節點的度只能是0或2。
- 完全二叉樹:除最後一層,每一層的節點數都達到最大。最後一層若是沒滿,則節點集中在左邊,空的只能是右邊。
- 擴充二叉樹:對二叉樹中度爲1的節點和葉子節點添加空節點,使之成爲滿二叉樹。
五、幾個量的關係
- 對於二叉樹,根節點是第一層,則第i層至多有個結點。若共有k層,則最多有節點個。
- 按層次順序對一棵有n個節點的完全二叉樹的所有節點從0到n-1編號。若父節點的編號是i,則左孩子的編號是2*i+1,右孩子的編號是2*i+2。(當然,這是在存在左右孩子的情況下)。同樣的,若孩子(無論左右孩子)節點是i,則父節點是(i-1)/2。
- 對於一棵滿二叉樹,外部節點或者說是葉子節點數是n,則內部節點數是n-1。
- 對於一棵二叉樹,用ni表示度爲i的節點個數,則n0=n2+1。證明如下:總節點數n=n0+n1+n2。用e表示邊數,則n=e+1,這是因爲除根節點外,每一個節點都和一條邊對應。同時,e=2n2+n1,推出n0+n1+n2=2n2+n1+1,化簡即得n0=n2+1。這個結論用語言表述:二叉樹中,葉子節點比度爲2的節點多一個。
- 有n個節點的完全二叉樹,樹高度,即logn向下取整,高度從0計數。
- 在二叉樹中,第i層的第一個節點(最左邊的節點)的編號是,層數從0計數。這個量在選擇排序:樹形選擇中用到了。
六、示意圖
二叉樹遍歷:
在計算機科學中,二叉樹是每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”(left subtree)和“右子樹”(right subtree)。
一顆簡單的二叉樹
二叉樹的遍歷分爲三種:前(先)序、中序、後序遍歷。
設L、D、R分別表示二叉樹的左子樹、根結點和遍歷右子樹,則先(根)序遍歷二叉樹的順序是DLR,中(根)序遍歷二叉樹的順序是LDR,後(根)序遍歷二叉樹的順序是LRD。還有按層遍歷二叉樹。這些方法的時間複雜度都是O(n),n爲結點個數。
假設我們有一個包含值的value和指向兩個子結點的left和right的樹結點結構。我們可以寫出這樣的過程:
先序遍歷(遞歸實現):visit(node)
print node.value
if node.left != null then visit(node.left)
if node.right != null then visit(node.right)
中序遍歷(遞歸實現):
visit(node)
if node.left != null then visit(node.left)
print node.value
if node.right != null then visit(node.right)
後序遍歷(遞歸實現):
visit(node)
if node.left != null then visit(node.left)
if node.right != null then visit(node.right)
print node.value
二叉樹創建和遍歷:
public class BinaryTree {
private Node root;
/**
*
* 內部節點類
* @author yhh
*/
private class Node{
private Node left;
private Node right;
private int data;
public Node(int data){
this.left = null;
this.right = null;
this.data = data;
}
}
public BinaryTree(){
root = null;
}
/**
* 遞歸創建二叉樹
* @param node
* @param data
*/
public void buildTree(Node node,int data){
if(root == null){
root = new Node(data);
}else{
if(data < node.data){
if(node.left == null){
node.left = new Node(data);
}else{
buildTree(node.left,data);
}
}else{
if(node.right == null){
node.right = new Node(data);
}else{
buildTree(node.right,data);
}
}
}
}
/**
* 前序遍歷
* @param node
*/
public void preOrder(Node node){
if(node != null){
System.out.println(node.data);
preOrder(node.left);
preOrder(node.right);
}
}
/**
* 中序遍歷
* @param node
*/
public void inOrder(Node node){
if(node != null){
inOrder(node.left);
System.out.println(node.data);
inOrder(node.right);
}
}
/**
* 後序遍歷
* @param node
*/
public void postOrder(Node node){
if(node != null){
postOrder(node.left);
postOrder(node.right);
System.out.println(node.data);
}
}
public static void main(String[] args) {
int[] a = {2,4,12,45,21,6,111};
BinaryTree bTree = new BinaryTree();
for (int i = 0; i < a.length; i++) {
bTree.buildTree(bTree.root, a[i]);
}
bTree.preOrder(bTree.root);
bTree.inOrder(bTree.root);
bTree.postOrder(bTree.root);
}
}
插入操作
二叉樹查找樹b插入操作x的過程如下:
1、若b是空樹,則直接將插入的結點作爲根結點插入。
2、x等於b的根結點的數據的值,則直接返回,否則。
3、若x小於b的根結點的數據的值,則將x要插入的結點的位置改變爲b的左子樹,否則。
4、將x要出入的結點的位置改變爲b的右子樹。
具體代碼:
public void insert(T t)
{
rootTree = insert(t, rootTree);
}
public BinaryNode insert(T t,BinaryNode node)
{
if(node==null)
{
//新構造一個二叉查找樹
return new BinaryNode(t, null, null);
}
int result = t.compareTo(node.data);
if(result<</SPAN>0)
node.left= insert(t,node.left);
else if(result>0)
node.right= insert(t,node.right);
else
;//doNothing
return node;
}
刪除操作:
不過在此之前,我們應該確保根據給定的值找到了要刪除的結點,如若沒找到該結點
不會執行刪除操作!
下面三種情況假設已經找到了要刪除的結點。
1、如果結點爲葉子結點(沒有左、右子樹),此時刪除該結點不會玻化樹的結構
直接刪除即可,並修改其父結點指向它的引用爲null.如下圖:
2、如果其結點只包含左子樹,或者右子樹的話,此時直接刪除該結點,並將其左子樹
或者右子樹設置爲其父結點的左子樹或者右子樹即可,此操作不會破壞樹結構。
3、 當結點的左右子樹都不空的時候,一般的刪除策略是用其右子樹的最小數據 (容易找到)代替要刪除的結點數據並遞歸刪除該結點(此時爲null),因爲 右子樹的最小結點不可能有左孩子,所以第二次刪除較爲容易。 z的左子樹和右子樹均不空。找到z的後繼y,因爲y一定沒有左子樹,所以可以刪除y,並讓y的父親節點成爲y的右子樹的父親節點,並用y的值代替z的值.如圖:
具體代碼:
public void remove(T t)
{
rootTree = remove(t,rootTree);
}
public BinaryNode remove(T t,BinaryNode node)
{
if(node == null)
return node;//沒有找到,doNothing
int result = t.compareTo(node.data);
if(result>0)
node.right = remove(t,node.right);
else if(result<</SPAN>0)
node.left = remove(t,node.left);
else if(node.left!=null&&node.right!=null)
{
node.data = findMin(node.right).data;
node.right = remove(node.data,node.right);
}
else
node = (node.left!=null)?node.left:node.right;
return node;
}
查找二叉樹節點:
在二叉查找樹中查找x的過程如下:
1、若二叉樹是空樹,則查找失敗。
2、若x等於根結點的數據,則查找成功,否則。
3、若x小於根結點的數據,則遞歸查找其左子樹,否則。
4、遞歸查找其右子樹。
具體代碼:
public TreeNode search(int Key) {
TreeNode node = root;
// 首先定義一個節點讓其指向根,在下面的循環中
// 只要節點值不等於要查找的節點值就進入循環如果沒有找到則返回null
while (node.keyValue != Key) {
if (Key < node.keyValue) { // 如果要查找的值小於節點值則指向左節點
node = node.leftNode;
} else { // 否則指向右節點
node = node.rightNode;
}
if (node == null) { // 如果節點爲空了則返回null
return null;
}
}
return node;
}
思路:最大值一直往右走,最小值一直往左走。
具體代碼:
public int max() {
TreeNode node = root;
TreeNode parent = null;
while (node != null) {
parent = node;
node = node.rightNode;
}
return parent.keyValue;
}
public int min() {
TreeNode node = root;
TreeNode parent = null;
while (node != null) {
parent = node;
node = node.leftNode;
}
return parent.keyValue;
}
樹的深度:
具體代碼:
int length(Node root){
int depth1;
int depth2;
if(root == null) return 0;
//左子樹的深度
depth1 = length(root.right);
//右子樹的深度
depth2 = length(root.left);
if(depth1>depth2)
return depth1+1;
else
return depth2+1;
}
參考鏈接:
http://blog.sina.com.cn/s/blog_401823210101f8t6.html
http://blog.sina.com.cn/s/blog_937cbcc10101dmqm.html
http://www.cnblogs.com/wuchanming/p/4067951.html
http://blog.csdn.net/fansongy/article/details/6798278
http://www.cricode.com/3489.html
http://www.cricode.com/3212.html
http://blog.csdn.net/sjf0115/article/details/8645991
http://www.cnblogs.com/vamei/archive/2013/03/17/2962290.html
http://blog.csdn.net/sysu_arui/article/details/7865876
http://blog.163.com/xiaopengyan_109/blog/static/14983217320108168618624/