(Data Structure)数据结构第六章 树和二叉树

6.1 树

  • 在计算机科学中,(英语:tree)是一种抽象数据类型(ADT)或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由n(n>0)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
  • 每个节点都只有有限个子节点或无子节点;
  • 没有父节点的节点称为根节点;
  • 每一个非根节点有且只有一个父节点;
  • 除了根节点外,每个子节点可以分为多个不相交的子树;
  • 树里面没有环路(cycle)
    在这里插入图片描述

术语:

  • 节点的度:一个节点含有的子树的个数称为该节点的度;
  • 树的度:一棵树中,最大的节点度称为树的度;
  • 叶节点或终端节点:度为零的节点;
  • 非终端节点或分支节点:度不为零的节点;
  • 父亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
  • 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
  • 兄弟节点:具有相同父节点的节点互称为兄弟节点;
  • 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
  • 深度:对于任意节点n,n的深度为从根到n的唯一路径长,根的深度为0;
  • 高度:对于任意节点n,n的高度为从n到一片树叶的最长路径长,所有树叶的高度为0;
  • 堂兄弟节点:父节点在同一层的节点互为堂兄弟;
  • 节点的祖先:从根到该节点所经分支上的所有节点;
  • 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。
  • 森林:由m(m>=0)棵互不相交的树的集合称为森林;

6.2 二叉树

  • 二元树(英语:Binary tree)是每个节点最多只有两个分支(即不存在分支度大于2的节点)的树结构。通常分支被称作“左子树”或“右子树”。二元树的分支具有左右次序,不能随意颠倒。

一棵有9个节点深度为3的二元树,其根节点的值为2
在这里插入图片描述

一棵简单的满二叉树
在这里插入图片描述

二叉树的性质

  • 性质1 :若二叉树的层次从1开始,二叉树的第 i 层最多有 2i-1 个结点
  • 性质2 :高度为 k 的二叉树最多有 2k-1个结点
  • 性质3 :对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数为 n2, 则有 n0=n2+1
  • 性质4 :具有 n 个结点的完全二叉树的高度为⌊log2n⌋+1

定义1 满二叉树(Full Binary Tree):一棵深度为k且有2k-1个结点的二叉树(每一层结点数都达到最大值)。
定义2 完全二叉树(Complete Binary Tree):若设二叉树的高度为k,除第 k 层外,其它各层 的结点数都达到最大个数,第 k层从右向左连续缺若干结点,这就是完全二叉树。
(1)叶子结点只可能在“最下”2层上出现
(2)对任一结点,若其右分支下子孙的最大层次为l,则其左分支下子孙的最大层次必为 l 或 l+1
在这里插入图片描述

二叉树的存储结构

// 定义结构体类型
typedef struct tree_node
{
    int data;
    struct tree_node *left;
    struct tree_node *right;
} tree_node;

// 创建结点
tree_node *create_tree_node(int data)
{
    tree_node *node = (tree_node *)malloc(sizeof(tree_node));
    node->data = data;
    node->left = NULL;
    node->right = NULL;
    return node;
}

在这里插入图片描述

二叉树的创建

// 创建二叉树
tree_node *create_binarytree(int *array, int start, int end)
{
    if (start > end)
    {
        return NULL;
    }
    int mid = (start + end) / 2;
    tree_node *node = create_tree_node(array[mid]);
    node->left = create_binarytree(array, start, mid - 1);
    node->right = create_binarytree(array, mid + 1, end);
    return node;
}

二叉树的递归遍历

// 先序遍历
void pre_order(tree_node *root)
{
    if (root == NULL)
    {
        return;
    }
    printf("%d ", root->data);
    in_order(root->left);
    in_order(root->right);
}

// 中序遍历
void in_order(tree_node *root)
{
    if (root == NULL)
    {
        return;
    }
    in_order(root->left);
    printf("%d ", root->data);
    in_order(root->right);
}

// 后序遍历
void post_order(tree_node *root)
{
    if (root == NULL)
    {
        return;
    }
    in_order(root->left);
    in_order(root->right);
    printf("%d ", root->data);
}

二叉树的非递归遍历

// 非递归先序遍历
void order_unrec(tree_node *root)
{
    if (root == NULL)
    {
        return;
    }
    tree_node *stack[MaxSize];
    int top = -1;
    tree_node *p = root;
    while (p || top != -1)
    {
        while (p)
        {
            stack[++top] = p;
            p = p->left;
        }

        if (top != -1)
        {
            p = stack[top--];
            printf("%d ", p->data);
            p = p->right;
        }
    }
}

// 层序遍历
void level_order(tree_node *root)
{
    if (root == NULL)
    {
        return;
    }
    tree_node *queue[MaxSize];
    int front = 0, rear = 0;
    queue[rear++] = root;

    while (rear > front)
    {
        tree_node *p = queue[front++];
        printf("%d ", p->data);
        if (p->left)
        {
            queue[rear++] = p->left;
        }
        if (p->right)
        {
            queue[rear++] = p->right;
        }
    }
}

二叉树的复制

// 二叉树的复制
tree_node *copy_binarytree(tree_node *root)
{
    if (root == NULL)
    {
        return NULL;
    }
    tree_node *copy = create_tree_node(root->data);
    copy->left = copy_binarytree(root->left);
    copy->right = copy_binarytree(root->right);
    return copy;
}

计算二叉树的叶子节点个数

// 计算二叉树的叶子节点个数
int count_leaves(tree_node *root)
{
    if (root == NULL)
    {
        return 0;
    }
    if (root->left == NULL && root->right == NULL)
    {
        return 1;
    }
    return count_leaves(root->left) + count_leaves(root->right);
}

计算二叉树的深度

// 计算二叉树的深度
int tree_depth(tree_node *root)
{
    if (root == NULL)
    {
        return 0;
    }
    int left_depth = tree_depth(root->left);
    int right_depth = tree_depth(root->right);
    return (left_depth > right_depth ? left_depth : right_depth) + 1;
}

计算二叉树的节点个数

// 计算二叉树的节点个数
int node_count(tree_node *root)
{
    if (root == NULL)
    {
        return 0;
    }
    return 1 + node_count(root->left) + node_count(root->right);
}

销毁二叉树

// 销毁二叉树
void destroy_binarytree(tree_node *root)
{
    if (root == NULL)
    {
        return;
    }
    destroy_binarytree(root->left);  // 销毁左子树
    destroy_binarytree(root->right); // 销毁右子树
    free(root);                      // 释放当前节点的内存空间
}

6.3 树与森林

双亲表示法

  • 用连续的空间存储树的结点,每个结点增设指示器指示双亲结点位置
#define MAX_TREE_SIZE 100 // 树的最大结点数
#define MAX_FOREST_SIZE 10 // 森林的最大树数

typedef struct {
    int data;
    int parent; // 双亲结点下标
} PTNode; // 双亲表示法结点类型

typedef struct {
    PTNode nodes[MAX_TREE_SIZE]; // 结点数组
    int r, n; // 根的位置和结点数
} PTree; // 树的类型

typedef struct {
    PTree trees[MAX_FOREST_SIZE]; // 树数组
    int n; // 树的个数
} PForest; // 森林的类型

孩子表示法

  • 与二叉链表类似,但孩子指针有多个
// 树结点的结构体
struct TreeNode {
    int value;
    struct TreeNode *child;   // 指向第一个孩子结点的指针
    struct TreeNode *sibling; // 指向下一个兄弟结点的指针
};

孩子兄弟表示法(树转换为二叉树)

  • 结点的结构同二叉链表,两个指针分别指向该结点的第一个孩子和下一个兄弟
struct TreeNode {
    int data;              // 节点存储的数据
    struct TreeNode *firstChild;  // 指向第一个孩子节点的指针
    struct TreeNode *nextSibling; // 指向右兄弟节点的指针
};

6.4 哈夫曼树

  • 给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近

在这里插入图片描述

定义哈夫曼树

// 定义结构体类型
typedef struct tree_node
{
    int weight;
    int parent;
    int left;
    int right;
} tree_node;

创建哈夫曼树

// 创建哈夫曼树
tree_node *create_huffmantree(int array[], int size)
{
    // 初始化哈夫曼树
    tree_node *node = (tree_node *)malloc(sizeof(tree_node) * (2 * size - 1));
    for (int i = 0; i < 2 * size - 1; i++)
    {
        node[i].weight = 0;
        node[i].parent = -1;
        node[i].left = -1;
        node[i].right = -1;
    }

    // 输入叶子结点的权值
    for (int i = 0; i < size; i++)
    {
        node[i].weight = array[i];
    }

    // 创建哈夫曼树
    for (int i = size; i < 2 * size - 1; i++)
    {
        int min1_index = -1;
        int min2_index = -1;
        select_min_index(node, i, &min1_index, &min2_index);
        node[min1_index].parent = i;
        node[min2_index].parent = i;
        node[i].left = min1_index;
        node[i].right = min2_index;
        node[i].weight = node[min1_index].weight + node[min2_index].weight;
    }

    // 返回哈夫曼树
    return node;
}

// 选择权值最小的两个结点
void select_min_index(tree_node *node, int size, int *min1_index, int *min2_index)
{
    int min1 = 10000;
    int min2 = 10000;
    // 找到权值最小的两个结点
    for (int i = 0; i < size; i++)
    {
        // 只在尚未构建二叉树的结点中查找
        if (node[i].parent == -1)
        {
            if (node[i].weight < min1)
            {
                min2 = min1;
                *min2_index = *min1_index;
                min1 = node[i].weight;
                *min1_index = i;
            }
            else if (node[i].weight < min2)
            {
                min2 = node[i].weight;
                *min2_index = i;
            }
        }
    }
}
  • 霍夫曼编码(英语:Huffman Coding),又译为哈夫曼编码、赫夫曼编码是一种用于无损数据压缩的熵编码(权编码)算法。由美国计算机科学家大卫·霍夫曼(David Albert Huffman)在1952年发明。

在这里插入图片描述

创建哈夫曼编码

// 创建哈夫曼编码
void create_huffmancode(tree_node *node, int size, char **code)
{
    // 为哈夫曼编码分配内存
    char *temp = (char *)malloc(sizeof(char) * size);
    temp[size - 1] = '\0';
    // 为每个叶子结点创建哈夫曼编码
    for (int i = 0; i < size; i++)
    {
        int current = i;
        int parent = node[current].parent;
        int index = size - 2;
        // 从叶子结点开始,向上回溯,直到根结点
        while (parent != -1)
        {
            // 如果当前结点是父结点的左孩子,则编码为0,否则为1
            if (node[parent].left == current)
            {
                temp[index] = '0';
            }
            else
            {
                temp[index] = '1';
            }
            // 继续向上回溯
            current = parent;
            parent = node[current].parent;
            index--;
        }
        // 为当前叶子结点的编码分配内存
        code[i] = (char *)malloc(sizeof(char) * (size - index));
        // 将编码复制到当前叶子结点的编码中
        for (int j = index + 1; j < size; j++)
        {
            code[i][j - index - 1] = temp[j];
        }
    }
    // 释放内存
    free(temp);
}

完整代码

二叉树

#include <stdio.h>
#include <stdlib.h>
#define MaxSize 10

// 定义结构体类型
typedef struct tree_node
{
    int data;
    struct tree_node *left;
    struct tree_node *right;
} tree_node;

// 创建结点
tree_node *create_tree_node(int data)
{
    tree_node *node = (tree_node *)malloc(sizeof(tree_node));
    node->data = data;
    node->left = NULL;
    node->right = NULL;
    return node;
}

// 创建二叉树
tree_node *create_binarytree(int *array, int start, int end)
{
    if (start > end)
    {
        return NULL;
    }
    int mid = (start + end) / 2;
    tree_node *node = create_tree_node(array[mid]);
    node->left = create_binarytree(array, start, mid - 1);
    node->right = create_binarytree(array, mid + 1, end);
    return node;
}

// 先序遍历
void pre_order(tree_node *root)
{
    if (root == NULL)
    {
        return;
    }
    printf("%d ", root->data);
    in_order(root->left);
    in_order(root->right);
}

// 中序遍历
void in_order(tree_node *root)
{
    if (root == NULL)
    {
        return;
    }
    in_order(root->left);
    printf("%d ", root->data);
    in_order(root->right);
}

// 后序遍历
void post_order(tree_node *root)
{
    if (root == NULL)
    {
        return;
    }
    in_order(root->left);
    in_order(root->right);
    printf("%d ", root->data);
}

// 非递归先序遍历
void order_unrec(tree_node *root)
{
    if (root == NULL)
    {
        return;
    }
    tree_node *stack[MaxSize];
    int top = -1;
    tree_node *p = root;
    while (p || top != -1)
    {
        while (p)
        {
            stack[++top] = p;
            p = p->left;
        }

        if (top != -1)
        {
            p = stack[top--];
            printf("%d ", p->data);
            p = p->right;
        }
    }
}

// 层序遍历
void level_order(tree_node *root)
{
    if (root == NULL)
    {
        return;
    }
    tree_node *queue[MaxSize];
    int front = 0, rear = 0;
    queue[rear++] = root;

    while (rear > front)
    {
        tree_node *p = queue[front++];
        printf("%d ", p->data);
        if (p->left)
        {
            queue[rear++] = p->left;
        }
        if (p->right)
        {
            queue[rear++] = p->right;
        }
    }
}

// 二叉树的复制
tree_node *copy_binarytree(tree_node *root)
{
    if (root == NULL)
    {
        return NULL;
    }
    tree_node *copy = create_tree_node(root->data);
    copy->left = copy_binarytree(root->left);
    copy->right = copy_binarytree(root->right);
    return copy;
}

// 计算二叉树的叶子节点个数
int count_leaves(tree_node *root)
{
    if (root == NULL)
    {
        return 0;
    }
    if (root->left == NULL && root->right == NULL)
    {
        return 1;
    }
    return count_leaves(root->left) + count_leaves(root->right);
}

// 计算二叉树的深度
int tree_depth(tree_node *root)
{
    if (root == NULL)
    {
        return 0;
    }
    int left_depth = tree_depth(root->left);
    int right_depth = tree_depth(root->right);
    return (left_depth > right_depth ? left_depth : right_depth) + 1;
}

// 计算二叉树的节点个数
int node_count(tree_node *root)
{
    if (root == NULL)
    {
        return 0;
    }
    return 1 + node_count(root->left) + node_count(root->right);
}

// 销毁二叉树
void destroy_binarytree(tree_node *root)
{
    if (root == NULL)
    {
        return;
    }
    destroy_binarytree(root->left);  // 销毁左子树
    destroy_binarytree(root->right); // 销毁右子树
    free(root);                      // 释放当前节点的内存空间
}

int main(void)
{
    int array[] = {1, 2, 3};
    int size = sizeof(array) / sizeof(int);
    tree_node *root = create_binarytree(array, 0, size - 1);
    // in_order(root);
    // printf("\n");
    // pre_order(root);
    // printf("\n");
    // post_order(root);

    // order_unrec(root);
    tree_node *new_root = copy_binarytree(root);
    level_order(new_root);
    // printf("%d", count_leaves(root));
    // printf("%d", tree_depth(root));
    printf("%d", node_count(root));
    destroy_binarytree(root);
    return 0;
}

哈夫曼树和哈夫曼编码

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

// 定义结构体类型
typedef struct tree_node
{
    int weight;
    int parent;
    int left;
    int right;
} tree_node;

// 创建哈夫曼树
tree_node *create_huffmantree(int array[], int size)
{
    // 初始化哈夫曼树
    tree_node *node = (tree_node *)malloc(sizeof(tree_node) * (2 * size - 1));
    for (int i = 0; i < 2 * size - 1; i++)
    {
        node[i].weight = 0;
        node[i].parent = -1;
        node[i].left = -1;
        node[i].right = -1;
    }

    // 输入叶子结点的权值
    for (int i = 0; i < size; i++)
    {
        node[i].weight = array[i];
    }

    // 创建哈夫曼树
    for (int i = size; i < 2 * size - 1; i++)
    {
        int min1_index = -1;
        int min2_index = -1;
        select_min_index(node, i, &min1_index, &min2_index);
        node[min1_index].parent = i;
        node[min2_index].parent = i;
        node[i].left = min1_index;
        node[i].right = min2_index;
        node[i].weight = node[min1_index].weight + node[min2_index].weight;
    }

    // 返回哈夫曼树
    return node;
}

// 选择权值最小的两个结点
void select_min_index(tree_node *node, int size, int *min1_index, int *min2_index)
{
    int min1 = 10000;
    int min2 = 10000;
    // 找到权值最小的两个结点
    for (int i = 0; i < size; i++)
    {
        // 只在尚未构建二叉树的结点中查找
        if (node[i].parent == -1)
        {
            if (node[i].weight < min1)
            {
                min2 = min1;
                *min2_index = *min1_index;
                min1 = node[i].weight;
                *min1_index = i;
            }
            else if (node[i].weight < min2)
            {
                min2 = node[i].weight;
                *min2_index = i;
            }
        }
    }
}

// 创建哈夫曼编码
void create_huffmancode(tree_node *node, int size, char **code)
{
    // 为哈夫曼编码分配内存
    char *temp = (char *)malloc(sizeof(char) * size);
    temp[size - 1] = '\0';
    // 为每个叶子结点创建哈夫曼编码
    for (int i = 0; i < size; i++)
    {
        int current = i;
        int parent = node[current].parent;
        int index = size - 2;
        // 从叶子结点开始,向上回溯,直到根结点
        while (parent != -1)
        {
            // 如果当前结点是父结点的左孩子,则编码为0,否则为1
            if (node[parent].left == current)
            {
                temp[index] = '0';
            }
            else
            {
                temp[index] = '1';
            }
            // 继续向上回溯
            current = parent;
            parent = node[current].parent;
            index--;
        }
        // 为当前叶子结点的编码分配内存
        code[i] = (char *)malloc(sizeof(char) * (size - index));
        // 将编码复制到当前叶子结点的编码中
        for (int j = index + 1; j < size; j++)
        {
            code[i][j - index - 1] = temp[j];
        }
    }
    // 释放内存
    free(temp);
}

int main(void)
{
    int array[] = {2, 8, 7, 6, 5, 4};
    int size = sizeof(array) / sizeof(array[0]);
    tree_node *node = create_huffmantree(array, size);
    for (int i = 0; i < 2 * size - 1; i++)
    {
        printf("weight: %d, parent: %d, left: %d, right: %d\n", node[i].weight, node[i].parent, node[i].left, node[i].right);
    }
    printf("\n");
    char **code = (char **)malloc(sizeof(char *) * size);
    create_huffmancode(node, size, code);
    for (int i = 0; i < size; i++)
    {
        printf("%d : %s\n", node[i].weight, code[i]);
    }
}

AVL树

  • AVL树(Adelson-Velsky and Landis Tree)是计算机科学中最早被发明的自平衡二叉查找树

在这里插入图片描述

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

// 定义节点
typedef struct Node
{
    int data;
    int height;
    struct Node *left;
    struct Node *right;
} Node;

// 求最大值
int max(int a, int b)
{
    return (a > b) ? a : b;
}

// 求高度
int height(Node *node)
{
    if (node == NULL)
    {
        return 0;
    }
    return node->height;
}

// 求平衡因子
int balanceFactor(Node *node)
{
    if (node == NULL)
    {
        return 0;
    }
    return height(node->left) - height(node->right);
}

// 创建节点
Node *newNode(int data)
{
    Node *node = (Node *)malloc(sizeof(Node));
    node->data = data;
    node->height = 1;
    node->left = NULL;
    node->right = NULL;
    return node;
}

// 旋转
Node *rotateRight(Node *y)
{

    Node *x = y->left;
    Node *T2 = x->right;

    x->right = y;
    y->left = T2;

    // 更新高度
    y->height = max(height(y->left), height(y->right)) + 1;
    x->height = max(height(x->left), height(x->right)) + 1;

    return x;
}

// 旋转
Node *rotateLeft(Node *x)
{
    Node *y = x->right;
    Node *T2 = y->left;

    y->left = x;
    x->right = T2;

    // 更新高度
    x->height = max(height(x->left), height(x->right)) + 1;
    y->height = max(height(y->left), height(y->right)) + 1;

    return y;
}

// 插入
Node *insert(Node *node, int data)
{
    // 1. 执行标准BST插入
    if (node == NULL)
    {
        return newNode(data);
    }
    if (data < node->data)
    {
        node->left = insert(node->left, data);
    }
    else if (data > node->data)
    {
        node->right = insert(node->right, data);
    }
    else
    {
        return node;
    }

    // 2. 更新高度
    node->height = 1 + max(height(node->left), height(node->right));
    int bf = balanceFactor(node);

    if (bf > 1 && data < node->left->data)
    {
        return rotateRight(node);
    }
    if (bf < -1 && data > node->right->data)
    {
        return rotateLeft(node);
    }
    if (bf > 1 && data > node->left->data)
    {
        node->left = rotateLeft(node->left);
        return rotateRight(node);
    }
    if (bf < -1 && data < node->right->data)
    {
        node->right = rotateRight(node->right);
        return rotateLeft(node);
    }

    return node;
}

// 中序遍历
void inOrder(Node *node)
{
    if (node != NULL)
    {
        inOrder(node->left);
        printf("%d ", node->data);
        inOrder(node->right);
    }
}

int main()
{
    Node *root = NULL;

    root = insert(root, 10);
    root = insert(root, 20);
    root = insert(root, 30);
    root = insert(root, 40);
    root = insert(root, 50);
    root = insert(root, 25);

    printf("Inorder traversal of the constructed AVL tree is: ");
    inOrder(root);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值