1. 程式人生 > >樹堆(Treap)圖文詳解與實現

樹堆(Treap)圖文詳解與實現

1.Treap的定義

樹堆(Treap)是二叉排序樹(Binary Sort Tree)與堆(Heap)結合產生的一種擁有堆性質的二叉排序樹。

但是這裡要注意兩點,第一點是Treap和二叉堆有一點不同,就是二叉堆必須是完全二叉樹,而Treap並不一定是;第二點是Treap並不嚴格滿足平衡二叉排序樹(AVL樹)的要求,即樹堆中每個節點的左右子樹高度之差的絕對值可能會超過1,只是近似滿足平衡二叉排序樹的性質。

Treap每個節點記錄兩個資料,一個是鍵值,一個是隨機附加的優先順序,Treap在以關鍵碼構成二叉排序樹的同時,又以結點優先順序形成最大堆和最小堆。所以Treap必須滿足這兩個性質,一是二叉排序樹的性質,二是堆的性質。如下圖,即為一個樹堆。

這裡寫圖片描述

2.Treap的特點

Treap因在BST中加入了堆的性質,在以隨機順序將節點插入二叉排序
樹時,根據隨機附加的優先順序以旋轉的方式維持堆的性質,其特點是能基本實現隨機平衡的結構。相對於其他的平衡二叉搜尋樹,優點是實現簡單,因為Treap維護堆性質的方法只用到了旋轉,只需要兩種旋轉,易於維護,可用於高效快速查詢。

3.Treap的操作

3.1Treap的插入

給節點隨機分配一個優先順序,先和二叉排序樹(又叫二叉搜尋樹)的插入一樣,先把要插入的點插入到一個葉子上,然後再維護堆的性質。

以最小堆為例,如果當前節點的優先順序比其根節點小就旋轉。如果當前節點是根的左子節點就右旋。如果當前節點是根的右子節點就左旋。 即左旋能使根節點轉移到左邊,右旋能使根節點轉移到右邊。

下圖中,當X節點優先順序小於Y節點時右旋和Y節點優先順序小於X節點的左旋,其左右旋轉如下圖:

這裡寫圖片描述

插入寫成遞迴形式的話,只需要在遞迴呼叫完成後判斷是否滿足堆性質,如果不滿足就旋轉,實現相對簡單。其插入過程示例圖如下:

這裡寫圖片描述

時間複雜度:
由於旋轉是O(1)的,最多進行h次(h是樹的高度),插入的複雜度是O(h)的,在期望情況下h=O(log n),所以它的期望複雜度是O(log n)。

3.2Treap的刪除

(1)找到相應的結點;
(2)若該結點為葉子結點,則直接刪除;
(3)若該結點為只包含一個葉子結點的結點,則將其葉子結點賦值給它;
(4)若該結點為其他情況下的節點,則進行相應的旋轉,具體的方法就是每次找到優先順序最小的兒子,向與其相反的方向旋轉,直到該結點為上述情況之一,然後進行刪除。

時間複雜度:
最多進行O(h)次旋轉,期望複雜度是O(log n)。

3.3Treap的查詢

根據Treap具有二叉搜尋樹的性質,可以快速查詢所需節點。
時間複雜度:
期望複雜度是O(log n)。

4 資料結構的設計

結點採用結構體儲存,定義如下:

struct node
{ 
   int key;//關鍵字 
   int priority;//隨機優先順序
   node* left;//左節點
   node* right;//右節點
};

5.具體實現

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

typedef struct node Node;
typedef struct treap_t Treap;
typedef Node* nodePoint;

struct node
{ 

   int key;//關鍵字 
   int priority;//隨機優先順序
   Node* left;//左節點
   Node* right;//右節點

};

Node* root;//Treap的根結點

//左旋轉
 void rotate_left(Node* &node)
 {
   Node* x = node->right;
   node->right = x->left;
   x->left =node;
   node = x;
 }

 //右旋轉
 void rotate_right(Node* &node)
 {
   Node* x = node->left;
   node->left = x->right;
   x->right = node;
   node = x;
 }

 //插入操作
 void treap_insert(Node* &root, int key, int priority)
 {
   //根為NULL,則直接建立此結點為根結點
   if (root == NULL)
   {
     root = (Node*)new Node;
     root->left = NULL;
     root->right = NULL;
     root->priority = priority;
     root->key = key;
   }
   //向左插入結點
   else if (key <root->key)
   {
     treap_insert(root->left, key, priority);
     if (root->left->priority < root->priority)
       rotate_right(root);
   }

   //向左插入結點
   else
   {
     treap_insert(root->right, key, priority);
     if (root->right->priority < root->priority)
       rotate_left(root);
   }
 }

 //刪除結點操作
 void treap_delete(Node* &root, int key)
 {
   if (root != NULL)
   {
     if (key < root->key)
       treap_delete(root->left, key);
     else if (key > root->key)
       treap_delete(root->right, key);
     else
     {
       //左孩子為空
       if (root->left == NULL)
         root = root->right;

       //右孩子為空
       else if (root->right == NULL)
         root = root->left;

       //左右孩子均不為空
       else
       {
         //先旋轉,然後再刪除
         if (root->left->priority < root->right->priority)
         {
           rotate_right(root);
           treap_delete(root->right, key);
         }
         else
         {
           rotate_left(root);
           treap_delete(root->left,key);
         }
       }
     }
   }
 }

//中序遍歷
void in_order_traverse(Node* root)
{
  if (root!= NULL)
  {
    in_order_traverse(root->left);
    printf("%d\t", root->key);
    in_order_traverse(root->right);
  }
}

//計算樹的高度
int depth(Node* node)
{
    if(node == NULL)
        return -1;
    int l = depth(node->left);
    int r = depth(node->right);
    return (l < r)?(r+1):(l+1);
}

int main()
{
  srand(time(0));

  //產生最大偽隨機數0至RAND_MAX(32767)
  //printf("%d\n",RAND_MAX);

  //隨機插入10個節點
  printf("----------------------建立Treap樹堆-----------------------\n");
  printf("順序插入0至9十個數,鍵值與優先順序如下\n");
  for (int i = 0; i < 10; i++)
  {
    int pri=rand();
    printf("key:%d priority:%d\n",i,pri);
    treap_insert(root,i,pri);
  }

  //中序遍歷Treap
  printf("\n插入完畢,中序遍歷Treap所得結果為:\n");
  in_order_traverse(root);

  printf("\nTreap高度:%d\n", depth(root));

  printf("----------------------刪除結點-----------------------\n");
  printf("請輸入要刪除的結點鍵值\n");
  int rmKey;
  scanf("%d",&rmKey);
  treap_delete(root, rmKey);

  printf("\n刪除完畢,中序遍歷Treap所得結果為:\n");
  in_order_traverse(root);

 printf("\nTreap高度:%d\n", depth(root));
  getchar();
  getchar();
  return 0;
}

執行結果截圖:

這裡寫圖片描述

參考文獻