《演算法導論》中紅/黑樹狀結構的C語言實現

來源:互聯網
上載者:User

 

《演算法導論》好是好,只是看虛擬碼太頭痛了,總想要是有個C語言版的《演算法導論》就好了。

前幾天研究了紅/黑樹狀結構,把它翻譯成C語言。在VC6.0上編譯測試通過。並且寫了一個CView類來顯示效果。

 

標頭檔 rb_tree.h

 

#ifndef RB_TREE_H
#define RB_TREE_H

enum NODECOLOR {RED = 0, BLACK = 1};

typedef int NODEDATA;

struct rb_tree_node
{
 NODECOLOR color;
 int key;
 NODEDATA info;
 rb_tree_node* left;
 rb_tree_node* right;
 rb_tree_node* parent;
};

struct rb_tree
{
 rb_tree_node* root;
 rb_tree_node* nil;
};

void init_rb_tree(rb_tree* t);

void left_rotate(rb_tree* t, rb_tree_node* x);
void right_rotate(rb_tree* t, rb_tree_node* x);

void rb_insert(rb_tree* t, int key, NODEDATA info = 0);
void rb_insert_(rb_tree* t, rb_tree_node* z);
void rb_insert_fixup(rb_tree* t, rb_tree_node* z);

void rb_delete(rb_tree* t, int key);
void rb_delete_(rb_tree* t, rb_tree_node* z);
void rb_delete_fixup(rb_tree* t, rb_tree_node* x);

int get_tree_height(rb_tree* t, rb_tree_node* x);

#endif

 

實現檔案:rb_tree.cpp

 

/*
 紅/黑樹狀結構的實現,根據《演算法導論》虛擬碼翻譯
 作者:胡維臣 huweichen@163.com
 時間:2010.6.26
*/
#include <stdlib.h>
//#include <malloc.h>

#include "rb_tree.h"

void init_rb_tree(rb_tree* t)
{
 rb_tree_node* node = new rb_tree_node; 

 node->color = BLACK;
 node->info = 0;
 node->key = 0;
 node->left = 0;
 node->parent = 0;
 node->right = 0;

 t->nil = node;
 t->root = t->nil;
}

int get_tree_height(rb_tree* t, rb_tree_node* x)

 int lh = 0;
 int rh = 0;

 if(x->left != t->nil)
 {
  lh = get_tree_height(t, x->left);
 }

 if(x->right != t->nil)
 {
  rh = get_tree_height(t, x->right);
 }

 if(lh > rh)
 {
  return lh + 1;
 }
 else
 {
  return rh + 1;
 }
}

void left_rotate(rb_tree* t, rb_tree_node* x)
{
 rb_tree_node* y = x->right;

 x->right = y->left; 
 if(y->left != t->nil)
 {
  y->left->parent = x;
 }

 y->parent = x->parent;
 if(x->parent == t->nil)
 {
  t->root = y;
 }
 else if(x == x->parent->left)
 {
  x->parent->left = y;
 }
 else
 {
  x->parent->right = y;
 }

 y->left = x;
 x->parent = y;
}

void right_rotate(rb_tree* t, rb_tree_node* x)
{
 rb_tree_node* y = x->left;

 //先把左子節點的右子節點作為左子節點
 x->left = y->right;
 if(y->right != t->nil)
 {
  y->right->parent = x;
 }

 //再把原左子節點替代自己的位置
 y->parent = x->parent;
 if(x->parent == t->nil)
 {
  t->root = y;
 }
 else if(x == x->parent->right)
 {
  x->parent->right = y;
 }
 else
 {
  x->parent->left = y;
 }

 //最後把自己作為原左子節點的右子節點
 y->right = x;
 x->parent = y;
}

void rb_insert(rb_tree* t, int key, NODEDATA info)
{
 rb_tree_node* node = new rb_tree_node;
 node->key = key;
 node->info = info;

 rb_insert_(t, node);
}

void rb_insert_(rb_tree* t, rb_tree_node* z)
{
 rb_tree_node* y = t->nil;
 rb_tree_node* x = t->root;

 //找一個合適的插入位置
 while(x != t->nil)
 {
  y = x;
  if(z->key == x->key)
  {
   delete z;
   return;
  }
  else if(z->key < x->key)
  {
   x = x->left;
  }
  else
  {
   x = x->right;
  }
 }

 //插入節點
 z->parent = y;
 if(y == t->nil)
 {
  t->root = z;
 }
 else if(z->key < y->key)
 {
  y->left = z;
 }
 else
 {
  y->right = z;
 }

 //將新節點的其它域賦值
 z->left = t->nil;
 z->right = t->nil;
 z->color = RED;

 //修改樹,以滿足紅/黑樹狀結構的性質
 rb_insert_fixup(t, z);
}

void rb_insert_fixup(rb_tree* t, rb_tree_node* z)
{
 rb_tree_node* y = t->nil;
 
 //迴圈直到父節點為黑色
 while(z->parent->color == RED)
 {
  //如果父節點是祖父節點的左子節點
  if(z->parent == z->parent->parent->left)
  {
   y = z->parent->parent->right;
   //如果叔叔節點也是紅色,則把父節點和叔叔節點都設為黑色,把祖父節點設為紅色
   if(y->color == RED)
   {
    z->parent->color = BLACK;
    y->color = BLACK;
    z->parent->parent->color = RED;
    z = z->parent->parent;
   }
   //如果叔叔節點是黑色,那麼把父節點變成黑色,把祖父節點變成紅色,
   //然後右旋轉,使祖父節點成為叔叔節點
   else
   {
    //如果是右子節點,先左旋轉,這樣下面的右旋轉後才會對稱
    if(z == z->parent->right)
    {
     z = z->parent;
     left_rotate(t, z);
    }
    z->parent->color = BLACK;
    z->parent->parent->color = RED;
    right_rotate(t, z->parent->parent);
   }
  }
  //下面代碼是和上面對稱的
  else
  {
   y = z->parent->parent->left;
   if(y->color == RED)
   {
    z->parent->color = BLACK;
    y->color = BLACK;
    z->parent->parent->color = RED;
    z = z->parent->parent;
   }
   else
   {
    if(z == z->parent->left)
    {
     z = z->parent;
     right_rotate(t, z);
    }
    z->parent->color = BLACK;
    z->parent->parent->color = RED;
    left_rotate(t, z->parent->parent);
   }
  }
 }

 t->root->color = BLACK;
}

rb_tree_node* find_node(rb_tree* t, int key)
{
 rb_tree_node* x = t->root;

 while(x != t->nil)
 {
  if(key == x->key)
  {
   return x;
  }
  else if(key < x->key)
  {
   x = x->left;
  }
  else
  {
   x = x->right;
  }
 }

 return t->nil;
}

void rb_delete(rb_tree* t, int key)
{
 rb_tree_node* x = find_node(t, key);
 if(x != t->nil)
 {
  rb_delete_(t, x);
 }
}

rb_tree_node* tree_minimum(rb_tree* t, rb_tree_node* x)
{
 while(x->left != t->nil)
 {
  x = x->left;
 }

 return x;
}

rb_tree_node* tree_successor(rb_tree* t, rb_tree_node* x)
{
 rb_tree_node* y = t->nil;
 if(x->right != t->nil)
 {
  return tree_minimum(t, x->right);
 }

 y = x->parent;
 while(y != t->nil && x == y->right)
 {
  x = y;
  y = y->parent;
 }

 return y;
}

void rb_delete_(rb_tree* t, rb_tree_node* z)
{
 rb_tree_node* y = t->nil;
 rb_tree_node* x = t->nil;

 //只要z有一個位元組點不為空白,y就等於z,否則y為z的後驅
 if(z->left == t->nil || z->right == t->nil)
 {
  y = z;
 }
 else
 {
  y = tree_successor(t, z);
 }

 //把x設為y的子節點,當然也有可能為nil
 if(y->left != t->nil)
 {
  x = y->left;
 }
 else
 {
  x = y->right;
 }

 //從樹中刪除y,但暫時不釋放y所佔記憶體,因為後面還要用到
 x->parent = y->parent;
 if(y->parent == t->nil)
 {
  t->root = x;
 }
 else
 {
  if(y == y->parent->left)
  {
   y->parent->left = x;
  }
  else
  {
   y->parent->right = x;
  }
 }

 //如果y不等於z,而是z的後驅,則把y的關鍵字和資料拷貝到z
 if(y != z)
 {
  z->key = y->key;
  z->info = y->info;
 }

 //如果被刪的y是黑結點,樹的紅黑性質遭到破壞,即黑高度變了,則需要調整
 if(y->color == BLACK)
 {
  rb_delete_fixup(t, x);
 }

 //這時,可以釋放y所佔記憶體了
 delete y;
}

void rb_delete_fixup(rb_tree* t, rb_tree_node* x)
{
 rb_tree_node* w = t->nil;

 //x的顏色為只有在黑色情況下才需要調整,如果x為紅色,直接把它設為黑色就可以了
 while(x != t->root && x->color == BLACK)
 {
  if(x == x->parent->left)
  {
   //把w設為兄節點
   w = x->parent->right;

   //如果w為紅色,那麼把w設為黑色,把w的父節點設為紅色,然後把父節點左旋轉
   if(w->color == RED)
   {
    w->color = BLACK;
    x->parent->color = RED;
    left_rotate(t, x->parent);
    w = x->parent->right;
   }
   
   //如果w的左右子節點都為黑色,把w設為紅色,把x設為x的父節點,
   //這時,新x的左右子節點的黑高度就一致了,即x是一顆紅/黑樹狀結構,但新x的兄節點的
   //黑高度仍然大一個,所以進入下一次循壞,繼續調整
   if(w->left->color == BLACK && w->right->color == BLACK)
   {
    w->color = RED;
    x = x->parent;
   }
   else
   {
    //如果w的左子節點為紅,右子節點為黑,則先把w設為紅色,左子節點設黑色,
    //然後右旋轉,這樣做是為下面的左旋轉做好準備
    if(w->right->color == BLACK)
    {
     w->left->color = BLACK;
     w->color = RED;
     right_rotate(t, w);
     w = x->parent->right;
    }
    
    //把w設為父節點的顏色,這樣做是為了在下面的左旋轉後使新舊父節點的顏色不變,
    //再把w的父節點和右子節點都設為黑色,然後左旋轉,
    //這樣左子節點和右子節點的黑高度就一致了,不用再調整了,所以把x設為root。
    w->color = x->parent->color;
    x->parent->color = BLACK;
    w->right->color = BLACK;
    left_rotate(t, x->parent);
    x = t->root;
   }
  }
  //下面代碼和上面是對稱的
  else
  {
   w = x->parent->left;
   if(w->color == RED)
   {
    w->color = BLACK;
    x->parent->color = RED;
    right_rotate(t, x->parent);
    w = x->parent->left;
   }
   if(w->right->color == BLACK && w->left->color == BLACK)
   {
    w->color = RED;
    x = x->parent;
   }
   else
   {
    if(w->left->color == BLACK)
    {
     w->right->color = BLACK;
     w->color = RED;
     left_rotate(t, w);
     w = x->parent->left;
    }

    w->color = x->parent->color;
    x->parent->color = BLACK;
    w->left->color = BLACK;
    right_rotate(t, x->parent);
    x = t->root;
   }
  }
 }

 x->color = BLACK;
}

 

用來顯試效果的CTestRBTreeView類

void CTestRBTreeView::OnDraw(CDC* pDC)
{
 CTestRBTreeDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);
 
 if(rb_tree1.root == rb_tree1.nil)
  return;

 CRect rcClient;
 GetClientRect(&rcClient);
 int x = rcClient.Width() / 2;
 int y = 30;

 int level = get_tree_height(&rb_tree1, rb_tree1.root);

 DrawRBTree(pDC, &rb_tree1, rb_tree1.root, level, x, y);
}

 

#define NodeDistance 24
#define Radius 20

void CTestRBTreeView::DrawRBTree(CDC *pDC, rb_tree* t, rb_tree_node* node, int level, int x, int y)
{
 if(node == t->nil)
  return;
 
 CPen pen;
 CPen penBlue;
 penBlue.CreatePen(PS_SOLID, 1, COLORREF(0xff0000));

 if(node->left != t->nil)
 {
  int xLeft = x - (NodeDistance * (pow(2, level) - 1)) / 4;

  pDC->SelectObject(&penBlue);
  pDC->MoveTo(x, y);
  pDC->LineTo(xLeft, y + 60);

  DrawRBTree(pDC, t, node->left, level - 1, xLeft, y + 60);

 }

 if(node->right != t->nil)
 {
  int xRight = x + (NodeDistance * (pow(2, level) - 1)) / 4;

  pDC->SelectObject(&penBlue);
  pDC->MoveTo(x, y);
  pDC->LineTo(xRight, y + 60);

  DrawRBTree(pDC, t, node->right, level - 1, xRight, y + 60);
 }

 if(node->color == RED)
 {
  pDC->SetTextColor(0x0000ff);
  pen.CreatePen(PS_SOLID, 1, 0x0000ff);
 }
 else
 {
  pDC->SetTextColor(0);
  pen.CreatePen(PS_SOLID, 1, COLORREF(0));
 }
 pDC->SelectObject(&pen);

 CRect rect;
 rect.left = x - 12;
 rect.top = y - 12;
 rect.right = x + 12;
 rect.bottom = y + 12;

 pDC->Ellipse(&rect);

 CString str;
 str.Format("%d", node->key);
 if(node->key < 10)
 {
  pDC->TextOut(x - 4, y - 8, str);
 }
 else
 {
  pDC->TextOut(x - 8, y - 8, str);
 } 
}

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.