数据结构 树(Tree)

数据结构 树(Tree)

1. 树的简介

树(Tree)是一种重要的非线性数据结构,它以分层方式组织数据。树由一组以边连接的节点组成,其中一个节点被称为根节点,其他节点分层次地连接到根节点,形成了层级结构。

2. 树的基本概念

以下概念均以二叉树为主要对象进行解释、介绍和绘图。

2.1 根节点

树的顶层节点,没有父节点,根节点即为(Root)。

1709369905446.jpg

2.2 父节点

一个节点连接到它的直接上层节点,称为它的父节点(Parent)。

此时父节点是相对而谈的,比如图中parent节点是root的children节点,却是children的parent节点。

1709369980590.jpg

2.3 子节点

一个节点连接到它的直接下层节点,称为它的子节点(Children)。一个节点可以有零个或多个子节点。

此时父子节点是相对而谈的,比如图中parent节点是root的children节点,却是children节点的parent节点。

1709369980590.jpg

2.4 叶子节点

没有子节点的节点称为叶子节点或叶节点(Leaf)。

1709370107433.jpg

2.5 兄弟节点

具有相同父节点的节点互称为兄弟节点(Siblings)。

1709370278521.jpg

2.6 深度

从根节点到某个节点的唯一路径上的边数(Depth)。

此时根节点的深度为1,每向下一层,深度随即+1。

1709370521242.jpg

2.7 高度

树中任意节点的最大深度(Height)。

叶子节点的深度为0,从下往上逐层递增,以此类推。

1709370659600.jpg

2.9 子树

树中的任意一个节点和它的后代节点,以及与这些节点之间的连接,组成的树称为原树的子树(Subtree)。

1709371057390.jpg

3. 树的常见类型

3.1 二叉树

每个节点最多有两个子节点的树(Binary Tree)。

1709369905446.jpg

3.1.1 节点数量与边数量关系

对于一棵有 n 个节点的二叉树。
最多有 n-1 条边(有且只有一边子树)。最少有 ⌈n/2⌉ 条边(每层的节点都占满),其中 ⌈x⌉ 表示对 x 向上取整。

3.1.2. 节点数量与树的高度关系

对于一棵有 n 个节点的二叉树。
最小高度为 ⌈log₂(n+1)⌉ - 1(每层的节点都占满)。最大高度为 n-1(有且只有一边子树)。

3.2 二叉搜索树

一种特殊的二叉树 (Binary Search Tree),左子树的节点都小于根节点,右子树的节点都大于根节点。

二叉搜索树不一定是平衡二叉树,但是平衡二叉树一定是二叉搜索树。

1709371668026.jpg

3.3 平衡二叉树

一种高度平衡的二叉树(AVL),确保在插入或删除操作后,树的高度不会过高 (Balanced Binary Tree)。

二叉搜索树不一定是平衡二叉树,但是平衡二叉树一定是二叉搜索树。

1709372013784.jpg

3.4 完全二叉树

除了最后一层,每一层都是满的,并且最后一层的节点都尽量靠左排列 (Complete Binary Tree)。

⌈n/2⌉ 条边,且有 ⌈log₂(n+1)⌉ - 1高。

完全二叉树不一定是满二叉树,但是满二叉树一定是完全二叉树。

1709372151193.jpg

3.4.1 完全二叉树的性质

对于一棵有 n 个节点的完全二叉树:
若从上至下、从左至右编号节点,则节点 i左子节点2i右子节点2i + 1
若从上至下、从左至右编号节点,则节点 i父节点i/2(向下取整)。

1709372376900.jpg

3.5 满二叉树

每个节点要么是叶子节点,要么有两个子节点的二叉树 (Full Binary Tree)。

完全二叉树不一定是满二叉树,但是满二叉树一定是完全二叉树。

1709369905446.jpg

3.6 N叉树

每个节点最多有N个子节点的树 (N-ary Tree)。

如下图所示,是一个三叉树,即有N=3

1709372655924.jpg

3.7 哈夫曼树

是一种用于数据压缩的树形结构(Huffman Tree),它通过根据数据出现的频率构建一棵具有最小编码长度WPL的二叉树,从而实现对数据的高效压缩和解压缩。

越频繁的数据(权重越高)越靠近霍夫曼树的上层(更短的编码),越不频繁的数据(权重越低)更靠近霍夫曼树的底层(更长的编码)。

1709373972698.jpg

4. 树的存储

4.1 链式存储结构

每个节点使用指针来指向其子节点,这种方式灵活简单,适用于不确定树的大小或结构的情况。每个节点通常包含一个数据域和多个指针域(指向子节点的指针)。

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

// 定义树的节点结构
typedef struct TreeNode {
    int data;               // 节点数据
    struct TreeNode *left;  // 左子树指针
    struct TreeNode *right; // 右子树指针
} TreeNode;

// 创建新节点
TreeNode* createNode(int data) {
    TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
    if (newNode == NULL) {
        printf("内存分配失败\n");
        exit(1);
    }
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

// 打印树形结构
void printTree(TreeNode* root, int depth) {
    if (root == NULL) {
        return;
    }

    // 递归打印右子树
    printTree(root->right, depth + 1);

    // 打印当前节点
    for (int i = 0; i < depth; i++) {
        printf("    ");
    }
    printf("%d\n", root->data);

    // 递归打印左子树
    printTree(root->left, depth + 1);
}

int main() {
    // 创建根节点
    TreeNode* root = createNode(5);

    // 构建示例树结构
    root->left = createNode(3);
    root->right = createNode(8);
    root->left->left = createNode(1);
    root->left->right = createNode(4);
    root->right->left = createNode(7);
    root->right->right = createNode(9);

    // 打印树形结构
    printf("树的结构:\n");
    printTree(root, 0);

    return 0;
}

4.2 数组存储结构

将树的节点按照某种顺序(如层序遍历)依次存储在数组中,通过数组的下标关系来表示节点之间的父子关系。这种方式节省了指针的空间,但需要事先确定树的最大层数和每层节点的最大数量。

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

#define MAX_SIZE 100

// 定义树的节点结构
typedef struct TreeNode {
    int data;               // 节点数据
} TreeNode;

// 创建新节点
TreeNode* createNode(int data) {
    TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
    if (newNode == NULL) {
        printf("内存分配失败\n");
        exit(1);
    }
    newNode->data = data;
    return newNode;
}

// 初始化树的数组
void initTree(TreeNode* tree[], int size) {
    for (int i = 0; i < size; i++) {
        tree[i] = NULL;
    }
}

// 插入节点
void insert(TreeNode* tree[], int data, int index) {
    if (index >= MAX_SIZE) {
        printf("数组越界\n");
        return;
    }
    tree[index] = createNode(data);
}

// 层次遍历打印树的结构
void printTree(TreeNode* tree[], int size) {
    int i = 0;
    int level = 1;
    int nodesInLevel = 1;
    while (i < size) {
        printf("%d ", tree[i]->data);
        if (++i == nodesInLevel) {
            printf("\n");
            nodesInLevel = nodesInLevel * 2 + 1;
            level++;
        }
    }
    printf("\n");
}

int main() {
    // 初始化树的数组
    TreeNode* tree[MAX_SIZE];
    initTree(tree, MAX_SIZE);

    // 插入节点
    insert(tree, 5, 0);
    insert(tree, 3, 1);
    insert(tree, 8, 2);
    insert(tree, 1, 3);
    insert(tree, 4, 4);
    insert(tree, 7, 5);
    insert(tree, 9, 6);

    // 打印树的结构
    printf("树的结构:\n");
    printTree(tree, 7);

    return 0;
}

4.3 父节点指针存储结构

每个节点除了存储自身的数据外,还存储指向父节点的指针。这种方式常用于需要在树中向上移动的操作,如查找节点的父节点。

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

// 定义树的节点结构
typedef struct TreeNode {
    int data;               // 节点数据
    struct TreeNode *left;  // 左子树指针
    struct TreeNode *right; // 右子树指针
    struct TreeNode *parent; // 父节点指针
} TreeNode;

// 创建新节点
TreeNode* createNode(int data) {
    TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
    if (newNode == NULL) {
        printf("内存分配失败\n");
        exit(1);
    }
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    newNode->parent = NULL;
    return newNode;
}

// 插入节点
void insert(TreeNode** root, int data, TreeNode* parent) {
    if (*root == NULL) {
        *root = createNode(data);
        (*root)->parent = parent;
    } else {
        if (data < (*root)->data) {
            insert(&((*root)->left), data, *root);
        } else {
            insert(&((*root)->right), data, *root);
        }
    }
}

// 打印树形结构
void printTree(TreeNode* root, int level) {
    if (root == NULL) {
        return;
    }
    printTree(root->right, level + 1);
    for (int i = 0; i < level; i++) {
        printf("   ");
    }
    printf("%d\n", root->data);
    printTree(root->left, level + 1);
}

int main() {
    // 创建根节点
    TreeNode* root = NULL;

    // 插入节点
    insert(&root, 5, NULL);
    insert(&root, 3, root);
    insert(&root, 8, root);
    insert(&root, 1, root->left);
    insert(&root, 4, root->left);
    insert(&root, 7, root->right);
    insert(&root, 9, root->right);

    // 打印树形结构
    printf("树的结构:\n");
    printTree(root, 0);

    return 0;
}

4.4 孩子兄弟表示法

也称为二叉树的二叉链表存储结构,每个节点包含两个指针,分别指向其第一个子节点和其兄弟节点。这种方式常用于表示多叉树,如二叉树的拓展形式。

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

// 定义树的节点结构
typedef struct TreeNode {
    int data;               // 节点数据
    struct TreeNode *firstChild;  // 第一个孩子节点指针
    struct TreeNode *nextSibling; // 兄弟节点指针
} TreeNode;

// 创建新节点
TreeNode* createNode(int data) {
    TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
    if (newNode == NULL) {
        printf("内存分配失败\n");
        exit(1);
    }
    newNode->data = data;
    newNode->firstChild = NULL;
    newNode->nextSibling = NULL;
    return newNode;
}

// 打印树形结构
void printTree(TreeNode* root, int level) {
    if (root == NULL) {
        return;
    }

    // 打印当前节点
    printf("%d ", root->data);

    // 递归打印孩子节点
    if (root->firstChild != NULL) {
        printTree(root->firstChild, level + 1);
    }

    // 递归打印兄弟节点
    if (root->nextSibling != NULL) {
        printf("\n"); // 换行
        for (int i = 0; i < level; i++) {
            printf("  "); // 打印缩进
        }
        printTree(root->nextSibling, level);
    }
}

int main() {
    // 创建根节点
    TreeNode* root = createNode(5);

    // 构建示例树结构
    root->firstChild = createNode(3);
    root->firstChild->nextSibling = createNode(8);
    root->firstChild->firstChild = createNode(1);
    root->firstChild->firstChild->nextSibling = createNode(4);
    root->firstChild->nextSibling->firstChild = createNode(7);
    root->firstChild->nextSibling->firstChild->nextSibling = createNode(9);

    // 打印树形结构
    printf("树的结构:\n");
    printTree(root, 0);
    printf("\n"); // 添加额外的换行

    return 0;
}

  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python中的数据结构可以通过使用类和对象来实现。在Python中,我们可以使用节点类来表示的节点,并使用类来操作和管理结构。 首先,我们可以创建一个节点类,其中包含节点的值和左右子节点的引用。节点的值可以是任意类型的数据。然后,我们可以创建一个类,其中包含根节点的引用和一些方法来操作。 下面是一个示例的Python代码来实现数据结构: ``` class TreeNode: def __init__(self, value): self.value = value self.left = None self.right = None class Tree: def __init__(self): self.root = None def insert(self, value): if self.root is None: self.root = TreeNode(value) else: self._insert_recursive(self.root, value) def _insert_recursive(self, node, value): if value < node.value: if node.left is None: node.left = TreeNode(value) else: self._insert_recursive(node.left, value) else: if node.right is None: node.right = TreeNode(value) else: self._insert_recursive(node.right, value) def search(self, value): return self._search_recursive(self.root, value) def _search_recursive(self, node, value): if node is None or node.value == value: return node if value < node.value: return self._search_recursive(node.left, value) else: return self._search_recursive(node.right, value) def inorder_traversal(self): nodes = [] self._inorder_traversal_recursive(self.root, nodes) return nodes def _inorder_traversal_recursive(self, node, nodes): if node is not None: self._inorder_traversal_recursive(node.left, nodes) nodes.append(node.value) self._inorder_traversal_recursive(node.right, nodes) ``` 在上面的代码中,我们创建了一个`TreeNode`类来表示的节点,每个节点有一个值和左右子节点的引用。然后,我们创建了一个`Tree`类来操作和管理结构。`Tree`类包括了插入节点、搜索节点和中序遍历的方法。 使用这些类,我们可以轻松地创建一个二叉搜索,并对其进行插入、搜索和中序遍历等操作。 例如,我们可以创建一个对象并插入一些节点: ``` tree = Tree() tree.insert(5) tree.insert(3) tree.insert(7) tree.insert(2) tree.insert(4) ``` 然后,我们可以搜索特定的值: ``` node = tree.search(3) if node is not None: print("Found:", node.value) else: print("Not found") ``` 最后,我们可以使用中序遍历方法打印的节点值: ``` nodes = tree.inorder_traversal() print("Inorder traversal:", nodes) ``` 这是一个简单的例子来演示如何在Python中实现数据结构。通过使用节点类和类,我们可以方便地创建和操作结构

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值