拿捏数据结构- 链式二叉树

链式二叉树的概念:

链式二叉树解决的是非完全二叉树解决不了的问题

什么意思呢,简单的说就是,链式二叉树

可以是下面三种二叉树

但是非链式二叉树只能是前两种

链式二叉树的存储

节点结构:首先定义一个结构体或类来表示二叉树的节点。每个节点通常包含三个部分:

  • 存储数据的成员变量(例如,_data)。
  • 指向其左子节点的指针(例如,_left)。
  • 指向其右子节点的指针(例如,_right)。

链式二叉树的存储方式提供了很高的灵活性,可以轻松地添加和删除节点,同时也使得树结构的实现更加直观和易于操作。

前序/中序/后序遍历

讲解链式二叉树之前我们需要先了解一下,前序/中序/后序,因为在初阶数据结构里,链式二叉树我们是用递归的方式来实现的,非递归的方式,在后续篇章会进行讲解。

这里我们上图,假设是这一张图

前序遍历

前序遍历的顺序是:首先访问根节点,然后递归地进行左子树的前序遍历,最后递归地进行右子树的前序遍历。

遍历顺序:根-左-右

步骤

  1. 访问当前节点。
  2. 遍历左子树。
  3. 遍历右子树。

示例: 假设有如下的二叉树:

关于前序遍历,我们需要把空节点也看出来

如图

所以根据遍历顺便,我们应该是

1  2  3 NULL NULL NULL 4 5 NULL NULL 6 NULL NULL

中序遍历

中序遍历的顺序是:首先递归地进行左子树的中序遍历,然后访问根节点,最后递归地进行右子树的中序遍历。

遍历顺序:左-根-右

步骤

  1. 遍历左子树。
  2. 访问当前节点。
  3. 遍历右子树。

正确的是

后序遍历

后序遍历的顺序是:首先递归地进行左子树的后序遍历,然后递归地进行右子树的后序遍历,最后访问根节点。

遍历顺序:左-右-根

步骤

  1. 遍历左子树。
  2. 遍历右子树。
  3. 访问当前节点。

正确的是

总结

链式二叉树的实现

创建文件

定义链式二叉树结构

typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType _data;struct BinaryTreeNode* _left;struct BinaryTreeNode* _right;
}BTNode;

解释:

  1. typedef int BTDataType; 这行代码使用 typedef 关键字定义了一个新的别名 BTDataType,它是 int 类型的别名。这意味着在代码中,你可以使用 BTDataType 作为 int 类型数据的一个更有意义的别名。

  2. typedef struct BinaryTreeNode 这行代码开始定义一个名为 BinaryTreeNode 的新结构体类型。struct BinaryTreeNode 是结构体的名称,它将用于表示二叉树中的节点。

  3. BTDataType _data; struct BinaryTreeNode* _left; struct BinaryTreeNode* _right; } 这个大括号内的代码定义了 BinaryTreeNode 结构体的具体内容:

    • BTDataType _data; 定义了一个名为 _data 的成员变量,它用于存储节点中的数据。由于使用了之前定义的 BTDataType,所以这个成员变量是 int 类型的。
    • struct BinaryTreeNode* _left; 定义了一个名为 _left 的成员变量,它是一个指向 BinaryTreeNode 类型的指针,用于指向当前节点的左子节点。
    • struct BinaryTreeNode* _right; 定义了一个名为 _right 的成员变量,它也是一个指向 BinaryTreeNode 类型的指针,用于指向当前节点的右子节点。
  4. BTNode; 这行代码为 BinaryTreeNode 结构体定义了一个易记的别名 BTNode。这样,在代码中就可以使用 BTNode 来声明二叉树的节点。

总结来说,这段代码定义了一个用于表示链式二叉树节点的结构体 BTNode,其中包含一个整型数据 _data 和两个指向相同结构体类型的指针 _left_right,分别用于存储节点的数据和链接到左右子节点。通过这种定义,可以方便地创建和管理二叉树数据结构。

二叉树的初始化

//二叉树的初始化
BTNode* BuyNode(BTDataType x)
{BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));if (newnode == NULL){perror("BinaryTreeInit:newnode:");exit(1);}newnode->_data = x;newnode->_left = NULL;newnode->_right = NULL;return newnode;
}

解释:

  1. BTNode* BuyNode(BTDataType x)

    • 这是函数的声明行,定义了一个名为 BuyNode 的函数,它接收一个类型为 BTDataType(之前定义的 int 类型别名)的参数 x,并返回一个指向 BTNode 类型的指针。
  2. BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));

    • 这行代码使用 malloc 函数为新的二叉树节点分配内存。sizeof(BTNode) 计算 BTNode 类型的大小,确保分配足够的内存。分配成功后,newnode 指针将指向这块新分配的内存。
  3. if (newnode == NULL)

    • 这行代码检查 malloc 是否成功分配了内存。如果 newnode 为 NULL,表示内存分配失败。
  4. perror("BinaryTreeInit:newnode:");

    • 如果内存分配失败,使用 perror 函数输出错误信息到标准错误。"BinaryTreeInit:newnode:" 是自定义的错误前缀,后跟系统定义的错误信息。
  5. exit(1);

    • 紧接着,使用 exit 函数以状态码 1 退出程序。状态码 1 通常表示程序因错误而终止。
  6. newnode->_data = x;

    • 如果内存分配成功,这行代码将参数 x 的值赋给新节点的 _data 成员变量,从而初始化节点存储的数据。
  7. newnode->_left = NULL;

    • 这行代码将新节点的 _left 指针设置为 NULL,表示当前没有左子节点。
  8. newnode->_right = NULL;

    • 类似地,这行代码将新节点的 _right 指针设置为 NULL,表示当前没有右子节点。
  9. return newnode;

    • 最后,函数返回指向新创建并初始化的 BTNode 节点的指针。

总结来说,BuyNode 函数负责创建一个新的二叉树节点,初始化其数据和子节点指针,如果内存分配失败,则输出错误信息并终止程序。这个函数是构建二叉树时用于生成节点的基本工具。

这里的关键点是认识到 ,左右节点的存在

二叉树的销毁

这里就采取了递归,开始上难度了,这里我先不做讲解,模拟创建二叉树之后我们进行讲解递归

// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{if (root == NULL){return;}BinaryTreeDestory(root->_left);BinaryTreeDestory(root->_right);free(root);
}

解释:

  1. void BinaryTreeDestory(BTNode* root)

    • 这是函数的声明行,定义了一个名为 BinaryTreeDestory 的函数,它接收一个指向 BTNode 类型的指针 root 作为参数。该函数没有返回值(void 类型)。
  2. if (root == NULL)

    • 这行代码检查传入的 root 指针是否为 NULL。如果是 NULL,表示二叉树为空或已经被销毁,因此函数直接返回。
  3. BinaryTreeDestory(root->_left);

    • 如果 root 不是 NULL,这行代码首先递归调用 BinaryTreeDestory 函数,传入当前节点的左子节点 root->_left 作为参数。这样做会先销毁左子树。
  4. BinaryTreeDestory(root->_right);

    • 接着,这行代码递归调用 BinaryTreeDestory 函数,传入当前节点的右子节点 root->_right 作为参数。这会销毁右子树。
  5. free(root);

    • 在左子树和右子树都被销毁之后,这行代码使用 free 函数释放当前节点 root 所占用的内存。

总结来说,BinaryTreeDestory 函数通过递归的方式,先销毁二叉树的左子树和右子树,然后释放根节点的内存。这种销毁方式确保了二叉树中的所有节点都会被释放,避免了内存泄漏。需要注意的是,在使用这种销毁方式时,确保在销毁二叉树后不再使用任何指向该树的指针,因为整个树的内存已经被释放。

模拟简易链式二叉树

//构建二叉树
void teer()
{BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);//BTNode* node7 = BuyNode(7);node1->_left = node2;node2->_left = node3;node1->_right = node4;node4->_left = node5;node4->_right = node6;//node2->_right = node7;}
int main()
{//构建二叉树teer();return 0;
}

这里我们模拟实现一个二叉树

就是这样

前序遍历实现

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}printf("%d ", root->_data);BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);
}

解释:

  1. void BinaryTreePrevOrder(BTNode* root)

    • 这是函数的声明行,定义了一个名为 BinaryTreePrevOrder 的函数,它接收一个指向 BTNode 类型的指针 root 作为参数。该函数没有返回值(void 类型)。
  2. if (root == NULL)

    • 这行代码检查传入的 root 指针是否为 NULL。如果是 NULL,表示当前节点为空,不需要遍历。
  3. printf("NULL ");

    • 如果当前节点为空,打印字符串 "NULL " 到标准输出。这是一种表示空节点的常见做法,但在实际应用中,通常会省略这一步,或者用其他方式处理空节点。
  4. printf("%d ", root->_data);

    • 如果当前节点不为空,这行代码会打印当前节点的 _data 成员变量的值。%d 是格式化输出的占位符,表示后面跟着的参数是一个整数。
  5. BinaryTreePrevOrder(root->_left);

    • 这行代码递归调用 BinaryTreePrevOrder 函数,传入当前节点的左子节点 root->_left 作为参数。这将执行左子树的前序遍历。
  6. BinaryTreePrevOrder(root->_right);

    • 最后,这行代码递归调用 BinaryTreePrevOrder 函数,传入当前节点的右子节点 root->_right 作为参数。这将执行右子树的前序遍历。

总结来说,BinaryTreePrevOrder 函数通过递归的方式实现了二叉树的前序遍历。它首先打印当前节点的值,然后递归地遍历左子树,最后递归地遍历右子树。这种遍历方式在树的遍历、搜索、复制等操作中非常有用。

图解:

  

中序遍历实现

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}BinaryTreeInOrder(root->_left);printf("%d ", root->_data);BinaryTreeInOrder(root->_right);
}

解释:

  1. void BinaryTreeInOrder(BTNode* root)

    • 这是函数的声明行,定义了一个名为 BinaryTreeInOrder 的函数,它接收一个指向 BTNode 类型的指针 root 作为参数。该函数没有返回值(void 类型)。
  2. if (root == NULL)

    • 这行代码检查传入的 root 指针是否为 NULL。如果是 NULL,表示当前节点为空,不需要遍历。
  3. printf("NULL ");

    • 如果当前节点为空,打印字符串 "NULL " 到标准输出。这通常用于表示空节点,但在实际的中序遍历中,通常会忽略空节点而不是打印它们。
  4. BinaryTreeInOrder(root->_left);

    • 这行代码递归调用 BinaryTreeInOrder 函数,传入当前节点的左子节点 root->_left 作为参数。这将执行左子树的中序遍历。
  5. printf("%d ", root->_data);

    • 在左子树遍历之后,这行代码打印当前节点的 _data 成员变量的值。因为在左子树遍历之后访问根节点,所以对于二叉搜索树来说,这会保证按升序打印节点值。
  6. BinaryTreeInOrder(root->_right);

    • 最后,这行代码递归调用 BinaryTreeInOrder 函数,传入当前节点的右子节点 root->_right 作为参数。这将执行右子树的中序遍历。

后序遍历实现

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}BinaryTreePostOrder(root->_left);BinaryTreePostOrder(root->_right);printf("%d ", root->_data);
}

解释:

  1. void BinaryTreePostOrder(BTNode* root)

    • 这是函数的声明行,定义了一个名为 BinaryTreePostOrder 的函数,它接收一个指向 BTNode 类型的指针 root 作为参数。该函数没有返回值(void 类型)。
  2. if (root == NULL)

    • 这行代码检查传入的 root 指针是否为 NULL。如果是 NULL,表示当前节点为空,不需要遍历。
  3. printf("NULL ");

    • 如果当前节点为空,打印字符串 "NULL " 到标准输出。这通常用于表示空节点,但在实际的后序遍历中,通常会忽略空节点而不是打印它们。
  4. BinaryTreePostOrder(root->_left);

    • 这行代码递归调用 BinaryTreePostOrder 函数,传入当前节点的左子节点 root->_left 作为参数。这将执行左子树的后序遍历。
  5. BinaryTreePostOrder(root->_right);

    • 在左子树遍历之后,这行代码递归调用 BinaryTreePostOrder 函数,传入当前节点的右子节点 root->_right 作为参数。这将执行右子树的后序遍历。
  6. printf("%d ", root->_data);

    • 在左子树和右子树都遍历之后,这行代码打印当前节点的 _data 成员变量的值。因为这是在访问完左右子树之后的操作,所以它是后序遍历的一部分。

二叉树节点个数

什么是节点个数,也就是,全部节点个数

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL){return 0;}return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}

解释:

  1. int BinaryTreeSize(BTNode* root)

    • 这是函数的声明行,定义了一个名为 BinaryTreeSize 的函数,它接收一个指向 BTNode 类型的指针 root 作为参数。函数返回一个整数值,表示树中的节点总数。
  2. if (root == NULL)

    • 这行代码检查传入的 root 指针是否为 NULL。如果是 NULL,表示当前节点为空,不计入节点总数。
  3. return 0;

    • 如果当前节点为空,函数返回 0。这是递归的基本情况,表示空树的节点数为 0
  4. return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;

    • 如果当前节点不为空,这行代码递归调用自身两次:一次计算左子树的节点数 BinaryTreeSize(root->_left),一次计算右子树的节点数 BinaryTreeSize(root->_right)。然后将这两个调用的结果相加,并加上 1 来表示当前节点本身。这样,你就得到了整棵树的节点总数。

图解

二叉树叶子节点个数

什么是叶子结点个数

也就是:

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{if (root == NULL)return 0;if (BinaryTreeLeafSize(root->_left) == 0 && BinaryTreeLeafSize(root->_right) == 0)return 1;return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}

解释:

  1. int BinaryTreeLeafSize(BTNode* root)

    • 这是函数的声明行,定义了一个名为 BinaryTreeLeafSize 的函数,接收一个指向 BTNode 类型的指针 root 作为参数。函数返回一个整数值,表示树中叶子节点的总数。
  2. if (root == NULL)

    • 这行代码检查传入的 root 指针是否为 NULL。如果是 NULL,表示当前节点为空,不计入叶子节点的总数。
  3. return 0;

    • 如果当前节点为空,函数返回 0。这是递归的基本情况之一,表示空树的叶子节点数为 0
  4. if (BinaryTreeLeafSize(root->_left) == 0 && BinaryTreeLeafSize(root->_right) == 0)

    • 这行代码是一个条件语句,用于检查当前节点的左子节点和右子节点是否都为空。如果两者都为空,那么当前节点就是一个叶子节点。
  5. return 1;

    • 如果当前节点是叶子节点,即它的左右子节点都为空,那么返回 1,表示叶子节点数为 1
  6. return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);

    • 如果当前节点不是叶子节点,这行代码递归调用自身两次:一次计算左子树的叶子节点数 BinaryTreeLeafSize(root->_left),一次计算右子树的叶子节点数 BinaryTreeLeafSize(root->_right)。然后将这两个调用的结果相加,得到当前子树的叶子节点总数。

总结来说,BinaryTreeLeafSize 函数通过递归的方式遍历二叉树,计算叶子节点的个数。它首先检查当前节点是否为空,如果不是空,再检查当前节点是否为叶子节点(左右子节点都为空),如果是叶子节点则计数 1,否则递归地计算左右子树的叶子节点数并相加。这种方法可以正确地计算出任意二叉树的叶子节点总数。

图解

二叉树高度

高度也就是二叉树的层数

// 二叉树高度
int BinaryTreeHeight(BTNode* root)
{ if (root == NULL){return 0;}int heightleft = BinaryTreeHeight(root->_left);int heightright = BinaryTreeHeight(root->_right);return heightleft >= heightright ? heightleft + 1 : heightright + 1;
}

这里的关键是理解如何递归地计算左子树和右子树的高度。

  1. int heightleft = BinaryTreeHeight(root->_left);

    • 这行代码是对 BinaryTreeHeight 函数的递归调用,目的是计算当前节点的左子树的高度。root->_left 是对当前节点左子节点的引用,如果左子节点存在,就对它调用 BinaryTreeHeight 函数,否则返回 0(如果左子节点为空)。
  2. int heightright = BinaryTreeHeight(root->_right);

    • 类似于上面的调用,这行代码计算当前节点的右子树的高度。root->_right 是对当前节点右子节点的引用,同样地,如果右子节点存在,就对它调用 BinaryTreeHeight 函数,否则返回 0(如果右子节点为空)。

这里的关键点在于    

int heightleft = BinaryTreeHeight(root->_left);
int heightright = BinaryTreeHeight(root->_right);

这两行代码是递归过程中的关键步骤,它们分别独立地计算左子树和右子树的高度。由于树的高度是递归定义的,即树的高度是其左子树和右子树高度的最大值加 1(当前节点),所以需要分别计算左右子树的高度。

一旦得到 heightleftheightright,函数就会决定:

  • 如果 heightleft 大于或等于 heightright,则当前节点的左子树高度是决定整棵树高度的关键,因此返回 heightleft + 1
  • 如果 heightright 大于 heightleft,则右子树的高度决定了整棵树的高度,因此返回 heightright + 1

这种递归方法确保了能够找到从根节点到任意叶子节点的最长路径,从而得到二叉树的准确高度。

否则会产生栈溢出的现象

下面有一题题解,我们会进行图解,因为一样,所以这里不进行图解,

先上图

二叉树查找值为x的节点

这里还是有点难度的

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL)return NULL;if (root->_data == x)return root;BTNode* ret1 = BinaryTreeFind(root->_left, x);if (ret1 != NULL){return ret1;}BTNode* ret2 = BinaryTreeFind(root->_right, x);if (ret2 != NULL){return ret2;}return NULL;}

解释:

  1. BTNode* BinaryTreeFind(BTNode* root, BTDataType x)

    • 这是函数的声明行,定义了一个名为 BinaryTreeFind 的函数。它接收两个参数:一个指向 BTNode 类型的指针 root,表示二叉树的根节点;一个 BTDataType 类型的值 x,表示要查找的值。函数返回一个指向 BTNode 类型的指针,如果找到匹配的节点,则返回该节点的地址;如果没有找到,则返回 NULL
  2. if (root == NULL)

    • 这行代码检查传入的 root 指针是否为 NULL。如果是 NULL,表示当前节点为空,无法找到匹配的值,因此返回 NULL
  3. if (root->_data == x)

    • 如果当前节点不为空,这行代码比较当前节点的 _data 成员变量与给定值 x 是否相等。
  4. return root;

    • 如果找到匹配的值,即当前节点的 _data 等于 x,则返回当前节点的指针。
  5. BTNode* ret1 = BinaryTreeFind(root->_left, x);

    • 如果当前节点的值不匹配,这行代码递归调用 BinaryTreeFind 函数,搜索左子树。递归调用的结果(一个节点指针)被存储在 ret1 变量中。
  6. if (ret1 != NULL)

    • 接着,检查 ret1 是否不为空。如果不为空,表示在左子树中找到了匹配的节点,因此返回 ret1
  7. BTNode* ret2 = BinaryTreeFind(root->_right, x);

    • 如果左子树中没有找到匹配的节点,这行代码递归调用 BinaryTreeFind 函数,搜索右子树。递归调用的结果(一个节点指针)被存储在 ret2 变量中。
  8. if (ret2 != NULL)

    • 然后,检查 ret2 是否不为空。如果不为空,表示在右子树中找到了匹配的节点,因此返回 ret2
  9. return NULL;

    • 如果在当前节点的左子树和右子树中都没有找到匹配的节点,则返回 NULL

图解

二叉树第k层节点个数

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL)return 0;if (k == 1)return 1;return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}

解释:

  1. int BinaryTreeLevelKSize(BTNode* root, int k)

    • 这是函数的声明行,定义了一个名为 BinaryTreeLevelKSize 的函数。它接收两个参数:一个指向 BTNode 类型的指针 root,表示当前的节点(可以是任意层的节点,包括根节点);一个整型变量 k,表示要查找的目标层级。函数返回一个整数值,表示在第 k 层上的节点总数。
  2. if (root == NULL)

    • 这行代码检查传入的 root 指针是否为 NULL。如果是 NULL,表示当前节点为空,无法计算节点个数,因此返回 0
  3. if (k == 1)

    • 这行代码检查目标层级 k 是否为 1。如果是 1,表示当前节点位于第 1 层,因此返回 1,表示第 1 层有一个节点。
  4. return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);

    • 如果当前节点不为空且目标层级不是第 1 层,这行代码递归地调用自身两次,分别计算左子树和右子树在第 k-1 层的节点个数。然后,将这两个调用的结果相加,得到第 k 层的节点总数。

总结来说,BinaryTreeLevelKSize 函数通过递归的方式遍历二叉树,计算并返回第 k 层上的节点个数。它首先检查当前节点是否为空,如果不为空且目标层级为第 1 层,则返回 1。如果不是第 1 层,则递归地计算左子树和右子树在降低一层后的节点个数,并将它们相加得到结果。这种方法可以正确地计算出任意二叉树在指定层级的节点总数。

图解

层序遍历

什么是层序遍历

也就是:顾名思义,一层一层的输出

这里的关键点在于,怎么样一层一层进入,输出

那么此时我们可以采取队列的形式来进行使用,同时进队列,同时出队列

所以,这里我们是直接调用队列

队列的讲解-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/Jason_from_China/article/details/138730053

#include"Queue.h"
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{// 按照顺序,放入队列里面,先进先出,后进后出的原则// 1,创建队列,初始化// 2,如果不为空,根节点入队列// 3,取栈顶,打印,出队列Queue ps;QueueInit(&ps);if (root)QueuePush(&ps, root);while (!QueueEmpty(&ps)){BTNode* ret = QueueFront(&ps);QueuePop(&ps);printf("%d ", ret->_data);if (ret->_left)QueuePush(&ps, ret->_left);if (ret->_right)QueuePush(&ps, ret->_right);}//队列的销毁QueueDestroy(&ps);
}

解释:

  1. #include"Queue.h"

    • 这行代码包含了队列数据结构的头文件,该文件中定义了队列操作的相关函数和数据结构。
  2. void BinaryTreeLevelOrder(BTNode* root)

    • 这是函数的声明行,定义了一个名为 BinaryTreeLevelOrder 的函数,它接收一个指向 BTNode 类型的指针 root 作为参数,表示二叉树的根节点。
  3. Queue ps;

    • 这行代码声明了一个名为 ps 的队列变量。然而,这可能不足以正确地在某些语言中创建队列,因为队列可能需要动态分配(取决于其实现)。
  4. QueueInit(&ps);

    • 这行代码调用 QueueInit 函数来初始化队列 ps
  5. if (root) QueuePush(&ps, root);

    • 如果根节点 root 不为空,这行代码调用 QueuePush 函数将根节点入队。
  6. while (!QueueEmpty(&ps))

    • 这行代码开始一个循环,它会一直执行,直到队列变空。QueueEmpty 函数检查队列是否为空。
  7. BTNode* ret = QueueFront(&ps);

    • 这行代码调用 QueueFront 函数获取队列前端的节点,但不从队列中移除它。
  8. QueuePop(&ps);

    • 这行代码调用 QueuePop 函数从队列中移除前端的节点。
  9. printf("%d ", ret->_data);

    • 这行代码打印当前节点 ret 的数据。
  10. if (ret->_left) QueuePush(&ps, ret->_left);

    • 如果当前节点 ret 的左子节点存在,这行代码将其入队。
  11. if (ret->_right) QueuePush(&ps, ret->_right);

    • 如果当前节点 ret 的右子节点存在,这行代码将其入队。
  12. QueueDestroy(&ps);

    • 最后,这行代码调用 QueueDestroy 函数销毁队列,释放所有分配的内存。

总结来说,BinaryTreeLevelOrder 函数使用队列来实现二叉树的层序遍历。它首先初始化一个队列,然后将根节点入队。在循环中,它取出队列前端的节点,打印其数据,然后将其左右子节点(如果存在)依次入队。这个过程一直重复,直到队列为空。

这里的关键在于我们要认识到出去一个节点,进去两个节点

判断二叉树是否是完全二叉树

这里的逻辑也是采取队列的形式来进行判断,只是我们需要入空,当最后入的数值为空的时候,我们需要跳出循环,然后进行判断

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{Queue ps;QueueInit(&ps);if (root)QueuePush(&ps, root);while (!QueueEmpty(&ps)){BTNode* ret = QueueFront(&ps);QueuePop(&ps);if (ret == NULL){break;}if (ret->_left)QueuePush(&ps, ret->_left);if (ret->_right)QueuePush(&ps, ret->_right);}while (!QueueEmpty(&ps)){BTNode* ret = QueueFront(&ps);QueuePop(&ps);if (ret != NULL){QueueDestroy(&ps);return false;}}//队列的销毁QueueDestroy(&ps);return true;
}
  1. #include"Queue.h"

    • 包含队列操作的头文件。
  2. bool BinaryTreeComplete(BTNode* root)

    • 函数声明,返回类型为 bool,表示最终的布尔结果(是或不是完全二叉树)。
  3. Queue ps;

    • 声明一个队列 ps。注意:这可能不足以在某些语言中创建队列,因为队列可能需要动态分配。
  4. QueueInit(&ps);

    • 初始化队列。
  5. if (root) QueuePush(&ps, root);

    • 如果根节点不为空,则将其入队。
  6. 第一个 while 循环:

    • 使用队列进行层序遍历,访问所有节点。
  7. BTNode* ret = QueueFront(&ps);

    • 获取队列前端的节点。
  8. QueuePop(&ps);

    • 将队列前端的节点出队。
  9. if (ret == NULL) { break; }

    • 如果节点为 NULL,则退出循环。这个条件永远不会满足,因为 NULL 节点不会被入队。
  10. if (ret->_left) QueuePush(&ps, ret->_left);

    • 如果存在左子节点,则将其入队。
  11. if (ret->_right) QueuePush(&ps, ret->_right);

    • 如果存在右子节点,则将其入队。
  12. 第二个 while 循环:

    • 这个循环的目的是检查队列中是否还有非 NULL 节点。
  13. BTNode* ret = QueueFront(&ps);

    • 再次获取队列前端的节点。
  14. QueuePop(&ps);

    • 再次将队列前端的节点出队。
  15. if (ret != NULL) { QueueDestroy(&ps); return false; }

    • 如果节点不为 NULL,销毁队列并返回 false。这部分逻辑是错误的,因为按照完全二叉树的定义,队列中应该只有 NULL 节点。
  16. QueueDestroy(&ps);

    • 销毁队列。
  17. return true;

    • 返回 true 表示树是完全二叉树。

链式二叉树代码总结

Link_Teer.h

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType _data;struct BinaryTreeNode* _left;struct BinaryTreeNode* _right;
}BTNode;
//二叉树的初始化
BTNode* BuyNode(BTDataType x);
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树高度
int BinaryTreeHeight(BTNode* root);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树销毁
void BinaryTreeDestory(BTNode* root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root);

Link_Teer.c

#include"Link_Teer.h"
//二叉树的初始化
BTNode* BuyNode(BTDataType x)
{BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));if (newnode == NULL){perror("BinaryTreeInit:newnode:");exit(1);}newnode->_data = x;newnode->_left = NULL;newnode->_right = NULL;return newnode;
}
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}printf("%d ", root->_data);BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}BinaryTreeInOrder(root->_left);printf("%d ", root->_data);BinaryTreeInOrder(root->_right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}BinaryTreePostOrder(root->_left);BinaryTreePostOrder(root->_right);printf("%d ", root->_data);
}
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL){return 0;}return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{if (root == NULL)return 0;if (BinaryTreeLeafSize(root->_left) == 0 && BinaryTreeLeafSize(root->_right) == 0)return 1;return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}
// 二叉树高度
int BinaryTreeHeight(BTNode* root)
{ if (root == NULL){return 0;}int heightleft = BinaryTreeHeight(root->_left);int heightright = BinaryTreeHeight(root->_right);return heightleft >= heightright ? heightleft + 1 : heightright + 1;
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL)return 0;if (k == 1)return 1;return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL)return NULL;if (root->_data == x)return root;BTNode* ret1 = BinaryTreeFind(root->_left, x);if (ret1 != NULL){return ret1;}BTNode* ret2 = BinaryTreeFind(root->_right, x);if (ret2 != NULL){return ret2;}return NULL;}
// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{if (root == NULL){return;}BinaryTreeDestory(root->_left);BinaryTreeDestory(root->_right);free(root);
}#include"Queue.h"
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{// 按照顺序,放入队列里面,先进先出,后进后出的原则// 1,创建队列,初始化// 2,如果不为空,根节点入队列// 3,取栈顶,打印,出队列Queue ps;QueueInit(&ps);if (root)QueuePush(&ps, root);while (!QueueEmpty(&ps)){BTNode* ret = QueueFront(&ps);QueuePop(&ps);printf("%d ", ret->_data);if (ret->_left)QueuePush(&ps, ret->_left);if (ret->_right)QueuePush(&ps, ret->_right);}//队列的销毁QueueDestroy(&ps);
}
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{Queue ps;QueueInit(&ps);if (root)QueuePush(&ps, root);while (!QueueEmpty(&ps)){BTNode* ret = QueueFront(&ps);QueuePop(&ps);if (ret == NULL){break;}if (ret->_left)QueuePush(&ps, ret->_left);if (ret->_right)QueuePush(&ps, ret->_right);}while (!QueueEmpty(&ps)){BTNode* ret = QueueFront(&ps);QueuePop(&ps);if (ret != NULL){QueueDestroy(&ps);return false;}}//队列的销毁QueueDestroy(&ps);return true;
}

test.c

#include"Link_Teer.h"
//构建二叉树
void teer()
{BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);//BTNode* node7 = BuyNode(7);node1->_left = node2;node2->_left = node3;node1->_right = node4;node4->_left = node5;node4->_right = node6;//node2->_right = node7;printf(" 二叉树前序遍历测试:\n");BinaryTreePrevOrder(node1);printf("\n\n\n");printf("二叉树中序遍历测试:\n");BinaryTreeInOrder(node1);printf("\n\n\n");printf("二叉树后序遍历测试:\n");BinaryTreePostOrder(node1);printf("\n\n\n");printf("二叉树节点个数测试:\n");int ret1 = BinaryTreeSize(node1);printf("%d", ret1);printf("\n\n\n");printf("二叉树叶子节点个数测试:\n");int ret2 = BinaryTreeLeafSize(node1);printf("%d", ret2);printf("\n\n\n");printf("二叉树高度测试:\n");int ret3 = BinaryTreeHeight(node1);printf("%d", ret3);printf("\n\n\n");printf("二叉树第k层节点个数测试:\n");int ret4 = BinaryTreeLevelKSize(node1, 3);printf("%d", ret4);printf("\n\n\n");printf("二叉树查找值为x的节点测试:\n");BTNode* ret5 = BinaryTreeFind(node1, 6);printf("%d", ret5->_data);printf("\n\n\n");printf("层序遍历测试:\n");BinaryTreeLevelOrder(node1);printf("\n\n\n");printf("判断二叉树是否是完全二叉树测试:\n");bool ret6 = BinaryTreeComplete(node1);printf("%d", ret6);}
int main()
{//构建二叉树teer();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/17700.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单元测试的实现方式

单元测试的实现方式包括&#xff1a;人工静态检查、动态执行跟踪 人工静态检查 人工静态检查是一种单元测试实现方式&#xff0c;它主要依赖开发人员的人工代码审查和静态分析工具来识别潜在的代码问题。 代码审查&#xff1a;开发人员通过仔细检查代码来发现潜在的问题。他…

不怕YOLOv10高歌猛进,我有YOLOv8稳扎稳打

YOLOv10 出来有几天时间了&#xff0c;这次我没有选择第一时间出文章解析&#xff0c;如此频繁的发布数字版本的 YOLO 着实让人头疼&#xff0c;虽然数字的更新并非旧版技术的过时&#xff0c; 但是这肯定会让很多在校同学增加很多焦虑情绪。这里还是请大家辩证看待。 v10 这次…

区间选点问题-贪心-C++

问题&#xff1a; 给定 &#x1d441; 个闭区间 [ai,bi]&#xff0c;请你在数轴上选择尽量少的点&#xff0c;使得每个区间内至少包含一个选出的点。 输出选择的点的最小数量。 位于区间端点上的点也算作区间内。 输入格式 第一行包含整数 &#x1d441;&#xff0c;表示区间数…

CSS文本粒子动画特效之爱心粒子文字特效-Canvas

1. 效果图 2.完整代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><style>body,html {margin: 0;paddin…

order by工作过程和优化

工作过程 order by 是由优化器决定的&#xff0c;如果优化器认为filesort速度快&#xff0c;那么走filesort排序&#xff0c;如果优化器认为索引速度快&#xff0c;那么走索引排序。

有一个3x4的矩阵,求矩阵中所有元素中的最大值。要求用函数处理

解此题的算法已在之前的文章中介绍&#xff0c;详见&#xff1a;https://mp.csdn.net/mp_blog/creation/editor/139181787 编写程序&#xff1a; 运行结果&#xff1a;

卷径计算(PID输出补偿法 SCL源代码)

卷径计算有很多方法,这里我们提供另一个思路,这里我们采用的是通过速度控制间接控制张力通过线速度和系统卷径我们可以计算出我们的速度前馈量(主速度)。具体收放卷前馈量计算可以参考下面文章链接: 收放卷前馈量计算FC(梯形图+SCL代码)-CSDN博客文章浏览阅读584次。这篇博…

【数据分析面试】55. 寻找双词组 (Python)

题目&#xff1a; 寻找双词组 &#xff08;Python&#xff09; 编写一个名为 find_bigrams 的函数&#xff0c;该函数接收一个句子或段落的字符串&#xff0c;并按顺序返回其所有双词组的列表。 注意&#xff1a; 双词组是指连续的两个单词。 示例&#xff1a; 输入&#x…

JavaScript(ES6)入门

ES6 1、介绍 ECMAScript 6&#xff08;简称ES6&#xff09;是于2015年6月正式发布的JavaScript 语言的标准&#xff0c;正式名为ECMAScript 2015&#xff08;ES2015&#xff09;。它的目标是使得JavaScript语言可以用来编写复杂的大型应用程序&#xff0c;成为企业级开发语言。…

Dolphinscheduler不重启加载Oracle驱动

转载自刘茫茫看山 问题背景 某天我们的租户反馈数据库连接缺少必要的驱动&#xff0c;我们通过日志查看确实是缺少部分数据库的驱动&#xff0c;因为DolphinScheduler默认只带了Oracle和MySQL的驱动&#xff0c;并且需要将pom文件中的test模式去掉才可以在打包的时候引入。我…

Unity Dotween 定位点的制作

目录 前言 一、动画预览 二、动画拆分 三、素材准备 四、曲线 OutCirc详解 五、速度分类详解 六、代码 七、组件和设置 八、作者的话 前言 我答应我的粉丝接下来更新Dotween系列&#xff0c;但是我一直没想好&#xff0c;从哪里开始讲。 Dotween的安装我就跳过了&…

QtCreator调试运行工程报错,无法找到相关库的的解决方案

最新在使用国产化平台做qt应用开发时&#xff0c;总是遇到qtcreator内调试运行 找不到动态库的问题&#xff0c;为什么会出现这种问题呢&#xff1f;明明编译的时候能够正常通过&#xff0c;运行或者调试的时候找不到相关的库呢&#xff1f;先说结论&#xff0c;排除库本身的问…

精酿啤酒:品质与口感在消费者选择中的权重分析

在啤酒市场中&#xff0c;消费者选择的影响因素众多&#xff0c;其中品质与口感是两个核心要素。对于Fendi club啤酒而言&#xff0c;品质与口感的权重分析在消费者选择中显得尤为重要。 品质是消费者选择啤酒的首要因素。随着消费者对啤酒认知的提高&#xff0c;他们对品质的…

德邦快递和德邦物流运费标准哪个更划算?怎样才能便宜的寄大件快递?

在寄大件包裹快递时&#xff0c;我们一般都知道选择德邦&#xff0c;那么德邦快递和德邦物流的收费标准哪个更划算呢&#xff1f;下面&#xff0c;让我们一起来了解德邦快递和德邦物流的收费标准&#xff0c;以及如何根据实际情况做出最佳选择。 首先了解快递费用构成 快递费用…

Prometheus Operator创建告警规则并接入钉钉报警

prometheus之钉钉报警 前言1. 添加prometheus报警规则1.2 添加自定义报警规则文件 2. 配置钉钉报警2.2 部署dingding插件 3. 编写alertmanager配置文件 前言 在kubenetes上安装了kube-promethues&#xff08;包含Prometheus Operator&#xff09;,程序正常跑起来了&#xff0c…

IC开发——verdi基本用法

1. 基础知识 1.1. verdi VCS和Verdi这两个工具&#xff0c;这两个工具目前都属于synopsys公司。VCS主要负责编译运行Testbench和RTL&#xff0c;并负责生成相应的波形文件。而verdi主要负责加载波形文件&#xff0c;查看信号的波形及其对应的代码来进行调试验证。Verdi最开始…

Linux 查找命令的操作,学完效率瞬间翻倍?

可以很肯定地说&#xff0c;find 命令是 Linux 运维必须熟知的操作之一。 让我们看一道题&#xff1a; 如果你的 Linux 服务器上有一个名为 .logs 的目录&#xff0c;如何删除该目录下最后一次访问时间超过一年的日志文件呢&#xff1f; 这种情况很常见&#xff0c;但令人惊讶…

【Linux系统】进程间通信

本篇博客整理了进程间通信的方式管道、 system V IPC的原理&#xff0c;结合大量的系统调用接口&#xff0c;和代码示例&#xff0c;旨在让读者透过进程间通信去体会操作系统的设计思想和管理手段。 目录 一、进程间通信 二、管道 1.匿名管道 1.1-通信原理 1.2-系统调用 …

简谈SUID提权

SUID提权 0x01什么是SUID ​ SUID (Set UID)是Linux中的一种特殊权限,其功能为用户运行某个程序时&#xff0c;如果该程序有SUID权限&#xff0c;那么程序运行为进程时&#xff0c;进程的属主不是发起者&#xff0c;而是程序文件所属的属主。但是SUID权限的设置只针对二进制可…

强化学习4:DQN 算法

看这篇文章之前&#xff0c;建议先了解一下&#xff1a;Q-Learning 算法。 1. 算法介绍 DQN 算法全称为 Deep Q-Network&#xff0c;即深度Q网络。它将 Q-Learning 与 Deep Learning 结合在了一起。 1.1 Q-Network Q-Learning 是使用 Q-table 才存储决策信息的&#xff0c;…