⼀、⼆叉树
1.树简介
1) 树的定义
树是⼀种⾮线性的数据结构,数据逻辑中之所以会引⼊“树”这个东⻄,是借助了⽣活中树的“分⽀”的概念。逻辑数据中的树可⽤于描述⼀种类似组织结构的关 系。
树是⼀种数据结构,它是由n(n>=1)个有限结点组成⼀个具有层次关系的集合。 把它叫做“树”是因为它看起来像⼀棵倒挂的树,也就是说它是根朝上,⽽叶朝下的。
树的特点:
每个结点有零个或多个⼦结点;没有⽗结点的结点称为根结点;每⼀个⾮根结点有且只有⼀个⽗结点;除了根结点外,每个⼦结点可以分为多个不相交的⼦树;
相关术语:
1. 结点: 树中的元素及其⼦树
2. 孩⼦/双亲: ⼀个结点的后继结点叫该结点的孩⼦, 该结点叫孩⼦的双亲结点。
3. 结点的度:该结点分⽀的数量 ,⽐如A节点的度为3,C节点的度为1。
4. 叶⼦: 度为0的结点。
5. 结点的层次: 从根到该结点的层数(根的层次为1)。
6. 树的⾼度(深度):树中结点层次的最⼤值 。
7.根节点 :没有⽗结点的结点称为根结点
当n=0时称为空树。在任⼀⾮空树中:①有且仅有⼀个称为该树之根的节点;②除根结点之外的其余节点可分为有限个互不相⼲的集合,且其中每⼀个集合本身⼜是⼀棵树,称为根的⼦树。
2) ⼆叉树
⼆叉树是每个结点最多有两个⼦树的树结构。它有五种基本形态:⼆叉树可以是空集;根可以有空的左⼦树或右⼦树;或者左、右⼦树皆为空。
⼆叉树第i层上的结点数⽬最多为2 ^(i-1)(i>=1)
深度为k的⼆叉树⾄多有2^k -1个结点(k>=1)
3) 二叉树分类
①满二叉树:一个深度为k,且有2^k-1个结点的二叉树,称为满二叉树。这种树的特点是每一层上的特点是每一层上的结点数都是最大结点数。
②完全二叉树:在一棵二叉树中,除最后一层外,若其余层都是满的,并且或者最后一层是满的,或者是在右边缺少连续若干结点,则此二叉树为完全二叉树
③二叉排序树:⼆叉排序树(Binary Sort Tree)⼜称⼆叉查找(搜索)树(Binary Search Tree)。其定义为:⼆叉排序树或者是空树,或者是满⾜如下性质的⼆叉树:
①若它的左⼦树⾮空,则左⼦树上所有结点的值均⼩于根结点的值;
②若它的右⼦树⾮空,则右⼦树上所有结点的值均⼤于根结点的值;
③左、右⼦树本身⼜各是⼀棵⼆叉排序树。
建⽴⼆叉树:
//建⽴链表的函数如下:
#include <stdlib.h>
#include<stdio.h>
typedef int DATA; //将int 类型重定义为DATA 类型
#define LEN sizeof(struct node) //令LEN代表struct node类型数据的⻓度
struct node
{
DATA data;
struct node *left;
struct node *right;
};
插入过程:
⽐如:18 5 74 15 33 44 16
⼆叉树的遍历
1:先序遍历:根->左⼦树->右⼦树(先序)(如果⽤⾮递归,就是使⽤栈)
2:中序遍历:左⼦树->根->右⼦树(中序)
3:后序遍历:左⼦树->右⼦树->根(后序)
前序遍列:
中序遍历:
后序遍历:
实现过程:
1.二叉搜索树结构体
typedef struct mynode
{
int data;
struct mynode* left; //左子树
struct mynode* right; //右子树
} Node;
2.构建一颗二叉搜索树
//构建一颗搜索二叉树
Node* build_tree(Node* root, int val)
{
//1.构建根节点
if (root == NULL)
{
root = (Node*)malloc(sizeof(Node));
if (root == NULL)
return NULL;
root->data = val;
root->left = NULL;
root->right = NULL;
return root;
}
Node* cur = root;
//2.寻找挂载的位置
while (1)
{
if (cur->data > val) //找左子树的位置
{
if (cur->left != NULL)
{
cur = cur->left;
}
else
break;
}
else
{
if (cur->right != NULL)
cur = cur->right;
else
break;
}
}
//3.挂载节点
Node* new_node = (Node*)malloc(sizeof(Node));
if (new_node == NULL) return NULL;
new_node->data = val;
new_node->left = new_node->right = NULL;
if (cur->data > val) //要挂载的这个值比挂载节点的值小
cur->left = new_node; //挂载到左边
else //要挂载的这个值比挂载节点的值大
cur->right = new_node; //挂载到右边
return root;
}
3.遍历二叉树----使用中序遍历(可以实现排序输出(从小到大))
//遍历节点----中序遍历
void print_all(Node* root)
{
if (root == NULL)
return;
//中序遍历
print_all(root->left); //左
printf("%d\n", root->data); //根
print_all(root->right); //右
//根据这三个所放位置的不同也会实现不一样的遍历方式(前序遍历,后序遍历)
}
4.1计算节点个数--第一种方式
//计算节点个数----利用递归来统计节点个数
int print_num(Node* root)
{
if (root == NULL) return 0;
return print_num(root->left) + print_num(root->right) + 1;
}
4.2计算节点个数--第二种方式
//计算节点个数----利用栈结构来统计节点个数
int print_num(Node* root)
{
int count = 0;
Node* cur = root;
Node** stack = (Node**)malloc(sizeof(Node*) * 10);
int top = -1;
stack[++top] = root;
while (top != -1)
{
cur = stack[top--]; //出栈(后进先出) 所以先出的是左子树
if (cur != NULL)
count++;
if (cur->right != NULL) //先判断右子树是否有节点,如果有先压栈
{
stack[++top] = cur->right;
}
if (cur->left != NULL) //再判断右子树是否有节点,如果有压栈
{
stack[++top] = cur->left; //右子树后进栈
}
}
return count;
}
代码实现:
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include<stdbool.h>
#include<assert.h>
typedef struct mynode
{
int data;
struct mynode* left; //左子树
struct mynode* right; //右子树
} Node;
//构建一颗搜索二叉树
Node* build_tree(Node* root, int val)
{
//1.构建根节点
if (root == NULL)
{
root = (Node*)malloc(sizeof(Node));
if (root == NULL)
return NULL;
root->data = val;
root->left = NULL;
root->right = NULL;
return root;
}
Node* cur = root;
//2.寻找挂载的位置
while (1)
{
if (cur->data > val) //找左子树的位置
{
if (cur->left != NULL)
{
cur = cur->left;
}
else
break;
}
else
{
if (cur->right != NULL)
cur = cur->right;
else
break;
}
}
//3.挂载节点
Node* new_node = (Node*)malloc(sizeof(Node));
if (new_node == NULL) return NULL;
new_node->data = val;
new_node->left = new_node->right = NULL;
if (cur->data > val) //要挂载的这个值比挂载节点的值小
cur->left = new_node; //挂载到左边
else //要挂载的这个值比挂载节点的值大
cur->right = new_node; //挂载到右边
return root;
}
//遍历节点----中序遍历
void print_all(Node* root)
{
if (root == NULL)
return;
//中序遍历
print_all(root->left); //左
printf("%d\n", root->data); //根
print_all(root->right); //右
}
计算节点个数----利用递归来统计节点个数
//int print_num(Node* root)
//{
// if (root == NULL) return 0;
// return print_num(root->left) + print_num(root->right) + 1;
//}
//计算节点个数----利用栈结构来统计节点个数
int print_num(Node* root)
{
int count = 0;
Node* cur = root;
Node** stack = (Node**)malloc(sizeof(Node*) * 10);
int top = -1;
stack[++top] = root;
while (top != -1)
{
cur = stack[top--]; //出栈(后进先出) 所以先出的是左子树
if (cur != NULL)
count++;
if (cur->right != NULL) //先判断右子树是否有节点,如果有先压栈
{
stack[++top] = cur->right;
}
if (cur->left != NULL) //再判断右子树是否有节点,如果有压栈
{
stack[++top] = cur->left; //右子树后进栈
}
}
return count;
}
int main()
{
int arr[6] = { 5,4,2,8,9,6 };
Node* root = NULL;
for (int i = 0; i < 6; i++)
{
root = build_tree(root, arr[i]);
}
print_all(root);
printf("结点个数为:%d\n", print_num(root));
return 0;
}
结果(按从小到大顺序打印出来):