手撕 AVL 树 C 代码

一、调整不平衡的 AVL 树(左旋 & 右旋)

在这里插入图片描述

注意

调整前,首先要学会找到最小失衡子树,参见下例
在这里插入图片描述
新插入的节点:80
在这里插入图片描述

二、失衡情况

1、插入

1.1、LL 型失衡

在这里插入图片描述
新插入的节点:10
失衡位置:root 左孩子节点的左子树上
平衡方法:右旋

1.2、LR 型失衡

在这里插入图片描述
新插入的节点:25
失衡位置: root 左孩子节点的右子树上
平衡方法:

  1. 左旋 root 左子树
  2. 右旋

1.3、RR 型失衡

失衡位置:root 右孩子节点的右子树上
平衡方法:左旋

1.4、RL 型失衡

失衡位置:root 右孩子节点的左子树上
平衡方法:

  1. 右旋 root 右子树
  2. 左旋

2、删除(LL、LR、RR & RL)

在这里插入图片描述
删除节点:60

  1. 先找到最小失衡子树
    在这里插入图片描述
  2. 平衡方法:右旋(参见章节一中的右旋示例图)

二、代码实现

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

typedef struct Node
{
  int val;     /* 数据域 */
  int height;  /* 树高 */
  struct Node *left;
  struct Node *right;
} Node;

Node *newNode(int val)
{
    Node *node = (Node *)malloc(sizeof(Node));
    node->val = val;
    node->height = 1;
    node->left = NULL;
    node->right = NULL;

    return node;
}

/* 获取树高 */
int getHeight(Node *node) 
{
  /* 空树 */
  if (!node)
    return 0;
   
  return node->height;
}

int max(int a, int b) 
{
  return (a > b) ? a : b;
}

/*
 * 1、原根节点的右孩子节点作为新树的根节点
 * 2、原根节点会作为新树根节点的左孩子节点
 * 3、如果新根节点原来有左子树,那么就作为旧根节点的右子树
 */
Node *leftRoate(Node *root)
{
  /* 1、原根节点的右孩子节点作为新树的根节点 */
  Node *newroot = root->right;
  /* T2 用来保存新树根原来的左子树 */
  Node *T2 = newroot->left;

  /* 2、原根节点会作为新树根节点的左孩子节点 */
  newroot->left = root;
  /* 3、如果新根节点原来有左子树,那么就作为旧根节点的右子树 */
  root->right = T2;

  /* 更新树高,root newroot */
  root->height = 1 + max(getHeight(root->left), getHeight(root->right));
  newroot->height = 1 + max(getHeight(newroot->left), getHeight(newroot->right));

  return newroot;
}

/* 右旋 */
Node *rightRotate(Node *root)
{
  Node *newroot = root->left;
  Node *T2 = newroot->right;

  newroot->right = root;
  root->left = T2;

  root->height = 1 + max(getHeight(root->left), getHeight(root->right));
  newroot->height = 1 + max(getHeight(newroot->left), getHeight(newroot->right));

  return newroot;
}

/* 获取平衡因子 */
int getBalance(Node *node) 
{
  return getHeight(node->left) - getHeight(node->right);
}

Node *insertNode(Node *node, int key)
{
  if (node == NULL)
    return newNode(key);

  if (key < node->val)
    node->left = insertNode(node->left, key);
  else if (key > node->val)
    node->right = insertNode(node->right, key);
  else
    return node;
    
  /* 更新树高 */
  node->height = 1 + max(getHeight(node->left), getHeight(node->right));

  /* 获取当前节点的平衡因子 */
  int balance = getBalance(node);
  /* LL 型失衡 */
  if (balance > 1 && getBalance(node->left) > 0)
    return rightRotate(node);

  /* LR 型失衡 */
  if (balance > 1 && getBalance(node->left) < 0) 
  {
    node->left = leftRoate(node->left);
    return rightRotate(node);
  }

  /* RR 型失衡 */
  if (balance < -1 && getBalance(node->right) < 0)
    return leftRoate(node);

  /* RL 型失衡 */
  if (balance < -1 && getBalance(node->right) > 0)
  {
    node->right = rightRotate(node->right);
    return leftRoate(node);
  }

  return node;
}

Node *deleteNode(Node *node, int key) 
{
  Node *temp;
  int   balance;
  
  if (node == NULL)
    return node;

  if (key < node->key)
    node->left = deleteNode(node->left, key);
  else if (key > node->key)
    node->right = deleteNode(node->right, key);
  else 
  {
    if (NULL == node->left && NULL == node->right) 
    {
      temp = node;
      node = NULL;
      free(temp);
    }
    else if (NULL == node->left && NULL != node->right)
    {
      temp = node;
      node = node->right;
      free(temp);
    }
    else if (NULL != root->left && NULL == root->right)
    {
      temp = node;
      node = node->left;
      free(temp);
    }
    else
    {
      Node *cur = node->right;
	
      while (cur->left)
        cur = cur->left;

      node->val = cur->val;
      node->right = deleteNode(node->right, cur->val);
    }
  }

  if (node == NULL)
    return node;

  node->height = 1 + max(getHeight(node->left), getHeight(node->right));

  balance = getBalance(node);
  /* LL 型失衡 */ 
  if (balance > 1 && getBalance(node->left) >= 0)
    return rightRotate(node);

  /* RR 型失衡 */
  if (balance < -1 && getBalance(node->right) <= 0)
    return leftRotate(node);

  /*
   * LL 和 RR 型失衡相较 insertNode() 的 LL 和 RR 有所不同
   * 会多出 getBalance(node->left) == 0 和 getBalance(node->right) == 0 的情况
   * 以 getBalance(node->left) == 0,也就是右旋为例,具体可参见章节二中 2 小节的示例图
   */

  /* LR 型失衡 */
  if (balance > 1 && getBalance(node->left) < 0) 
  {
    node->left = leftRotate(node->left);
    return rightRotate(node);
  }

  /* RL型失衡 */
  if (balance < -1 && getBalance(node->node) > 0) 
  {
    node->right = rightRotate(node->right);
    return leftRotate(node);
  }

  return node;
}

/* 先序遍历 */
void preOrder(Node *root)
{
  if (!root)
    return;
    
  printf("%d ", root->val);
  preOrder(root->left);
  preOrder(root->right);
}

/* 中序遍历 */
void midOrder(Node *root) 
{
  if (!root)
    return ;
    
   midOrder(root->left);
   printf("%d ", root->val);
   midOrder(root->right);
}

Node *find(Node *root, int key, int *counter)
{
  Node *cur = root;
  while (cur != NULL)
  {
    if (key < cur->val)
    {
      cur = cur->left;
      (*counter)++;
    }
    else if (key > cur->val) 
    {
      cur = cur->right;
      (*counter)++;
    }
    else
      return cur;
  }
    
  return NULL;
}

void test() 
{
  Node *root = NULL;

  root = insertNode(root, 10);
  root = insertNode(root, 20);
  root = insertNode(root, 30);
  root = insertNode(root, 40);
  root = insertNode(root, 50);
  root = insertNode(root, 60);
  root = insertNode(root, 70);

  int counter = 0;
  Node *result = find(root, 70, &counter);
  printf("找了几次: %d\n", counter);
  printf("-------先序遍历结果--------\n");
  preOrder(root);
  printf("\n-------中序遍历结果--------\n");
  midOrder(root);
  printf("\n");

  counter = 0;
  root = deleteNpde(root, 10);
  root = deleteNpde(root, 20);
  root = deleteNpde(root, 30);
  result = find(root, 40, &counter);
  printf("找了几次: %d\n", counter);
  printf("-------先序遍历结果--------\n");
  preOrder(root);
  printf("\n-------中序遍历结果--------\n");
  midOrder(root);
  printf("\n");
}

int main()
{
  test();
  return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值