二叉搜索樹

二叉搜索樹是一種常用的數據結構,其也是面試中常被問到的問題,因此掌握二叉搜索樹十分必要。
本文就二叉搜索樹上的一些常見操作進行較爲詳細的介紹。


二叉搜索樹

首先看一下二叉搜索樹的定義:

二叉搜索樹或者是一棵空樹,或者是具有下列性質的二叉樹: 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; 它的左、右子樹也分別爲二叉排序樹。——百度百科

憑藉二叉搜索樹的性質,其可以用做字典樹和優先隊列。

二叉搜索樹上的常見操作主要有查找,插入,刪除,求最大值,最小值,求一個節點的前驅結點和後繼節點,前驅結點和後繼節點我會單獨寫文章來介紹。上述所有的操作都能在O(lgN)時間內完成,也就是樹的高度。

操作

我們首先定義二叉搜索樹節點的結構:

struct TreeNode {
      int val;
      TreeNode *left;
      TreeNode *right;
      TreeNode(int x) : val(x), left(NULL), right(NULL) {}
  };

可以看出我們不考慮有父親指針。

查找

二叉搜索樹的查找類似於二分查找。給定一個value值,我們從根節點root開始,

  • 若當前節點的val值和value相等,那麼就返回該節點
  • 若val值小於value,就查找當前節點的右子樹
  • 若val值大於value,就查找當前節點的左子樹

整體思路非常簡單,實現代碼如下:

TreeNode* TreeSearch(int val,TreeNode* root)
{
   TreeNode* cur = root;
   while(cur)
   {
      if(cur->val == val)  return cur;
      if(cur->val > val) cur = cur->left;
      else cur = cur->right;
   }
  return cur;
}

插入

插入過程和查找過程比較像,從根節點開始不斷向下查找插入的位置,過程一直記錄當前節點的父親節點,最後完成插入操作。代碼如下:

TreeNode* TreeInsert(int val,TreeNode* root)
{
    TreeNode* cur = root;
    TreeNode* parent =NULL;
     while(cur)
   {
      parent = cur;
      if(cur->val == val)  return cur;
      if(cur->val > val) cur = cur->left;
      else cur = cur->right;
   }
   TreeNode* newNode = new TreeNode(val);

   if(NULL != parent)//非空樹
   {   if(parent->val > val)
            parent->left = newNode;
        else
            parent->right = newNode; 
   }
   return newNode;
}

刪除節點

刪除節點是這幾個操作中最複雜的一個操作,要分3中不同的情況討論。
根據給定val值找到對應的節點,若

  • 該節點沒有左子樹也沒有右子樹,那麼直接刪除該節點
  • 該節點只有左子樹或者只有右子樹,那麼可以通過其父親節點和其子節點建立一條連接來刪除目標節點
  • 該節點既有左子樹又有右子樹,那麼首先要刪除當前節點的後繼節點m(該節點是val值大於當前節點val值中最小的節點,該節點一定沒有左子樹),再用m的內容來替代當前節點

下面盜用算法導論中圖來進行直觀的解釋…

沒有左右子樹
沒有左右子樹的情況.

這裏寫圖片描述
只有左子樹或者只有右子樹的情況

這裏寫圖片描述
左右子樹都有的情況

實現代碼如下:


TreeNode* getNextNode(TreeNode* root)
{
   if(NULL == root) return root;
   while(root->left)
   {
      root = root->left;
   }
   return root;
}

void TreeDelete(int val, TreeNode* root)
{
    TreeNode* node= root;
    TreeNode* parent =NULL;
     while(cur)
   {
      parent = cur;
      if(cur->val == val)  break;
      if(cur->val > val) cur = cur->left;
      else cur = cur->right;
   }
    if(NULL == node) return;
    if(!node->left && !node->right)//左右子樹都沒有的情況
     {     delete node; return;}
    if(node->left && node->right)//左右子樹都有
    {
       //找到node節點的後繼節點 即是右子樹中val最小的節點(求後繼節點的一種情況)
       TreeNode* next = getNextNode(node->right);
       int newVal = next->val;
       TreeDelete(next->val,root);//刪除後繼節點
       node->val = newVal;//重新設定當前節點的val值

    }else{
    //只有左子樹或者右子樹
       TreeNode* child = node->left ? node->left : node->right;
       if(parent->left == node)
          parent->left = child;
       else
          parent->right =child;
       delete node;
    }
}

最大值最小值

返回一個二叉搜索樹中的最大值最小值可以說是最簡單的一個操作,最大值是樹中“最右邊”的節點的val值,最小值是“最左邊”的節點的val值,實現代碼如下:

int getMax(TreeNode* root)
{
   if(NULL == root) return -1;//?並不是很合適,假設樹中元素值都爲正..
   while(root->right)
      root = root->right;
   return root->val;
}

int getMin(TreeNode* root)
{
   if(NULL == root) return -1;//?並不是很合適,假設樹中元素值都爲正..
   while(root->left)
      root = root->left;
   return root->val;
}

本文總結了二叉搜索樹的常見操作,這些操作包括:查找,插入,刪除,最大最小值。這些操作的時間複雜度都爲O(lgN),即爲樹的高度。求一個節點的後繼和前驅節點會在另外一篇文章中給出..本文有點類似搬運工的感覺,但是自己總結一遍總會比不總結強,代碼自敲,有問題麻煩請指出哈。

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