赞
踩
树是一种非线性的数据结构,它有着多分支,层次性的特点。
由于其形态类似于自然界中倒过来的数,所以我们将这种数据结构称为“树形结构”
注意: 树形结构中,子树之间不能有交集,否它就不是树形结构
树的表示我们使用:孩子兄弟表示法
设计一个数的节点,其中包含数据域(存储数据)、指针域(左孩子指针,右兄弟指针)
- typedef int DataType;
- struct Node
- {
- struct Node* firstChild1; // 第一个孩子结点
- struct Node* pNextBrother; // 指向其下一个兄弟结点
- DataType data; // 结点中的数据域
- };
这种数的设计方法,我们可以通过左孩子指针找到 A节点 的第一个孩子(B),在通过孩子的右兄弟指针把 A节点 的所有孩子都找到
树在实际中的运用:电脑中的数目录
在实际运用中,二叉树要比树更加实用
二叉树其实就是特殊的一种树,它的每个节点最多有两个子节点,通常被称为左子节点和右子节点
像下面这个二叉树,最后一层并不连续,因此它并非是完全二叉树:
顺序存储结构只推荐完全叉树来进行存储,一般的二叉树容使用顺序结构进行存储,容易造成空间的大量浪费,现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段
堆在这篇文章中有所介绍 ———— 数据结构 - 堆
在进行二叉树链式结构的实现时,我们首先回顾二叉树是:
每一颗二叉树都可以看做是递归形成的因为:
每一颗二叉树都可以拆分成:根节点 左子树 右子树
它的左子树可以被拆分成 :根节点 左子树 右子树
它的右子树右也可以被拆分成 :根节点 左子树 右子树
依次类推直到变成一颗空树,不能被拆分,所以才会说二叉树可以看做是递归形成,二叉树可以被拆分成一个一个的小问题(即一个一个的子树 根节点),直到变成空树不能再被拆分,因此后序基本操作中基本都是按照递归概念实现的
- typedef int BTDataType;
-
- typedef struct BinaryTreeNode //二叉树的单个节点
- {
- BTDataType _data;
- struct BinaryTreeNode* _left; //左孩子
- struct BinaryTreeNode* _right; //右孩子
- }BTNode;
二叉树的遍历是指按照某种规则访问树中的所有节点,并且每个节点只被访问一次。访问结点所做的操作依赖于具体的应用问题。遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。
按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:
1. 前序遍历 (Preorder Traversal )—— 访问顺序:根节点 —>左子树 —>右子树2. 中序遍历(Inorder Traversal)——访问顺序:左子树 —>根节点 —>右子树3. 后序遍历(Postorder Traversal)——访问顺序:左子树 —>右子树—>根节
- // 二叉树前序遍历
- void BinaryTreePrevOrder(BTNode* root) {
-
- if (root == NULL) //当访问的数为NULL树时停止访问
- {
- printf("N ");
- return;
- }
-
- printf("%d ",root->_data);//先便利根节点,整形的数据类型
-
- BinaryTreePrevOrder(root->_left);//左子树
- BinaryTreePrevOrder(root->_right);//右子树
-
- }
- // 二叉树中序遍历
- void BinaryTreeInOrder(BTNode* root) {
-
- if (root == NULL)
- {
- printf("N ");
- return;
- }
-
- BinaryTreeInOrder(root->_left);//左子树
- printf("%d ", root->_data);//根节点
- BinaryTreeInOrder(root->_right);//右子树
- }
-
- // 二叉树后序遍历
- void BinaryTreePostOrder(BTNode* root) {
- if (root == NULL)
- {
- printf("N ");
- return;
- }
-
- BinaryTreePostOrder(root->_left);//左子树
- BinaryTreePostOrder(root->_right);//右子树
- printf("%d ", root->_data);//根节点
- }
代码实现:
二叉树的层序遍历并不是通过递归来完成的,而是通过 —— 数据结构中的队列来实现的
遍历的原理是从根节点开始,首先访问根节点,然后将根节点的左右子节点依次入队。接下来,从队列中取出一个节点(队首节点),访问该节点,再将其未被访问的左右子节点入队。重复此过程,直到队列为空,即所有节点都被访问过。
动图理解:
- // 层序遍历
- void BinaryTreeLevelOrder(BTNode* root) {
- //创建队列
- Queue qu;
- QueueInit(&qu);
- QueuePush(&qu, root);
-
- //开始拖家带口,当队列为NULL时,说明已经遍历完成,循环结束
- while (!QueueEmpty(&qu))
- {
- //先访问队头的元素
- BTNode* bt = QueueFront(&qu);//获取队头元素
- printf("%d ", bt->_data);
-
- //将树的左右孩子都带入队列中,NULL孩子除外
- if (bt->_left)
- QueuePush(&qu, bt->_left);
- if (bt->_right)
- QueuePush(&qu, bt->_right);
-
- //队头数据处队列
- QueuePop(&qu);
- }
-
- //销毁队列
- QueueDestroy(&qu);
-
- }
- #pragma once
- #include<stdio.h>
- #include<stdlib.h>
- #include<assert.h>
- #include<stdbool.h>
-
- typedef struct BinaryTreeNode* QDataType; //队列中的元素是树的节点
- // 链式结构:表示队列
- typedef struct QListNode
- {
- struct QListNode* _next;
- QDataType _data;
- }QNode;
-
- // 队列的结构
- typedef struct Queue
- {
- QNode* _front;
- QNode* _rear;
- int size;
- }Queue;
-
- // 初始化队列
- void QueueInit(Queue* q);
- // 队尾入队列
- void QueuePush(Queue* q, QDataType data);
- // 队头出队列
- void QueuePop(Queue* q);
- // 获取队列头部元素
- QDataType QueueFront(Queue* q);
- // 获取队列队尾元素
- QDataType QueueBack(Queue* q);
- // 获取队列中有效元素个数
- int QueueSize(Queue* q);
- // 检测队列是否为空,如果为空返回非零结果,如果非空返回0
- int QueueEmpty(Queue* q);
- // 销毁队列
- void QueueDestroy(Queue* q);
- #define _CRT_SECURE_NO_WARNINGS 1
- #include"Queue.h"
-
- // 初始化队列
- void QueueInit(Queue* q) {
- assert(q);
-
- q->size = 0;
- q->_front = NULL;
- q->_rear = NULL;
- }
- // 队尾入队列
- void QueuePush(Queue* q, QDataType data) {
- assert(q);
-
- QNode* newnode = (QNode*)malloc(sizeof(QNode));
- if (newnode == NULL)
- {
- perror("QueuePush()::malloc()");
- return;
- }
-
- newnode->_data = data;
- newnode->_next = NULL;
-
- //队列为NULL
- if (q->_front == NULL)
- {
- q->_front = q->_rear = newnode;
- }
- else
- {
- q->_rear->_next = newnode;
- q->_rear = q->_rear->_next;
- }
-
- q->size++;
- }
- // 队头出队列
- void QueuePop(Queue* q) {
- assert(q);
- assert(q->size != 0);
-
- //单个节点
- if (q->_front == q->_rear)
- {
- free(q->_front);
- q->_front = q->_rear = NULL;
- }
- //多个节点
- else
- {
- QNode* next = q->_front->_next;
- free(q->_front);
- q->_front = next;
- }
-
- q->size--;
- }
- // 获取队列头部元素
- QDataType QueueFront(Queue* q) {
-
- assert(q);
- assert(q->_front);//队头不能为NULL
-
- return q->_front->_data;
- }
- // 获取队列队尾元素
- QDataType QueueBack(Queue* q) {
- assert(q);
- assert(q->_rear);//队尾不能为NULL
-
- return q->_rear->_data;
- }
- // 获取队列中有效元素个数
- int QueueSize(Queue* q) {
-
- return q->size;
- }
- // 检测队列是否为空,如果为空返回非零结果,如果非空返回0
- int QueueEmpty(Queue* q) {
- assert(q);
-
- return q->size == 0;
- }
- // 销毁队列
- void QueueDestroy(Queue* q) {
- assert(q);
-
- QNode* cur = q->_front;
- while (cur)
- {
- QNode* next = cur->_next;
- free(cur);
- cur = next;
- }
-
- q->_front = q->_rear = NULL;
- q->size = 0;
-
- //这个应该留给用户去释放
- /*free(q);
- q = NULL;*/
- }
- #pragma once
- #include<stdio.h>
- #include<assert.h>
- #include<stdlib.h>
- #include"Queue.h"
- typedef char BTDataType;
-
- typedef struct BinaryTreeNode
- {
- BTDataType _data;
- struct BinaryTreeNode* _left;
- struct BinaryTreeNode* _right;
- }BTNode;
-
-
- // 二叉树销毁
- void BinaryTreeDestory(BTNode** root);
-
- // 二叉树节点个数
- int BinaryTreeSize(BTNode* root);
-
- // 二叉树叶子节点个数
- int BinaryTreeLeafSize(BTNode* root);
-
- // 二叉树第k层节点个数
- int BinaryTreeLevelKSize(BTNode* root, int k);
-
- // 二叉树查找值为x的节点
- BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
-
- // 二叉树前序遍历
- void BinaryTreePrevOrder(BTNode* root);
-
- // 二叉树中序遍历
- void BinaryTreeInOrder(BTNode* root);
-
- // 二叉树后序遍历
- void BinaryTreePostOrder(BTNode* root);
-
- // 层序遍历
- void BinaryTreeLevelOrder(BTNode* root);
-
- // 判断二叉树是否是完全二叉树
- int BinaryTreeComplete(BTNode* root);
-
- BTNode* CreatBinaryTree();
- #define _CRT_SECURE_NO_WARNINGS 1
- #include"BinaryTree.h"
- BTNode* BuyNode(BTDataType x) {
- BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
- if (newnode == NULL)
- {
- perror("Buynode()::malloc()");
- return newnode;
- }
-
- newnode->_data = x;
- newnode->_left = NULL;
- newnode->_right = NULL;
- return newnode;
- }
-
- // 二叉树前序遍历
- void BinaryTreePrevOrder(BTNode* root) {
-
- if (root == NULL) //当访问的数为NULL树时停止访问
- {
- printf("N ");
- return;
- }
-
- printf("%d ",root->_data);//先便利根节点,整形的数据类型
-
- BinaryTreePrevOrder(root->_left);//左子树
- BinaryTreePrevOrder(root->_right);//右子树
-
- }
- // 二叉树中序遍历
- void BinaryTreeInOrder(BTNode* root) {
-
- if (root == NULL)
- {
- printf("N ");
- return;
- }
-
- BinaryTreeInOrder(root->_left);//左子树
- printf("%d ", root->_data);//根节点
- BinaryTreeInOrder(root->_right);//右子树
- }
-
- // 二叉树后序遍历
- void BinaryTreePostOrder(BTNode* root) {
- if (root == NULL)
- {
- printf("N ");
- return;
- }
-
- BinaryTreePostOrder(root->_left);//左子树
- BinaryTreePostOrder(root->_right);//右子树
- printf("%d ", root->_data);//根节点
- }
-
- //求二叉树的高度
- int maxDepth(BTNode* root) {
-
- if (root == NULL) //如果为空树则返回 0
- {
- return 0;
- }
-
- int lefthigh = maxDepth(root->_left); //记录树的左子树高度
- int righthigh = maxDepth(root->_right); //记录树的右子树高度
-
- //左子树高则返回左子树的高度 右子树高则返回右子树高度
- return lefthigh > righthigh ? lefthigh + 1 : righthigh + 1;
-
- }
-
- // 二叉树叶子节点个数
- int BinaryTreeLeafSize(BTNode* root) {
-
- if (root == NULL) //如果为空树则返回 0
- return 0;
-
- if (root->_left == NULL && root->_right == NULL) //如果是叶子节点就返回 1
- return 1;
-
- //返回左子树 与 右子树总共的叶子节点
- return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
- }
-
- // 二叉树第k层节点个数
- int BinaryTreeLevelKSize(BTNode* root, int k) {
-
- //将找第k层问题转化成:层序遍历按照树的层次进行遍历,每次遍历一层,直到遍历到第k层或者遍历完整个树。
-
- if (root == NULL) //如果为空树则返回 0
- return 0;
-
- if (root != NULL && k == 1) //当不为空且k为1时,到达所找层,返回1
- return 1;
-
- //一层一层的往下找
- if (root != NULL && k > 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* find1 = NULL;
- BTNode* find2 = NULL;
-
-
- find1 = BinaryTreeFind(root->_left, x); //记录所找的节点
-
- if (find1)//如果左边找到了就不用去右边找了
- return find1;
-
- find2 = BinaryTreeFind(root->_right, x);
- return find2;
- }
-
- // 二叉树销毁
- void BinaryTreeDestory(BTNode** root) {
-
- if (*root == NULL)
- return;
- BinaryTreeDestory(&((*root)->_left));//先销毁左子树
- BinaryTreeDestory(&((*root)->_right));//在销毁右子树
- free(*root);
- *root = NULL;
- }
-
- // 二叉树节点个数
- int BinaryTreeSize(BTNode* root) {
- if (root == NULL)
- return 0;
-
- return BinaryTreeSize(root->_left)+ BinaryTreeSize(root->_right) + 1;
- }
-
- // 层序遍历
- void BinaryTreeLevelOrder(BTNode* root) {
- //创建队列
- Queue qu;
- QueueInit(&qu);
- QueuePush(&qu, root);
-
- //开始拖家带口,当队列为NULL时,说明已经遍历完成,循环结束
- while (!QueueEmpty(&qu))
- {
- //先访问队头的元素
- BTNode* bt = QueueFront(&qu);//获取队头元素
-
- printf("%d ", bt->_data);
-
- //将树的左右孩子都带入队列中
- if (bt->_left)
- QueuePush(&qu, bt->_left);
- if (bt->_right)
- QueuePush(&qu, bt->_right);
-
- QueuePop(&qu);
- }
-
- QueueDestroy(&qu);
-
-
- }
-
- // 判断二叉树是否是完全二叉树
- int BinaryTreeComplete(BTNode* root) {
-
- //创建队列
- Queue qu;
- QueueInit(&qu);
- QueuePush(&qu, root);
-
- //开始拖家带口,当队列为NULL时,说明已经遍历完成,循环结束
- while (!QueueEmpty(&qu))
- {
- //队列中存的数据是,树节点的指针,我们先访问队头的元素
- BTNode* bt = QueueFront(&qu);//获取队头元素
-
- if (bt == NULL)
- {
- break;
- }
-
-
- //将树的左右孩子都带入队列中,NULL也不例外
- QueuePush(&qu, bt->_left);
- QueuePush(&qu, bt->_right);
-
- QueuePop(&qu);
- }
-
- while (!QueueEmpty(&qu))
- {
- BTNode* bt = QueueFront(&qu);//获取队头元素
-
- //如果在遇到非空的节点,说明它不是一个完全二叉树返回false
- if (bt)
- {
- return false;
- }
-
- QueuePop(&qu);
- }
-
- QueueDestroy(&qu);
-
- return true;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。