C++学习之树、二叉树

本文详细介绍了树的基本概念,包括树的度、层次和深度等专业术语,以及树的存储方式如双亲表示法和孩子表示法。接着深入讲解了二叉树的定义、性质和特殊类型,如满二叉树和完全二叉树。此外,还涵盖了二叉树的操作、遍历方法以及有序二叉树的应用。最后讨论了线索二叉树、选择树、堆等高级主题,并提供了相关代码示例,如二叉树的镜像转换、子树判断等。
摘要由CSDN通过智能技术生成

一、树形结构:
    1、树的基本概念:一种表示层次关系(一对多)的数据结构,有且仅有一个特定的节点,
        该节点没有前驱,称为根节点,剩余的n个不相交的子集,其中每个子集也都是一棵树
        称为根节点的子树
        注意:树形结构有递归性(树中有树)
    
    2、树的表示方式:倒悬树、嵌套法、凹凸法
    
    3、树的专业术语:
        1.节点:组成树的基本元素,同时它也是一棵树
        2.节点的度:该节点的子树数量
        3.树的度:树中节点的数量
        4.树的层次:从根节点层次为1出发计算
        5.树的深度:树的最大层次数
        6.叶子节点:节点的度为0的节点
        ***7.双亲节点和孩子节点:节点的子树称为该节点的孩子节点,该节点就是它们孩子节点的双亲节点
        8.兄弟节点:具有同一个双亲节点,互为兄弟节点
        9.堂兄弟节点:双亲节点在同一层次的节点互为堂兄弟
        10.祖先节点:从根节点出发到该节点,经过的所有节点都称为该节点的祖先节点
        11.子孙节点:一个节点的子树中任意一个节点都是它的子孙结点
        12.森林:n个不相交的树组成的集合称为森林

    4、树的存储:
        树可以顺序存储,也可以链式存储,还可以混合存储,由于存储的信息不同,有以下几种存储表示方式
            1.双亲表示法:
                顺序存储:
                    位置  data 双亲位置
                     0      A     -1
                     1      B      0
                     2      C      0
                     3      D      0
                     4      E      1
                     5      F      1
                     6      G      3
                     优点:方便找到双亲
                     缺点:不方便查找孩子节点

            2.孩子表示法:
                顺序存储:浪费空间
                    位置  data   存储孩子位置的数组sub_arr[n-1]
                     0     A             1、2、3 
                     1     B             4、5
                     2     C
                     3     D             6、7
                     4     E
                     5     F
                     6     G
                     7     H                8
                     8     I
                链式存储:节约空间
                    位置  data             listhead
                        0     A             1->2->3->N
                        1     B             4->5->N
                        2     C
                        3     D             6->7->N
                        4     E
                        5     F
                        6     G
                        7     H               8->N
                        8     I
                优点:方便找孩子
                缺点:不方便找双亲

            3.兄弟表示法:
                双亲只存储第一个子节点,然后链式指向所有的兄弟节点
                优点:方便查找到所有的兄弟节点、孩子节点
                缺点:不方便找双亲
    注意:普通树不常用,一般会把普通树转换成二叉树使用

二、二叉树
    是一种常用的数据结构,处理起来比较简单方便,并且普通树可以很方便的转换成二叉树
    普通树转二叉树:孩子变左节点,兄弟变右节点
    1、定义:节点的度最多为2
        二叉树是n个有限元素的集合,该集合或者为空。或者由一个称为根的元素及两个不相交
        、被分别称为左子树和右子树的二叉树组成,是有序树,当集合为空时,该二叉树为空二叉树
        在二叉树中,一个元素也称为一个节点

    2、二叉树的性质:
        性质1:二叉树的第i层上至多有2i-1(i≥1)个节点
        性质2:深度为h的二叉树中至多含有2h-1个节点
        性质3:若在任意一棵二叉树中,有n0个叶子节点,有n2个度为2的节点,则必有n0=n2+1
        性质4:具有n个节点的满二叉树深为log2n+1。

        性质5:若对一棵有n个节点的完全二叉树进行顺序编号(1≤i≤n),那么,对.于编号为i(i≥1)的节点:
            当i=1时,该节点为根,它无双亲节点
            当i>1时,该节点的双亲节点的编号为i/2
            若2i≤n,则有编号为2i的左节点,否则没有左节点
            若2i+1≤n,则有编号为2i+1的右节点,否则没有右节点 

    3、特殊的二叉树:
        1.满二叉树:二叉树每层节点数都满足2^(i-1)个
        2.完全二叉树:满二叉树最后一层从右开始少为完全二叉树

三、二叉树的常用操作:构建、销毁、遍历、高度、密度、添加、删除、查询、求左、求右、求根

四、二叉树的存储:
    顺序存储:必须按照完全二叉树的格式存储,空位置使用特殊数据代替
        数据项:存储节点的内存首地址、容量
    链式存储:由一个个节点组成,每个节点也都是一棵树
        节点数据项:
        数据项:
        左子树指针:
        右子树指针:

五、二叉树的遍历
    前序遍历:根、左、右
    中序遍历:左、根、右
    后序遍历:左、右、根
    注意:前中后由根节点的遍历次序决定,并且左右子树的次序不会改变
    注意:根据中序+前序 或者 中序+后序 可以还原一棵树,无法通过前序+后序还原
    层序遍历:从上到下、从左到右依次遍历,必须与队列配合

六、有序二叉树(二叉排序树、二叉查找树、二叉搜索树)
    左子树的数据小于根节点,根节点的数据小于等于右子树,并且左右子树都符合以上定义
    特殊:平衡二叉树

    注意:由于这种树的中序遍历是从小到大,所以有序二叉树也是一种排序算法,并且有序二叉树中查找某个值天然是二分查找
          所以经常考
    注意:由于有序二叉树需要频繁地插入删除,因此不适合使用链式存储

七、线索二叉树
    也是有序二叉树
    规律:在一棵有n个节点的链式二叉树中必定存在n+1个空指针
    因此链式二叉树中有很多的空指针,可以在有序二叉树中,让这些空指针指向下一个或前一个节点,这样有序遍历树时可以
    不使用递归而是使用循环进行,以此提高树的遍历效率

    中序线索二叉树
    节点数据项:
    左子树指针:
    右子树指针:
    右子树线索标志(假:右子树指针指向真的右子树;真:右子树指针指向下一个节点):

    实现过程:
        1、创建一棵有序二叉树
        2、通过中序遍历创建线索(只需一次)
        3、根据线索遍历(循环)

    简答题:线索二叉树就是为了提高树的遍历速度、效率

八、选择树
    是一种完全二叉树,把要存储的数据存放在最后一层,根节点是左右子树中的其中一个,可以是最大的(胜者树)也可以是最小的(败者树)
    功能:快速的找出其中最大或者最小值

九、堆
    是完全二叉树,不适合链式存储的树,不好找双亲
    大顶堆(大根堆):根节点比左右子树大
    小顶堆(小根堆):根节点比左右子树小

    数据项:
        存储数据的内存首地址:
        容量:
        数量:
    运算:创建、销毁、添加、删除、判断是否空堆、满堆、堆顶

作业:
    1、把一棵二叉树转换成镜像树
    typedef struct TreeNode 
    {
        int val;
        struct TreeNode *left;
        struct TreeNode *right;
    }TreeNode;

    TreeNode* mirror_tree(TreeNode* root) 
    {
        if (root == NULL) 
        {
            return NULL;
        }

        // 交换当前节点的左右子树
        TreeNode* temp = root->left;
        root->left = root->right;
        root->right = temp;

        // 递归地处理左子树和右子树
        mirror_tree(root->left);
        mirror_tree(root->right);

        return root;
    }

    int main() 
    {
        // 创建一个简单的二叉树进行测试
        TreeNode* root = (TreeNode*) malloc(sizeof(TreeNode));
        root->val = 1;

        TreeNode* left_child = (TreeNode*) malloc(sizeof(TreeNode));
        left_child->val = 2;
        left_child->left = NULL;
        left_child->right = NULL;

        TreeNode* right_child = (TreeNode*) malloc(sizeof(TreeNode));
        right_child->val = 3;
        right_child->left = NULL;
        right_child->right = NULL;

        root->left = left_child;
        root->right = right_child;

        // 转换为镜像树
        TreeNode* mirrored_root = mirror_tree(root);

        // 释放内存
        free(left_child);
        free(right_child);
        free(root);

        return 0;
    }

    
    2、输入两棵二叉树A、B,判断B是否是A的子结构,空树不是任何树的子结构
        typedef struct TreeNode {
        int val;
        struct TreeNode *left;
        struct TreeNode *right;
    } TreeNode;

    bool is_sub_tree(TreeNode* A, TreeNode* B) {
        if (B == NULL) {
            return true;
        }
        if (A == NULL) {
            return false;
        }
        if (A->val != B->val) {
            return false;
        }
        return is_sub_tree(A->left, B->left) && is_sub_tree(A->right, B->right);
    }

    bool has_sub_structure(TreeNode* A, TreeNode* B) {
        if (A == NULL || B == NULL) {
            return false;
        }

        return is_sub_tree(A, B) || has_sub_structure(A->left, B) || has_sub_structure(A->right, B);
    }

    int main() {
        // 创建两个简单的二叉树 A 和 B 进行测试
        TreeNode* A = (TreeNode*) malloc(sizeof(TreeNode));
        A->val = 1;

        TreeNode* A_left_child = (TreeNode*) malloc(sizeof(TreeNode));
        A_left_child->val = 2;
        A_left_child->left = NULL;
        A_left_child->right = NULL;

        TreeNode* A_right_child = (TreeNode*) malloc(sizeof(TreeNode));
        A_right_child->val = 3;
        A_right_child->left = NULL;
        A_right_child->right = NULL;

        A->left = A_left_child;
        A->right = A_right_child;

        TreeNode* B = (TreeNode*) malloc(sizeof(TreeNode));
        B->val = 2;
        B->left = NULL;
        B->right = NULL;

        bool result = has_sub_structure(A, B);
        printf("B is a substructure of A: %s\n", result ? "true" : "false");

        // 释放内存
        free(A_left_child);
        free(A_right_child);
        free(A);
        free(B);

        return 0;
    }

    3、把一个有序二叉树转换成有序的双向链表
        typedef struct TreeNode {
        int val;
        struct TreeNode *left;
        struct TreeNode *right;
    } TreeNode;

    TreeNode* prev = NULL;  // 全局变量,用于记录遍历过程中的前一个节点

    void inorder_traversal(TreeNode* node) {
        if (node == NULL) {
            return;
        }

        inorder_traversal(node->left);

        node->left = prev;
        if (prev != NULL) {
            prev->right = node;
        }
        prev = node;

        inorder_traversal(node->right);
    }

    TreeNode* convert_to_doubly_linked_list(TreeNode* root) {
        if (root == NULL) {
            return NULL;
        }

        prev = NULL;
        inorder_traversal(root);

        TreeNode* head = root;
        while (head->left != NULL) {
            head = head->left;
        }

        return head;
    }

    int main() {
        // 创建一个简单的二叉搜索树进行测试
        TreeNode* root = (TreeNode*) malloc(sizeof(TreeNode));
        root->val = 4;

        TreeNode* left_child = (TreeNode*) malloc(sizeof(TreeNode));
        left_child->val = 2;
        left_child->left = NULL;
        left_child->right = NULL;

        TreeNode* right_child = (TreeNode*) malloc(sizeof(TreeNode));
        right_child->val = 6;
        right_child->left = NULL;
        right_child->right = NULL;

        root->left = left_child;
        root->right = right_child;

        // 转换为双向链表
        TreeNode* head = convert_to_doubly_linked_list(root);

        // 打印双向链表
        TreeNode* temp = head;
        while (temp != NULL) {
            printf("%d ", temp->val);
            temp = temp->right;
        }
        printf("\n");

        // 释放内存
        free(left_child);
        free(right_child);
        free(root);

        return 0;
    }

    4、计算一棵有序二叉树的倒数第k大的数
        typedef struct TreeNode {
        int val;
        struct TreeNode *left;
        struct TreeNode *right;
    } TreeNode;

    int kth_largest_value = 0;
    int count = 0;

    void reverse_inorder_traversal(TreeNode* node, int k) {
        if (node == NULL || count >= k) {
            return;
        }

        reverse_inorder_traversal(node->right, k);

        count++;
        if (count == k) {
            kth_largest_value = node->val;
        }

        reverse_inorder_traversal(node->left, k);
    }

    int find_kth_largest(TreeNode* root, int k) {
        count = 0;
        reverse_inorder_traversal(root, k);
        return kth_largest_value;
    }

    int main() {
        // 创建一个简单的二叉搜索树进行测试
        TreeNode* root = (TreeNode*) malloc(sizeof(TreeNode));
        root->val = 4;

        TreeNode* left_child = (TreeNode*) malloc(sizeof(TreeNode));
        left_child->val = 2;
        left_child->left = NULL;
        left_child->right = NULL;

        TreeNode* right_child = (TreeNode*) malloc(sizeof(TreeNode));
        right_child->val = 6;
        right_child->left = NULL;
        right_child->right = NULL;

        root->left = left_child;
        root->right = right_child;

        // 查找倒数第 2 大的数
        int kth_largest = find_kth_largest(root, 2);
        printf("The 2nd largest value in the BST is: %d\n", kth_largest);

        // 释放内存
        free(left_child);
        free(right_child);
        free(root);

        return 0;
    }

        5、判断一个二叉树是否对称(镜像)
            typedef struct TreeNode {
            int val;
            struct TreeNode *left;
            struct TreeNode *right;
        } TreeNode;

        bool is_mirror(TreeNode* left_node, TreeNode* right_node) {
            if (left_node == NULL && right_node == NULL) {
                return true;
            }
            if (left_node == NULL || right_node == NULL) {
                return false;
            }
            if (left_node->val != right_node->val) {
                return false;
            }
            return is_mirror(left_node->left, right_node->right) && is_mirror(left_node->right, right_node->left);
        }

        bool is_symmetric(TreeNode* root) {
            if (root == NULL) {
                return true;
            }
            return is_mirror(root->left, root->right);
        }

        int main() {
            // 创建一个对称的二叉树进行测试
            TreeNode* root = (TreeNode*) malloc(sizeof(TreeNode));
            root->val = 1;

            TreeNode* left_child = (TreeNode*) malloc(sizeof(TreeNode));
            left_child->val = 2;
            left_child->left = NULL;
            left_child->right = NULL;

            TreeNode* right_child = (TreeNode*) malloc(sizeof(TreeNode));
            right_child->val = 2;
            right_child->left = NULL;
            right_child->right = NULL;

            root->left = left_child;
            root->right = right_child;

            bool symmetric = is_symmetric(root);
            printf("The binary tree is symmetric: %s\n", symmetric ? "true" : "false");

            // 释放内存
            free(left_child);
            free(right_child);
            free(root);

            return 0;
        }
    6、实现之字形遍历二叉树,第一层从左到右,第二层从右到左,第三层从左到右
        #include <stdio.h>
        #include <stdbool.h>
        #include <stdlib.h>

        typedef struct TreeNode {
            int val;
            struct TreeNode *left;
            struct TreeNode *right;
        } TreeNode;

        typedef struct StackNode {
            TreeNode *data;
            struct StackNode *next;
        } StackNode;

        void push(StackNode **stack, TreeNode *data) {
            StackNode *new_node = (StackNode *) malloc(sizeof(StackNode));
            new_node->data = data;
            new_node->next = *stack;
            *stack = new_node;
        }

        TreeNode *pop(StackNode **stack) {
            if (*stack == NULL) return NULL;
            TreeNode *data = (*stack)->data;
            StackNode *temp = *stack;
            *stack = (*stack)->next;
            free(temp);
            return data;
        }

        bool is_empty(StackNode *stack) {
            return stack == NULL;
        }

        void zigzag_traversal(TreeNode *root) {
            if (root == NULL) return;

            StackNode *stack1 = NULL;
            StackNode *stack2 = NULL;

            push(&stack1, root);

            while (!is_empty(stack1) || !is_empty(stack2)) {
                while (!is_empty(stack1)) {
                    TreeNode *node = pop(&stack1);
                    printf("%d ", node->val);
                    if (node->left) push(&stack2, node->left);
                    if (node->right) push(&stack2, node->right);
                }

                while (!is_empty(stack2)) {
                    TreeNode *node = pop(&stack2);
                    printf("%d ", node->val);
                    if (node->right) push(&stack1, node->right);
                    if (node->left) push(&stack1, node->left);
                }
            }
        }

        int main() {
            // 创建一个简单的二叉树进行测试
            TreeNode* root = (TreeNode*) malloc(sizeof(TreeNode));
            root->val = 1;

            TreeNode* left_child = (TreeNode*) malloc(sizeof(TreeNode));
            left_child->val = 2;
            left_child->left = NULL;
            left_child->right = NULL;

            TreeNode* right_child = (TreeNode*) malloc(sizeof(TreeNode));
            right_child->val = 3;
            right_child->left = NULL;
            right_child->right = NULL;

            root->left = left_child;
            root->right = right_child;

            printf("Zigzag traversal: ");
            zigzag_traversal(root);
            printf("\n");

            // 释放内存
            free(left_child);
            free(right_child);
            free(root);

            return 0;
        }


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值