[原创] 二叉平衡树AVL的插入和删除的C实现源码

来源:互联网 发布:彩票数字组合软件 编辑:程序博客网 时间:2024/05/05 13:09
导读:
  复习数据结构,顺便用C实现了一下二叉平衡树AVL的插入和删除算法
  共享一下,呵呵
   #include
  #include
  #include
  #include
  #include
   typedef struct node node;
   struct node
  {
  node* parent;
  node* left;
  node* right;
  int balance; //左右子树高度之差
  int key;
  };
   extern void traverseAVL1(node* root); //中序遍历, 下面定义
   extern void traverseAVL2(node* root); //前序遍历, 下面定义
   int searchNode(int key, node* root, node** parent) //按key查找结点
  {
  node* temp;
  assert(root != NULL);
  temp = root;
  *parent = root->parent;
  while (temp !=NULL)
  {
  if (temp->key == key)
  return 1;
  else
  {
  *parent = temp;
  if (temp->key > key)
  temp = temp->left;
  else
  temp = temp->right;
  }
  }
  return 0;
   }
   node* minNode(node* root) //树root的最小结点
  {
  if (root == NULL)
  return NULL;
  while (root->left != NULL)
  root = root->left;
  return root;
  }
   node* maxNode(node* root) //树root的最大结点
  {
  if (root == NULL)
  return NULL;
  while (root->right != NULL)
  root = root->right;
  return root;
  }
   node* preNode(node* target) //求前驱结点
  {
  if (target == NULL)
  return NULL;
  if (target->left != NULL)
  return maxNode(target->left);
  else
  while ((target->parent!=NULL) && (target!=target->parent->right))
  target = target->parent;
  return target->parent;
  }
   node* nextNode(node* target) //求后继结点
  {
  if (target == NULL)
  return NULL;
  if (target->right != NULL)
  return minNode(target->right);
  else
  while ((target->parent!=NULL) && (target!=target->parent->left))
  target = target->parent;
  return target->parent;
  }
   node* adjustAVL(node* root, node* parent, node* child)
  {
  node *cur;
  assert((parent != NULL)&&(child != NULL));
  switch (parent->balance)
  {
  case 2:
  if (child->balance == -1)//LR型
  {
  cur = child->right;
  cur->parent = parent->parent;
  child->right = cur->left;
  if (cur->left != NULL)
  cur->left->parent = child;
  parent->left = cur->right;
  if (cur->right != NULL)
  cur->right->parent = parent;
  cur->left = child;
  child->parent = cur;
  cur->right = parent;
  if (parent->parent != NULL)
  if (parent->parent->left == parent)
  parent->parent->left = cur;
  else parent->parent->right = cur;
  else
  root = cur;
  parent->parent = cur;
  if (cur->balance == 0)
  {
  parent->balance = 0;
  child->balance = 0;
  }
  else if (cur->balance == -1)
  {
  parent->balance = 0;
  child->balance = 1;
  }
  else
  {
  parent->balance = -1;
  child->balance = 0;
  }
  cur->balance = 0;
  }
  else //LL型
  {
  child->parent = parent->parent;
  parent->left = child->right;
  if (child->right != NULL)
  child->right->parent = parent;
  child->right = parent;
  if (parent->parent != NULL)
  if (parent->parent->left == parent)
  parent->parent->left = child;
  else parent->parent->right = child;
  else
  root = child;
  parent->parent = child;
  if (child->balance == 1) //插入时
  {
  child->balance = 0;
  parent->balance = 0;
  }
  else //删除时
  {
  child->balance = -1;
  parent->balance = 1;
  }
  }
  break;
  
  case -2:
  if (child->balance == 1) //RL型
  {
  cur = child->left;
  cur->parent = parent->parent;
  child->left = cur->right;
  if (cur->right != NULL)
  cur->right->parent = child;
  parent->right = cur->left;
  if (cur->left != NULL)
  cur->left->parent = parent;
  cur->left = parent;
  cur->right = child;
  child->parent = cur;
  if (parent->parent != NULL)
  if (parent->parent->left == parent)
  parent->parent->left = cur;
  else parent->parent->right = cur;
  else
  root = cur;
  parent->parent = cur;
  if (cur->balance == 0)
  {
  parent->balance = 0;
  child->balance = 0;
  }
  else if (cur->balance == 1)
  {
  parent->balance = 0;
  child->balance = -1;
  }
  else
  {
  parent->balance = 1;
  child->balance = 0;
  }
  cur->balance = 0;
  }
  else //RR型
  {
  child->parent = parent->parent;
  parent->right = child->left;
  if (child->left != NULL)
  child->left->parent = parent;
  child->left = parent;
  if (parent->parent != NULL)
  if (parent->parent->left == parent)
  parent->parent->left = child;
  else parent->parent->right = child;
  else
  root = child;
  parent->parent = child;
  if (child->balance == -1) //插入时
  {
  child->balance = 0;
  parent->balance = 0;
  }
  else //删除时
  {
  child->balance = 1;
  parent->balance = -1;
  }
  }
  break;
  }
  return root;
  }
   node* insertNode(int key, node* root)
  {
  node *parent, *cur, *child;
  assert (root != NULL);
  if (searchNode(key, root, &parent)) //结点已存在
  return root;
  else
  {
  cur = (node*)malloc(sizeof(node));
  cur->parent = parent;
  cur->key = key;
  cur->left = NULL;
  cur->right = NULL;
  cur->balance = 0;
  if (keykey)
  {
  parent->left = cur;
  child = parent->left;
  }
  else
  {
  parent->right = cur;
  child = parent->right;
  }
  
  while ((parent != NULL)) //查找需要调整的最小子树
  {
  if (child == parent->left)
  if (parent->balance == -1)
  {
  parent->balance = 0;
  return root;
  }
  else if (parent->balance == 1)
  {
  parent->balance = 2;
  break;
  }
  else
  {
  parent->balance = 1;
  child = parent;
  parent = parent->parent;
  }
  else if (parent->balance == 1)
  {
  parent->balance = 0;
  return root;
  }
  else if (parent->balance == -1)
  {
  parent->balance = -2;
  break;
  }
  else
  {
  parent->balance = -1;
  child = parent;
  parent = parent->parent;
  }
  }
  
  if (parent == NULL)
  return root;
  return adjustAVL(root, parent, child);
  }
  }
   node* deleteNode(int key, node* root)
  {
  node *dNode, *parent, *child, *tp;
  int temp, tc;
  assert(root!=NULL);
  if (!searchNode(key, root, &parent))
  return root;
  else
  {
  if (parent == NULL)
  dNode = root;
  else if ((parent->left!=NULL)&&(parent->left->key==key))
  dNode = parent->left;
  else dNode = parent->right;
   child = dNode;
  while ((child->left!=NULL)||(child->right!=NULL)) //确定需要删除的结点
  {
  if (child->balance == 1)
  child = preNode(dNode);
  else child = nextNode(dNode);
  temp = child->key;
  child->key = dNode->key;
  dNode->key = temp;
  dNode = child;
  }
  
  child = dNode;
  parent = dNode->parent;
  
  while ((parent != NULL)) //查找需要调整的最小子树
  {
  if (child == parent->left)
  if (parent->balance == 1)
  {
  parent->balance = 0;
  child = parent;
  parent = parent->parent;
  }
  else if (parent->balance == -1)
  {
  parent->balance = -2;
  child = parent->right;
  temp = parent->right->balance;//临时变量保存
  tp = parent->parent;//临时变量保存
  if (tp != NULL)
  if (parent == tp->left)
  tc = 1;
  else tc = -1;
  else tc = 0;
  
  root = adjustAVL(root, parent, child);
  
  if (temp == 0)
  break;
  else
  {
  if (tc == 1)
  child = tp->left;
  else if (tc == -1)
  child = tp->right;
  parent = tp;
  }
   }
  else
  {
  parent->balance = -1;
  break;
  }
  else if (parent->balance == -1)
  {
  parent->balance = 0;
  child = parent;
  parent = parent->parent;
  }
  else if (parent->balance == 1)
  {
  parent->balance = 2;
  child = parent->left;
  temp = parent->left->balance;//临时变量保存
  tp = parent->parent;//临时变量保存
  if (tp != NULL)
  if (parent == tp->left)
  tc = 1;
  else tc = -1;
  else tc = 0;
  
  root = adjustAVL(root, parent, child);
  
  if (temp == 0)
  break;
  else
  {
  if (tc == 1)
  child = tp->left;
  else if (tc == -1)
  child = tp->right;
  parent = tp;
  }
  }
  else
  {
  parent->balance = 1;
  break;
  }
  }
  
  if (dNode->parent != NULL)
  if (dNode == dNode->parent->left)
  dNode->parent->left = NULL;
  else dNode->parent->right = NULL;
  free(dNode);
  if (root == dNode)
  root = NULL; //root被删除, 避免野指针
  dNode = NULL;
  
  return root;
  }
  }
   node* createAVL(int *data, int size)
  {
  int i, j;
  node *root;
  if (size<1)
  return NULL;
  root = (node*)malloc(sizeof(node));
  root->parent = NULL;
  root->left = NULL;
  root->right = NULL;
  root->key = data[0];
  root->balance = 0;
  
  for(i=1;i  root = insertNode(data, root);
  return root;
  }
   void destroyAVL(node* root)
  {
  if (root != NULL)
  {
  destroyAVL(root->left);
  destroyAVL(root->right);
  free(root);
  root=NULL;
  }
  
  }
   void traverseAVL1(node* root) //中序遍历
  {
  if (root != NULL)
  {
  traverseAVL1(root->left);
  printf("%d, %d/n", root->key, root->balance);
  traverseAVL1(root->right);
  }
  }
   void traverseAVL2(node* root) //先序遍历
  {
  if (root != NULL)
  {
  printf("%d, %d/n", root->key, root->balance);
  traverseAVL2(root->left);
  traverseAVL2(root->right);
  }
  }
   int main(int argc, char *argv[])
  {
  int data[] = {1, 5, 7, 4, 3, 2, 11, 9, 10};
  node* root;
  root = createAVL(data, sizeof(data)/sizeof(data[0]));
   printf("++++++++++++++++++++++++++++/n");
  traverseAVL1(root);
  printf("/n");
  traverseAVL2(root);
  
  root = deleteNode(5, root);
  printf("++++++++++++++++++++++++++++/n");
  traverseAVL1(root);
  printf("/n");
  traverseAVL2(root);
   root = deleteNode(3, root);
  printf("++++++++++++++++++++++++++++/n");
  traverseAVL1(root);
  printf("/n");
  traverseAVL2(root);
   root = deleteNode(1, root);
  printf("++++++++++++++++++++++++++++/n");
  traverseAVL1(root);
  printf("/n");
  traverseAVL2(root);
   root = deleteNode(7, root);
  printf("++++++++++++++++++++++++++++/n");
  traverseAVL1(root);
  printf("/n");
  traverseAVL2(root);
   root = deleteNode(4, root);
  printf("++++++++++++++++++++++++++++/n");
  traverseAVL1(root);
  printf("/n");
  traverseAVL2(root);
   root = deleteNode(2, root);
  printf("++++++++++++++++++++++++++++/n");
  traverseAVL1(root);
  printf("/n");
  traverseAVL2(root);
   destroyAVL(root);
  }
  
  [ 本帖最后由 ypxing 于 2007-12-8 12:06 编辑 ]
  cugb_cat回复于:2007-11-03 21:38:28
  顶~~~
  converse回复于:2007-11-03 21:42:56
  我也写过一个:
  http://www.cppblog.com/converse/archive/2007/08/29/31179.html
  细细想来,基本的数据结构除了图没有碰过以外其他都自己动手实践过了
  ypxing回复于:2007-11-03 21:47:46
  看过你写的红黑树
  准备自己也实现一下:mrgreen:
  引用:原帖由 converse 于 2007-11-3 21:42 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7548662&ptid=1011237]
  我也写过一个:
  http://www.cppblog.com/converse/archive/2007/08/29/31179.html
  细细想来,基本的数据结构除了图没有碰过以外其他都自己动手实践过了
  converse回复于:2007-11-03 21:52:08
  引用:原帖由 ypxing 于 2007-11-3 21:47 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7548693&ptid=1011237]
  看过你写的红黑树
  准备自己也实现一下:mrgreen:
  
  最近一直在想AVL树和红黑树效率上到底有什么区别?分别有哪些适用场合...
  更让我吐血的是,发明红黑树的哥们是怎么想到这么个鬼东西的:outu: :outu: :outu: :outu:
  ypxing回复于:2007-11-03 22:03:16
  印象里红黑树的应用似乎更广些
  引用:原帖由 converse 于 2007-11-3 21:52 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7548712&ptid=1011237]
  最近一直在想AVL树和红黑树效率上到底有什么区别?分别有哪些适用场合...
  更让我吐血的是,发明红黑树的哥们是怎么想到这么个鬼东西的:outu: :outu: :outu: :outu:
  converse回复于:2007-11-03 22:15:31
  我总觉得红黑树是AVL的特例
  :shock: :shock: :shock:
  win_hate回复于:2007-11-04 17:32:18
  红黑树不是 AVL 的特例
  AVL 的左子树和右子树高度最多相差 1, 而红黑的最长路径可以是最短路径的两倍, AVL 比红黑树要平衡。
  AVL 的删除最多需要 O(lg n) 次旋转,而红黑树的删除需要不多于 3 次旋转,但更改颜色次数以 log n 为界。
  红黑树的黑点是骨架,限制了整棵树的高度;而红点是肉,可以通过插入红点来把树撑高,但由于两个黑点之间最多有一个红点,所以高度仍受黑点限制。最短的路径上可以全是黑点,而最长的路径上,红点至多能达到黑点数减 1,或者说路径长度是 black height 的两倍。
  红黑树其实与 2-3-4 树(一种 B 树)对应,若把黑点的红儿子提上来,并与黑点一起,作为一个大点,就得到了一棵 2-3-4 树,树高就是 black height. 而红黑树的插入、删除与相应的 2-3-4 树的插入删除是可以相互印证的。
  [ 本帖最后由 win_hate 于 2007-11-4 17:36 编辑 ]
  baohuaihuai回复于:2007-11-04 17:37:11
  引用:原帖由 win_hate 于 2007-11-4 17:32 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7549245&ptid=1011237]
  红黑树不是 AVL 的特例
  AVL 的左子树和右子树高度最多相差 1, 而红黑的最长路径可以是最短路径的两倍, AVL 比红黑树要平衡。
  AVL 的删除最多需要 O(lg n) 次旋转,而红黑树的删除需要不多于 3 次旋转, ...
  版主给讲讲红黑树吧,之前看过你的AVL讲解.
  我也比较不懂发明红黑树的人是怎么想到的.
  win_hate回复于:2007-11-04 17:50:33
  红黑树刚出来的时候不叫红黑树,叫对称二元 B 树, 发明者是 Bayer, 后来 Sedgewick 和另一个不记得是谁把颜色加进去,演化成现在的红黑树。
  看他们的文章就可以知道红黑树是怎么演变过来的了。一些算法书的参考文献列出了相关文献。
  converse回复于:2007-11-04 17:51:34
  引用:原帖由 win_hate 于 2007-11-4 17:32 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7549245&ptid=1011237]
  红黑树不是 AVL 的特例
  AVL 的左子树和右子树高度最多相差 1, 而红黑的最长路径可以是最短路径的两倍, AVL 比红黑树要平衡。
  AVL 的删除最多需要 O(lg n) 次旋转,而红黑树的删除需要不多于 3 次旋转, ...
  是的,今天也正找这方面的资料,前面的理解是不对的.
  这里有几篇文章进行比较的:
  http://blog.chinaunix.net/u1/35281/showart_279925.html
  http://blog.csdn.net/naivebaby/archive/2006/11/04/1366579.aspx
  但是里面的解释:
  引用:
  AVL trees are actually easier to implement than RB trees because there are fewer cases. And AVL trees require O(1) rotations on an insertion, whereas red-black trees require O(lg n).
  In practice, the speed of AVL trees versus red-black trees will depend on the data that you're inserting. If your data is well distributed, so that an unbalanced binary tree would generally be acceptable (i.e. roughly in random order), but you want to handle bad cases anyway, then red-black trees will be faster because they do less unnecessary rebalancing of already acceptable data.On the other hand, if a pathological insertion order (e.g. increasing order of key) is common, then AVL trees will be faster, because the stricter balancing rule will reduce the tree's height.
  Splay trees might be even faster than either RB or AVL trees,depending on your data access distribution. And if you can use a hash instead of a tree, then that'll be fastest of all.
  并不能让我满意.
  自己尝试了一下插入同样多的数据,红黑树的效率比AVL慢一倍.
  augustusqing回复于:2007-12-07 15:17:39
  Hi,我用你的程序测试了下
  测试代码稍微修改了下,用随机数据,运行是带上参数20,如下所示,程序在pause暂停时就只输出了一个数,感觉你测试的数据太少了,^_^
   int main(int argc, char *argv[])
  {
  int t, i, N = atoi(argv[1]);
  int *data = malloc(N * sizeof(*data));
  for (i = 0; i < N; i++)
  {
  t = rand() + i;
  data = t;
  }
  node* root;
  root = createAVL(data, sizeof(data)/sizeof(data[0]));
   printf("++++++++++++++++++++++++++++/n");
  traverseAVL1(root);
  printf("/n");
  traverseAVL2(root);
  system("pause");
   root = deleteNode(5, root);
  printf("++++++++++++++++++++++++++++/n");
  traverseAVL1(root);
  printf("/n");
  traverseAVL2(root);
   root = deleteNode(3, root);
  printf("++++++++++++++++++++++++++++/n");
  traverseAVL1(root);
  printf("/n");
  traverseAVL2(root);
   root = deleteNode(1, root);
  printf("++++++++++++++++++++++++++++/n");
  traverseAVL1(root);
  printf("/n");
  traverseAVL2(root);
   root = deleteNode(7, root);
  printf("++++++++++++++++++++++++++++/n");
  traverseAVL1(root);
  printf("/n");
  traverseAVL2(root);
   root = deleteNode(4, root);
  printf("++++++++++++++++++++++++++++/n");
  traverseAVL1(root);
  printf("/n");
  traverseAVL2(root);
   root = deleteNode(2, root);
  printf("++++++++++++++++++++++++++++/n");
  traverseAVL1(root);
  printf("/n");
  traverseAVL2(root);
   destroyAVL(root);
  
  return 0;
  }
  ypxing回复于:2007-12-07 15:24:50
  >>int *data;
  >>...
  >> root = createAVL(data, sizeof(data)/sizeof(data[0]));
  你这样写代码是不对的
  引用:原帖由 augustusqing 于 2007-12-7 15:17 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7689048&ptid=1011237]
  Hi,我用你的程序测试了下
  测试代码稍微修改了下,用随机数据,运行是带上参数20,如下所示,程序在pause暂停时就只输出了一个数,感觉你测试的数据太少了,^_^
  int main(int argc, char *argv[])
  {
  in ...
  anthony1983回复于:2007-12-07 15:30:55
  代码拷下来了。
  记得以前看AVL树,自己只把插入实现了,删除有些复杂,就懒得实现了,现在正好学习一下
  ypxing回复于:2007-12-07 15:34:41
  >>root = createAVL(data, sizeof(data)/sizeof(data[0]));
  替换成root = createAVL(data, N);
  而且后面要删除key,你要修改一下
  引用:原帖由 anthony1983 于 2007-12-7 15:30 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7689132&ptid=1011237]
  代码拷下来了。
  记得以前看AVL树,自己只把插入实现了,删除有些复杂,就懒得实现了,现在正好学习一下
  [ 本帖最后由 ypxing 于 2007-12-7 15:37 编辑 ]
  anthony1983回复于:2007-12-07 15:35:50
  引用:原帖由 ypxing 于 2007-12-7 15:34 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7689161&ptid=1011237]
  >>root = createAVL(data, sizeof(data)/sizeof(data[0]));
  替换成root = createAVL(data, N);
  
  为啥
  ypxing回复于:2007-12-07 15:40:12
  他前面是这样定义data的:
  >>int t, i, N = atoi(argv[1]);
  >> int *data = malloc(N * sizeof(*data));
  >> for (i = 0; i < N; i++)
  >> {
  >> t = rand() + i;
  >> data = t;
  >> }
  >> node* root;
  >> root = createAVL(data, sizeof(data)/sizeof(data[0]));
  所以sizeof(data)为4, sizeof(data[0])也为4
  他误把data作为数组了, 这就错了
  引用:原帖由 anthony1983 于 2007-12-7 15:35 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7689170&ptid=1011237]
  为啥
  anthony1983回复于:2007-12-07 15:54:50
  引用:原帖由 ypxing 于 2007-12-7 15:40 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7689204&ptid=1011237]
  他前面是这样定义data的:
  >>int t, i, N = atoi(argv[1]);
  >> int *data = malloc(N * sizeof(*data));
  >> for (i = 0; i < N; i++)
  >> {
  >> t = rand() + i;
  >> data = t;
  >> }
  >> ...
  汗~~我拷下来的是数组~~int data[] = {1, 5, 7, 4, 3, 2, 11, 9, 10};
  ypxing回复于:2007-12-07 15:56:12
  误会了, 我回的是augustusqing的帖子,:mrgreen:
  引用:原帖由 anthony1983 于 2007-12-7 15:54 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7689307&ptid=1011237]
  汗~~我拷下来的是数组~~int data[] = {1, 5, 7, 4, 3, 2, 11, 9, 10};
  augustusqing回复于:2007-12-07 16:15:26
  晕,不好意思啊,^_^,现在能跑对了,我再测测看,^_^
  augustusqing回复于:2007-12-07 16:34:26
  我把你的destroy函数改成下面的形式,以方便测试delete操作,你看没问题吧,然后在main里面就直接调用一下destroy就能测试delete了
  发现用20个数据测的时候,能删除17个
   void destroyAVL(node* root)
  {
  /*
  if (root != NULL)
  {
  destroyAVL(root->left);
  destroyAVL(root->right);
  free(root);
  root=NULL;
  }
  */
  int key;
  while (root)
  {
  key = root->key;
  printf("Delete key %d /n", key);
  root = deleteNode(key, root);
  }
  }
  ypxing回复于:2007-12-08 12:08:26
  谢谢, 确实是我的程序的bug
  我已经更新了deleteNode函数, 呵呵,
  你可以再试试
  引用:原帖由 augustusqing 于 2007-12-7 16:34 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7689596&ptid=1011237]
  我把你的destroy函数改成下面的形式,以方便测试delete操作,你看没问题吧,然后在main里面就直接调用一下destroy就能测试delete了
  发现用20个数据测的时候,能删除17个
  void destroyAVL(node* root)
  {
  ...
  augustusqing回复于:2007-12-08 12:09:57
  哈哈,在等你更新了,^_^
  augustusqing回复于:2007-12-08 12:19:25
  没发现问题了,正常删除到底了
  建议你编译的时候,加上 -Wall -Wstrict-prototypes , ^_^,俺的个人习惯,不好意思
  hotjuly回复于:2007-12-08 22:31:53
  http://www.stanford.edu/~blp/avl/libavl.html/index.html
  GNU libavl 2.0.2
  libavl is a library in ANSI C for manipulation of various types of binary trees.

本文转自
http://www.chinaunix.net/jh/23/1011237.html
原创粉丝点击