JAVA數據結構:二叉樹


為什么使用樹:

   樹結合了兩種數據結構的有點:一種是有序數組,樹在查找數據項的速度和在有序數組中查找一樣快;另一種是鏈表,樹在插入數據和刪除數據項的速度和鏈表一樣。既然這樣,我就要好好去學了....

(最主要討論的是二叉樹中的二叉搜索樹,即一個節點的左子節點關鍵值小於這個節點,右子節點的關鍵值大於這個節點)

 

設計前的思考:

樹——>元素(節點)

class Node
{
   public int iData ;
   public float fData ;
   public Node left ;
   public Node right ;

   //方法
   public Node(int iData,float fData){}
   public void displayNode(){} 
}

 

class Tree
{
   Node root ;//樹根
 
   //方法
   public void insert(){}
   public void displayTree(){}
   public void find(){}
   public void delete(){}
}

 

插入數據:

View Code
 1 //插入子節點
 2    public void insert(int iData ,float fData)
 3    {
 4        Node newNode = new Node(iData,fData) ;
 5        
 6        if(root == null)
 7           root = newNode ;
 8        else
 9        {
10            Node current = root ;
11            Node parent ;
12            while(true)//尋找插入的位置     
13            {
14                parent = current ;
15                if(iData<current.iData)
16                {
17                    current = current.left ;
18                    if(current == null)
19                    {
20                        parent.left = newNode ;
21                        return ;
22                     }
23                }
24                else
25                {
26                    current =current.right ;
27                    if(current == null)
28                    {
29                        parent.right = newNode ;
30                        return ;
31                     }
32                 }
33            }
34        }
35    }

遍歷樹:

View Code
View Code
 1  //中序遍歷方法
 2    public void inOrder(Node localRoot)
 3    {
 4        if(localRoot != null)
 5        {
 6            inOrder(localRoot.left) ;//調用自身來遍歷左子樹
 7            localRoot.displayNode() ;//訪問這個節點
 8            inOrder(localRoot.right) ;//調用自身來遍歷右子樹
 9        }
10    }

查找某個節點:

View Code
 1  //查找某個節點
 2    public Node find(int iData)
 3    {
 4        Node current = root ;
 5        while(current.iData != iData)
 6        {
 7            if(current.iData<iData)
 8               current = current.right ;
 9            else
10               current = current.left ;
11            if(current == null)
12                return null ;
13         }
14        return current ;
15     }

 

查找樹中關鍵字的最大值和最小值:

最大值:不斷地尋找右子節點

最小值:不斷地尋找左子節點

View Code
View Code
 1 //查找關鍵字最小的節點
 2    public Node findMinNode()
 3    {
 4        Node current , last ;
 5        last = null ;
 6        current = root ;
 7        if(current.left == null)
 8           return current ;
 9        else
10        {
11             while(current != null)
12            {
13               last = current ;
14               current = current.left ;
15            }
16            return last ;
17        }
18    }

 刪除某個節點:

 思考:

1).先找到要刪除的節點:

 1  public boolean delete(int key)
 2    {
 3        //先找到需要刪除的節點
 4        Node current = root ;
 5        Node parent = root ;
 6        boolean isLeftChild = false ;
 7        
 8        while(current.iData != key)//顯然,當current.iData == key 時,current 就是要找的節點
 9        {
10            parent = current ;
11            if(key < current.iData)
12            {
13                isLeftChild = true ;
14                current = current.left ;
15            }
16            else
17            {
18                isLeftChild = false ;
19                current = current.right ;
20            }
21            if(current == null)//找不到key時返回false
22               return false ;
23        }
24 //continue ........
25 }

2).再考慮要刪除的節點是怎樣的節點,經分析,有三種情況:葉節點、有一個節點的節點、有兩個節點的節點

A).如果刪除的是一個葉子節點,直接刪除即可

//接上................

 //分情況考慮刪除的節點
       //刪除的節點為葉節點時
       if(current.left == null && current.right == null)
       {
           if(current == root)
              root = null ;
           else
               if(isLeftChild)
                  parent.left = null ;
               else
                  parent.right = null ;
       }

//continue...........

 

B).如果刪除的節點有一個節點時:分兩種情況,刪除的節點只有一個左子節點,或者只有一個右子節點

//接上.......
//刪除的節點有一個子節點
       else
          if(current.right == null)//刪除的節點只有一個左子節點時
          {
              if(current == root)//要刪除的節點為根節點
                 root = current.left ;
              else
                  if(isLeftChild)//要刪除的節點是一個左子節點
                     parent.left = current.left ;
                  else
                     parent.right = current.left ;//要刪除的節點是一個右子節點
          }
          else
              if(current.left == null)//刪除的節點只有一個右子節點時
              {
                  if(current == root)//要刪除的節點為根節點
                     root = current.right ;
                  else
                      if(isLeftChild)//要刪除的節點是一個左子節點
                         parent.left = current.right ;
                      else
                         parent.right = current.right ;//要刪除的節點是一個右子節點
              }
//continue.......

 

c).如果刪除的節點有兩個節點時:

 

這種情況就比較復雜,需要去尋找一個節點去替代要刪除的節點。這個節點應該是什么節點呢?

據書本介紹,最合適的節點是后繼節點,即比要刪除的節點的關鍵值次高的節點是它的后繼節點。

說得簡單一些,后繼節點就是比要刪除的節點的關鍵值要大的節點集合中的最小值。

以上面的為例,40的后繼節點為74,10的后繼節點是13,19的后繼節點時26

以下是尋找后繼節點的代碼:

 

 1 //返回后繼節點
 2    private Node getSuccessor(Node delNode)
 3    {
 4        Node successorParent = delNode ;//后繼節點的父節點
 5        Node successor = delNode ;//后繼節點
 6        Node current = delNode.right ;//移動到位置節點位置
 7        while(current != null)
 8        {
 9            successorParent = successor ;
10            successor = current ;
11            current = current.left ;
12        }
13        if(successor != delNode.right)
14        {
15           successorParent.left = successor.right ;
16           successor.right = delNode.right ;
17        }
18        return successor ;
19    }

 

找到了后繼節點,接着就要討論如何用后繼節點替代葯刪除的節點

a)如果后繼節點是剛好是要刪除節點的右子節點(此時可以知道,這個右子節點沒有左子點,如果有,就不該這個右子節點為后繼節點)

//要刪除的節點為左子節點時
parent.left = successor ;
successor.left = current.left ;

//要刪除的節點是右子節點時
parent.right = successor ;
successor.left = current.left ;

b)如果后繼節點為要刪除節點的右子節點的左后代:

//假如要刪除的節點為右子節點
successorParent.left = successor.right ;//第一步
successor.right = current.right ;//第二步
parent.right = successor ;
successor.left = current.left ;

//假設要刪除的節點為左子節點
successorParent.left = successor.right ;
successor.right = current.right ;
parent.left = successor ;
successor.left = current.left ;

注意:第一步和第二步在getSuccessor()方法的最后的if語句中完成

以下是刪除的節點有連個節點的代碼:

 1 //接上 
 2 //刪除的節點有兩個子節點
 3               else
 4               {
 5                   Node successor = getSuccessor(current) ;//找到后繼節點
 6                   if(current == root)
 7                      root = successor ;
 8                   else
 9                       if(isLeftChild)
10                          parent.left = successor ;
11                       else
12                          parent.right = successor ;
13                   successor.left = current.left ;
14               }
15 //continue....

綜合上述,給出delete()方法的代碼:

View Code
 1 //刪除某個節點
 2    public boolean delete(int key)
 3    {
 4        //先找到需要刪除的節點
 5        Node current = root ;
 6        Node parent = root ;
 7        boolean isLeftChild = false ;
 8        
 9        while(current.iData != key)//顯然,當current.iData == key 時,current 就是要找的節點
10        {
11            parent = current ;
12            if(key < current.iData)
13            {
14                isLeftChild = true ;
15                current = current.left ;
16            }
17            else
18            {
19                isLeftChild = false ;
20                current = current.right ;
21            }
22            if(current == null)//找不到key時返回false
23               return false ;
24        }
25        
26        //分情況考慮刪除的節點
27        //刪除的節點為葉節點時
28        if(current.left == null && current.right == null)
29        {
30            if(current == root)
31               root = null ;
32            else
33                if(isLeftChild)
34                   parent.left = null ;
35                else
36                   parent.right = null ;
37        }
38        //刪除的節點有一個子節點
39        else
40           if(current.right == null)//刪除的節點只有一個左子節點時
41           {
42               if(current == root)//要刪除的節點為根節點
43                  root = current.left ;
44               else
45                   if(isLeftChild)//要刪除的節點是一個左子節點
46                      parent.left = current.left ;
47                   else
48                      parent.right = current.left ;//要刪除的節點是一個右子節點
49           }
50           else
51               if(current.left == null)//刪除的節點只有一個右子節點時
52               {
53                   if(current == root)//要刪除的節點為根節點
54                      root = current.right ;
55                   else
56                       if(isLeftChild)//要刪除的節點是一個左子節點
57                          parent.left = current.right ;
58                       else
59                          parent.right = current.right ;//要刪除的節點是一個右子節點
60               }
61               //刪除的節點有兩個子節點
62               else
63               {
64                   Node successor = getSuccessor(current) ;//找到后繼節點
65                   if(current == root)
66                      root = successor ;
67                   else
68                       if(isLeftChild)
69                          parent.left = successor ;
70                       else
71                          parent.right = successor ;
72                   successor.left = current.left ;
73               }
74        return true ;
75    }

 

進一步考慮:

刪除那么復雜,那刪除是必要的嗎?我們可以給每個節點定義一個標志,該標志用於記錄該節點是否已經刪除了,

顯示樹時,先判斷該節點是否已經刪除,如果沒有,則顯示。

這樣的結果是,節點其實是沒有刪除的,這樣顯然逃避責任了。當樹中沒有那么多的刪除操作時,這也不失為一種好方法,例如:

已經離職的員工的檔案要永久地保存在員工的記錄中。

 

OVER.....( ^_^ )

 

 

 

 

 

 


免責聲明!

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



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