【数据结构】树与二叉树(十三):递归复制二叉树(算法CopyTree)

文章目录

  • [5.2.1 二叉树](#5.2.1 二叉树)
    • 二叉树性质
      • [引理5.1:二叉树中层数为i的结点至多有 2 i 2^i 2i个,其中 i ≥ 0 i \geq 0 i≥0。](#引理5.1:二叉树中层数为i的结点至多有 2 i 2^i 2i个,其中 i ≥ 0 i \geq 0 i≥0。)
      • [引理5.2:高度为k的二叉树中至多有 2 k + 1 − 1 2^{k+1}-1 2k+1−1个结点,其中 k ≥ 0 k \geq 0 k≥0。](#引理5.2:高度为k的二叉树中至多有 2 k + 1 − 1 2^{k+1}-1 2k+1−1个结点,其中 k ≥ 0 k \geq 0 k≥0。)
      • [引理5.3:设T是由n个结点构成的二叉树,其中叶结点个数为 n 0 n_0 n0,度数为2的结点个数为 n 2 n_2 n2,则有 n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1。](#引理5.3:设T是由n个结点构成的二叉树,其中叶结点个数为 n 0 n_0 n0,度数为2的结点个数为 n 2 n_2 n2,则有 n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1。)
    • 满二叉树、完全二叉树定义、特点及相关证明
  • [5.2.2 二叉树顺序存储](#5.2.2 二叉树顺序存储)
  • [5.2.3 二叉树链接存储](#5.2.3 二叉树链接存储)
  • [5.2.4 二叉树的遍历](#5.2.4 二叉树的遍历)
    • [1-3 先序、中序、后序遍历递归实现及相关练习](#1-3 先序、中序、后序遍历递归实现及相关练习)
    • [4. 中序遍历非递归](#4. 中序遍历非递归)
    • [5. 后序遍历非递归](#5. 后序遍历非递归)
    • [6. 先序遍历非递归](#6. 先序遍历非递归)
    • [7. 层次遍历](#7. 层次遍历)
  • [5.2.5 二叉树的创建](#5.2.5 二叉树的创建)

5.2.1 二叉树

二叉树是一种常见的树状数据结构,它由结点的有限集合组成。一个二叉树要么是空集 ,被称为空二叉树 ,要么由一个根结点和两棵不相交的子树组成,分别称为左子树右子树 。每个结点最多有两个子结点,分别称为左子结点和右子结点。

二叉树性质

引理5.1:二叉树中层数为i的结点至多有 2 i 2^i 2i个,其中 i ≥ 0 i \geq 0 i≥0。

引理5.2:高度为k的二叉树中至多有 2 k + 1 − 1 2^{k+1}-1 2k+1−1个结点,其中 k ≥ 0 k \geq 0 k≥0。

引理5.3:设T是由n个结点构成的二叉树,其中叶结点个数为 n 0 n_0 n0,度数为2的结点个数为 n 2 n_2 n2,则有 n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1。

满二叉树、完全二叉树定义、特点及相关证明

5.2.2 二叉树顺序存储

二叉树的顺序存储是指将二叉树中所有结点按层次顺序存放在一块地址连续的存储空间中,详见:
【数据结构】树与二叉树(五):二叉树的顺序存储(初始化,插入结点,获取父节点、左右子节点等)

5.2.3 二叉树链接存储

二叉树的链接存储系指二叉树诸结点被随机存放在内存空间中,结点之间的关系用指针说明。在链式存储中,每个二叉树结点都包含三个域:数据域(Data)、左指针域(Left)和右指针域(Right),用于存储结点的信息和指向子结点的指针,详见:
【数据结构】树与二叉树(六):二叉树的链式存储

5.2.4 二叉树的遍历

  • 遍历(Traversal)是对二叉树中所有节点按照一定顺序进行访问的过程。
  • 通过遍历,可以访问树中的每个节点,并按照特定的顺序对它们进行处理。
  • 对二叉树的一次完整遍历,可给出树中结点的一种线性排序。
    • 在二叉树中,常用的遍历方式有三种:先序遍历中序遍历后序遍历
    • 这三种遍历方式都可以递归地进行,它们的区别在于节点的访问顺序。
      • 在实现遍历算法时,需要考虑递归终止条件和递归调用的顺序。
    • 还可以使用迭代的方式来实现遍历算法,使用栈或队列等数据结构来辅助实现。
  • 遍历是二叉树中基础而重要的操作,它为其他许多操作提供了基础,如搜索、插入、删除等。

1-3 先序、中序、后序遍历递归实现及相关练习

【数据结构】树与二叉树(七):二叉树的遍历(先序、中序、后序及其C语言实现)

4. 中序遍历非递归

【数据结构】树与二叉树(八):二叉树的中序遍历(非递归算法NIO)

5. 后序遍历非递归

【数据结构】树与二叉树(九):二叉树的后序遍历(非递归算法NPO)

6. 先序遍历非递归

【数据结构】树与二叉树(十):二叉树的先序遍历(非递归算法NPO)

7. 层次遍历

【数据结构】树与二叉树(十一):二叉树的层次遍历(算法LevelOrder)

5.2.5 二叉树的创建

  • 先序遍历
    • a b d e f g c
  • 中序遍历
    • d b f e g a c
  • 后序遍历
    • d f g e b c a
  • 层次遍历
    • a b c d e f g

先序创建

由二叉树的遍历,很容易想到用遍历方法去创建二叉树,我们考虑从先根遍历思想出发来构造二叉树。

方法:输入当前被创建结点的数据域的值,如果不空,申请空间用指针指向,然后对数据域进行赋值,再递归对该结点的左右指针域进行赋值,这就是先根创建过程。当输入为空,则算法返回一个空指针(即空树。递归出口)。
【数据结构】树与二叉树(十二):二叉树的递归创建(算法CBT)

复制二叉树

考虑用后根遍历思想递归复制二叉树的算法CopyTree

a. 算法CopyTree

b. 时间复杂度

设二叉树有n个结点,算法CopyTree中,每个结点都要进行1次复制,即复制操作要执行n次,每次复制都是常数级的操作,因此算法CopyTree的时间复杂度为O(n)。

c. 代码实现

c 复制代码
struct Node* CopyTree(struct Node* t) {
    if (t == NULL) {
        return NULL;
    }

    struct Node* p = createNode('\0');  // 创建新结点
    struct Node* newlptr = NULL;  // 初始化左指针
    struct Node* newrptr = NULL;  // 初始化右指针

    // 复制左子树
    if (t->left != NULL) {
        newlptr = CopyTree(t->left);
    }

    // 复制右子树
    if (t->right != NULL) {
        newrptr = CopyTree(t->right);
    }

    // 复制数据和指针
    p->data = t->data;
    p->left = newlptr;
    p->right = newrptr;

    return p;
}

代码整合

c 复制代码
#include <stdio.h>
#include <stdlib.h>

// 二叉树结点的定义
struct Node {
    char data;
    struct Node* left;
    struct Node* right;
};

// 创建新结点
struct Node* createNode(char data) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    if (newNode == NULL) {
        printf("Memory allocation failed!\n");
        exit(1);
    }
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

// 复制二叉树
struct Node* CopyTree(struct Node* t) {
    if (t == NULL) {
        return NULL;
    }

    struct Node* p = createNode('\0');  // 创建新结点
    struct Node* newlptr = NULL;  // 初始化左指针
    struct Node* newrptr = NULL;  // 初始化右指针

    // 复制左子树
    if (t->left != NULL) {
        newlptr = CopyTree(t->left);
    }

    // 复制右子树
    if (t->right != NULL) {
        newrptr = CopyTree(t->right);
    }

    // 复制数据和指针
    p->data = t->data;
    p->left = newlptr;
    p->right = newrptr;

    return p;
}

// 中序遍历二叉树
void inorderTraversal(struct Node* root) {
    if (root != NULL) {
        inorderTraversal(root->left);
        printf("%c ", root->data);
        inorderTraversal(root->right);
    }
}
struct Node* CBT(char data[], int* index, char tostop) {
    char ch = data[(*index)++];
    if (ch == tostop) {
        return NULL;
    } else {
        struct Node* t = createNode(ch);
        t->left = CBT(data, index, tostop);
        t->right = CBT(data, index, tostop);
        return t;
    }
}

int main() {
    // 创建一棵二叉树
    char tostop = '#';
    char input_data[] = {'a', 'b', 'd', '#', '#', 'e', 'f', '#', '#', 'g', '#', '#', 'c', '#', '#'};
    int index = 0;

    struct Node* original = CBT(input_data, &index, tostop);

    // 复制二叉树
    struct Node* copy = CopyTree(original);

    // 中序遍历并输出原始二叉树
    printf("Original Inorder Traversal: ");
    inorderTraversal(original);
    printf("\n");

    // 中序遍历并输出复制后的二叉树
    printf("  Copied Inorder Traversal: ");
    inorderTraversal(copy);
    printf("\n");

    return 0;
}
相关推荐
励志成为嵌入式工程师17 分钟前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉1 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer1 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq1 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
wheeldown1 小时前
【数据结构】选择排序
数据结构·算法·排序算法
hikktn2 小时前
如何在 Rust 中实现内存安全:与 C/C++ 的对比分析
c语言·安全·rust
观音山保我别报错2 小时前
C语言扫雷小游戏
c语言·开发语言·算法
TangKenny4 小时前
计算网络信号
java·算法·华为
景鹤4 小时前
【算法】递归+深搜:814.二叉树剪枝
算法
iiFrankie4 小时前
SCNU习题 总结与复习
算法