1. 程式人生 > >使用c++構建一個二叉樹

使用c++構建一個二叉樹

The binary tree is a fundamental data structure used in computer science. The binary tree is a useful data structure for rapidly storing sorted data and rapidly retrieving

stored data. A binary tree is composed of parent nodes, or leaves, each of which stores data and also links to up to two other child nodes (leaves) which can be visualized

spatially as below the first node with one placed to the left and with one placed to the right. It is the relationship between the leaves linked to and the linking leaf, also know

n as the parent node, which makes the binary tree such an efficient data structure. It is the leaf on the left which has a lesser key value (i.e., the value used to search for a le

af in the tree), and it is the leaf on the right which has an equal or greater key value. As a result, the leaves on the farthest left of the tree have the lowest values, whereas th

e leaves on the right of the tree have the greatest values. More importantly, as each leaf connects to two other leaves, it is the beginning of a new, smaller, binary tree. Due

to this nature, it is possible to easily access and insert data in a binary tree using search and insert functions recursively called on successive leaves. 

二叉樹是電腦科學中的一種基本資料結構。它是一種非常有用的資料結構用於有序資料的快速儲存和快速遍歷。二叉樹由父節點或者子葉組成,其中每一個都儲存有資料,並且連線到另外兩子節點(樹葉), 這樣的結構在空間上直觀的表現為在第一個節點下面有一個位置存放左節點,一個位置存放右節點,是被連線的節點(子葉)和去連線別的節點的節點(父節點)之間的關係。這使得二叉樹是一種高效的資料結構。二叉樹左邊的樹葉數值總是存放較小的值,而右邊的樹葉總存放較大的值。因此,左邊最底部樹

葉的值是最小的,反之,右邊最底部樹葉的值是最大的。最重要的是,每個連線著另外兩個節點的節點都是一個新二叉樹的開始根節點,這使得使用搜索和插入函式通過遞迴

輕鬆訪問二叉樹的每一個樹葉和插入資料到二叉樹成為可能。

The typical graphical representation of a binary tree is essentially that of an upside down tree. It begins with a root node, which contains the original key value. The root 

node has two child nodes; each child node might have its own child nodes. Ideally, the tree would be structured so that it is a perfectly balanced tree, with each node 

having the same number of child nodes to its left and to its right. A perfectly balanced tree allows for the fastest average insertion of data or retrieval of data. The worst case

scenario is a tree in which each node only has one child node, so it becomes as if it were a linked list in terms of speed. The typical representation of a binary tree looks 

like the following:

二叉樹最典型的圖形化表示就是一個倒置的樹,由根節點開始,其節點帶一個鍵值。根節點有兩個子節點,每個子節點可能又有自己的子節點,理想情況下,這棵樹將被構造成一顆完全平衡二叉樹,每一個左右子節點都有同等數目的子節點。完全平衡二叉樹能夠是插入資料和遍歷資料有最小的平均時間複雜度,最糟糕的情況就是每一個節點只有

一個子節點,這就讓這棵樹的結構和連結串列一樣的訪問速度了,下面是一個典型二叉樹的圖形表示:

       10
 
           /    \
 
          6    14
          /  \     /   \
 
      5    8 11  18

The node storing the 10, represented here merely as 10, is the root node, linking to the left and right child nodes, with the left node storing a lower value than the parent node, and the node on the right storing a greater value than the parent node. Notice that if one removed the root node and the right child nodes, that the node storing the 

value 6 would be the equivalent a new, smaller, binary tree.

儲存值為10的節點在這裡就簡單的用10表示,他是整棵樹的跟節點,連線了一個左子樹和一個右子樹,左子樹儲存比其父節點的數,右子樹儲存比其父節點大的數。注意,如果我們移走了根節點和和其右子樹,那麼儲存6的節點將成為一個新的,更小的樹的根。

The structure of a binary tree makes the insertion and search functions simple to implement using recursion. In fact, the two insertion and search functions are also both very similar. To insert data into a binary tree involves a function searching for an unused node in the proper position in the tree in which to insert the key value. The insert 

function is generally a recursive function that continues moving down the levels of a binary tree until there is an unused leaf in a position which follows the rules of 

placing nodes. The rules are that a lower value should be to the left of the node, and a greater or equal value should be to the right. Following the rules, an insert function 

should check each node to see if it is empty, if so, it would insert the data to be stored along with the key value (in most implementations, an empty node will simply be a 

NULL pointer from a parent node, so the function would also have to create the node). If the node is filled already, the insert function should check to see if the key value 

to be inserted is less than the key value of the current node, and if so, the insert function should be recursively called on the left child node, or if the key value to be inserted 

is greater than or equal to the key value of the current node the insert function should be recursively called on the right child node. 

二叉樹的結構使插入和查詢簡單的通過簡單的遞迴就能實現,事實上,插入操作和搜尋非常相似,在想二叉樹插入資料的時候,就涉及到去查詢一個在正確位置並且沒有被使用的節點,然後才將值插入。插入函式是一個不斷按指定規則向下面節點移動的遞迴函式,直到在正確位置找到一個沒有被使用的節點,它的移動原則就是:相比某一個節點

較小的值應該放在改節點的左子節點,大於或者等於就放在有子節點。根據這個原則,插入函式應該檢查每個節點是否為空,如果為空,那麼插入值的儲存應伴隨一個新的節

點,(在大多數程式碼實現中,空節點用NULL指標表示,所以插入函式需要建立新的節點)。如果找到節點已經有了一個確切的值,那麼插入函式就應該判斷新插入的值是不是

比當前節點上的值小,如果新的值教小的話,那麼函式就遞迴的呼叫函式去訪問當前節點的子節點;如果新的值大於或者等於當前節點的值,那麼就應該遞迴到有右子樹,直

到找到一個合適的位置並插入。

The search function works along a similar fashion. It should check to see if the key value of the current node is the value to be searched. If not, it should check to see if the  value to be searched for is less than the value of the node, in which case it should be recursively called on the left child node, or if it is greater than the value of the node,  it should be recursively called on the right child node. Of course, it is also necessary to check to ensure that the left or right child node actually exists before calling the 

function on the node.

搜尋函式和插入函式類似,它要判斷當前節點的值對否和要搜尋的值一致,如果不一致,那麼就判斷搜尋的值比當前節點的值小,如果是,訪問左節點的值繼續做對比,否則訪問右節點的值。當然,在遞迴中檢查並保證訪問的下一個左右子節點是存在的。

Because binary trees have log (base 2) n layers, the average search time for a binary tree is log (base 2) n. To fill an entire binary tree, sorted, takes roughly log (base 2) 

n * n. Let's take a look at the necessary code for a simple implementation of a binary tree. First, it is necessary to have a struct, or class, defined as a node.

因為二叉樹有log(base 2) n層,所以搜尋的平均時間複雜度是log(base 2) n。填充一整個有序的二叉樹大致的時間複雜度是log(base 2) n * n. 讓我們看看實現一個二叉樹的核心程式碼,首先,應該構造一個結構體,或者一個類,定義為node:

struct node
{
  	int key_value;  
  	node *left;  
  	node *right;
};

The struct has the ability to store the key_value and contains the two child nodes which define the node as part of a tree. In fact, the node itself is very similar to the node in a linked list. A basic knowledge of the code for a linked list will be very helpful in understanding the techniques of binary trees. Essentially, pointers are necessary to

allow the arbitrary creation of new nodes in the tree.

定義的結構體應該能夠儲存節點的鍵值並且包含定義為樹的一部分的兩個子節點。實際上,節點本身和連結串列的節點非常相似。連結串列的基礎知識對理解二叉樹是非常有幫助的,為了能連給樹建立意數量的節點,指標的的設定是必要的。

It is most logical to create a binary tree class to encapsulate the workings of the tree into a single area, and also making it reusable. The class will contain functions to

insert data into the tree and to search for data. Due to the use of pointers, it will be necessary to include a function to delete the tree in order to conserve memory after the

program has finished.

建立一個二叉樹類來封裝一棵樹的操作室最符合邏輯的。並且同時使其具有複用性。類中應該包含插入函式和搜尋函式,因為使用了指標,在程式完成工作後要使用刪除函式釋放記憶體。

class btree
{
public:        
	btree();        
	~btree();        
	void insert(int key);       
	node *search(int key);        
	void destroy_tree();    
private:        
	void destroy_tree(node *leaf);        
	void insert(int key, node *leaf);        
	node *search(int key, node *leaf);                
	node *root;
};

The insert and search functions that are public members of the class are designed to allow the user of the class to use the class without dealing with the underlying 

design. The insert and search functions which will be called recursively are the ones which contain two parameters, allowing them to travel down the tree. The 

destroy_tree function without arguments is a front for the destroy_tree function which will recursively destroy the tree, node by node, from the bottom up. 

The code for the class would look similar to the following:

Insert函式和search函式設定為類中的public方法,是為了讓類的使用者能更好的使用功能。含有兩個引數的Insert函式和search函式將會被遞迴呼叫,從而實現從上到下訪問這棵樹。沒有引數的destroy_tree函式是呼叫destroy_tree的前期步驟,含參destroy_tree函式將遞迴的從底部節點到跟節點一個一個的刪除。程式碼如下:

btree::btree()
{
	root=NULL;
}

It is necessary to initialize root to NULL for the  later functions to be able to recognize that it does not exist.

初始化root為NULL使後面的函式能夠判斷根已經不存在是必要的。
btree::~btree()
{  
	destroy_tree();
}

The destroy_tree function will set off the recursive function destroy_tree shown below which will actually delete all nodes of the tree.

destroy_tree函式用來觸發下面遞迴函式destroy_tree去刪除樹中的每一個節點

void btree::destroy_tree(node *leaf)
{
<span style="white-space:pre">	</span>if(leaf!=NULL)
  <span style="white-space:pre">	</span>{
 <span style="white-space:pre">		</span>destroy_tree(leaf->left);
   <span style="white-space:pre">		</span>destroy_tree(leaf->right);
<span style="white-space:pre">		</span>delete leaf;
<span style="white-space:pre">	</span>}
}

The function destroy_tree goes to the bottom of each part of the tree, that is, searching while there is a non-null node, deletes that leaf, and then it works its way back up. The function deletes the leftmost node, then the right child node from the leftmost node's parent node, then it deletes the parent node, and it continues this deletion 

working its way up to the node of the tree upon which delete_tree was originally called. In the example tree above, the order of deletion of nodes would be

5 8 6 11 18 14 10. Note that it is necessary to delete all the child nodes to avoid wasting memory.

destroy_tree函式從樹的底部經過樹的每一部分走到樹的頂部。也就是說,搜尋到每個非空的節點就刪除這個節點,然後函式返回一層,函式在刪除最左節點後然後就刪除

最左節點的父節點的右子節點,然後才刪除父節點,然後繼續以同樣的方式刪除同一個父節點下的另外一個節點,然後又刪除父節點,直到最後跟節點也沒刪除,例子中給

的數的刪除順序就是5 8 6 11 18 14 10. 刪除樹中每一個節點防止記憶體的浪費

void btree::insert(int key, node *leaf)
{
  if(key< leaf->key_value)
  {
    if(leaf->left!=NULL)
     insert(key, leaf->left);
    else
    {
      leaf->left=new node;
      leaf->left->key_value=key;
      leaf->left->left=NULL;    //Sets the left child of the child node to null
      leaf->left->right=NULL;   //Sets the right child of the child node to null
    }  
  }
  else if(key>=leaf->key_value)
  {
    if(leaf->right!=NULL)
      insert(key, leaf->right);
    else
    {
      leaf->right=new node;
      leaf->right->key_value=key;
      leaf->right->left=NULL;  //Sets the left child of the child node to null
      leaf->right->right=NULL; //Sets the right child of the child node to null
    }
  }
}

The case where the root node is still NULL will be  taken care of by the insert function that is nonrecursive and available to non-members of the class. The insert function searches, moving down the tree of children nodes, following the prescribed rules, left for a lower value to be inserted and right for a greater value, until it finds an empty 

node which it creates using the 'new' keyword and initializes with the key value while setting the new node's child node pointers to NULL. After creating the new node, 

the insert function will no longer call itself.

Root節點為NULL的情況將被insert函式考慮到,它並不是遞迴的,而且對於無成員的類也是有效的。Insert(帶引數)函式向下查詢沒個節點時,遵循前面所說的那個規則

-----左邊插入小的值,右邊插入大的值,直到它找到一空節點,那麼就建立一個新的節點儲存鍵值並設定子節指向NULL,然後插入到空節點處,insert函式就停止遞迴。

node *btree::search(int key, node *leaf)
{
  if(leaf!=NULL)
  {
    if(key==leaf->key_value)
      return leaf;
    if(key<leaf->key_value)
      return search(key, leaf->left);
    else
      return search(key, leaf->right);
  }
  else return NULL;
}

The search function shown above recursively moves down the tree until it either reaches a node with a key value equal to the value for which the function is searching or until the function reaches an uninitialized node, meaning that the value being searched for is not stored in the binary tree. It returns a pointer to the node to the previous 

instance of the function which called it, handing the pointer back up to the search function accessible outside the class.

上面的search函式遞迴的從跟節點向下搜尋這棵樹,直到找到一個和目標值相同的值,或者遇到了沒有初始化的節點,意味著這個樹中並不存在要查詢的目標值。函式返回一個指向節點的指標給前面例項物件呼叫它的函式,處理search函式返回的指標在類為是可行的。

void btree::insert(int key)
{
  if(root!=NULL)
    insert(key, root);
  else
  {
    root=new node;
    root->key_value=key;
    root->left=NULL;
    root->right=NULL;
  }
}

The public version of the insert function takes care of the case where the root has not been initialized by allocating the memory for it and setting both child nodes to NULL and setting the key_value to the value to be inserted. If the root node already exists, insert is called with the root node as the initial node of the function, and the recursive 

insert function takes over.

公共insert函式在發現root節點為NULL的時候會為其開闢記憶體並設定其兩個子節點都為NULL,並將鍵值插入root節點,如果root節點已經存在,insert函式就會以root節點為

這個函式的初始節點遞迴的呼叫insert將節點插入完畢。

node *btree::search(int key)
{
  return search(key, root);
}

The public version of the search function is used to set off the search recursion at the root node, keeping it from being necessary for the user to have access to the root 

node.

公共函式search的作用是啟動從樹跟節點的遞迴搜尋,通過它能夠使使用者的search(key, root)正常放問到根節點

void btree::destroy_tree()
{
  destroy_tree(root);
}
The public version of the destroy tree function is merely used to initialize the recursive destroy_tree function which then deletes all the nodes of the tree. 

共的destroy_tree函式同查詢一樣,用於啟動destroy_tree(root)刪除樹中的所用節點。