title: 树
date: 2018-06-02 00:05:36
tags: 数据结构
树形结构
一、树形结构
基本术语:
-
结点: 数据元素 + 若干指向子树的分支
-
结点的度:分支的个数
-
树的度:树中所有结点的度的最大值
-
结点的层次:假设根结点的层次为1,第k层的结点的子树根结点(也就是它的下一层)的层次为k+1
-
树的深度:树中叶结点所在的最大层次
-
有序树和无序树的区别:子树之间是否存在次序关系,即左右子树交换后还是不是同一棵树
-
**森林:**是m(m≥0)棵互不相交的树的集合
-
树形结构的其他表示方法:
-
树形结构和线性结构的区别:除第一个结点和最后一个结点外的中间结点有一个前驱多个后继,而线性结构是一个前驱一个后继
-
树的存储结构:
(1)双亲表示法: data|parent
#define MAXSIZE 100 typedef char datatype; /*结点值的类型*/ typedef struct node /*结点的类型*/ { datatype data; int parent; /*结点双亲的下标*/ } node; typedef struct tree { node treelist[MAXSIZE];/*存放结点的数组*/ int length, root ; /* 树中实际所含结点的 个数及根结点的位置*/ } tree;
(2)孩子表示法:有指针、数组、链表三种方式表示法
1、指针式
#define m 3 /*树的度数*/ typedef char datatype; /*结点值的类型*/ typedef struct node { /*结点的类型*/ datatype data; struct node *child[m];/*指向子女的指针数组*/ } node,* tree; tree root;
2、数组式
#define m 3 #define MAXSIZE 20 typedef char datatype; typedef struct node { datatype data; int child[m]; } treenode; treenode tree[MAXSIZE]; int root ; int length;
3、链表式
# define MAXSIZE 50 typedef char datatype; typedef struct chnode { /*孩子结点的类型*/ int child; struct chnode *next; } chnode, * chpoint; typedef struct { /* 树中每个结点的类型*/ datatype data; chpoint firstchild;/*指向第一个子女的指针*/ } node; typedef struct { /*树的类型*/ node treelist [MAXSIZE]; int length, root; } tree;
(3)孩子兄弟表示法
typedef char datatype;/*树中结点值的类型*/ typedef struct node {/*树中每个结点的类型*/ datatype data; struct node * firstchild, *rightsibling; } node, * pnode; pnode root; /*指向树根结点的指针*/
-
树的遍历:(指针式孩子表示法)
(1)前序遍历 root–left–right
(2)后序遍历 left–right–root
(3)层次遍历
#include <iostream> #include <algorithm> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <map> #include <queue> #include <stack> #define ll long long #define m 3 const int maxn=100010; const int inf=0x3f3f3f3f; const double eps=1e-8; using namespace std; ll mod=1000000007; using namespace std; typedef struct node{ char data; struct node* child[m]; }node,*tree; tree root;//tree是node*的 void create(tree *p)//前序遍历建树 { char ch; if((ch=getchar())=='#') *p=NULL; else { *p=(tree)malloc(sizeof(node)); (*p)->data=ch; for(int i=0;i<m;++i) { create(&(*p)->child[i]); } } } void preorder(tree &p) { if(p) { printf("%c",p->data); for(int i=0;i<m;++i) preorder(p->child[i]); } } void postorder(tree p) { if(p) { for(int i=0;i<m;++i) postorder(p->child[i]); printf("%c",p->data); } } void levelorder(tree t) { queue<tree>q; q.push(t); tree p; while(!q.empty()) { p=q.front(); printf("%c",p->data); for(int i=0;i<m;i++) if(p->child[i]) q.push(p->child[i]); q.pop(); } } int main() { ios::sync_with_stdio(false); create(&root); preorder(root);//ABCEFHIGD //postorder(root);//BEHIFGCDA //levelorder(root);//ABCDEFGHI return 0; }
二、二叉树
-
特征:
(1)二叉树中每个非空结点最多只有两个子女.
(2)二叉树中结点的子树要区分左子树和右子树, 即使在结点只有一棵子树的情况下也要明确指 出是左子树还是右子树。
-
性质:
(1)一棵非空二叉树的第i层上至多有2^i-1个结点 (i≥1)
(2)深度为h的二叉树至多有2^h-1个结点(h>1)
(3)对于任何一棵二叉树T,如果其终端结点数(即 叶结点数)为n0,度为2的结点数为n2,则n0=n2+1。
证明:
假设二叉树中总的结点个数为n ,度为1的结点个数为n1,则有: n=n0+n1+n2
又由于在二叉树中除根结点外,其它结点均通过一条树枝且仅通过一条树枝与其父母结点相连,即除根结点外,其它结点与树中的树枝存在一一对应的关系;
而二叉树中树枝的总条数为n1+2n2,因而二叉树总结点的个数为: n=n1+2n2+1 于是有: n0+n1+n2=n1+2*n2+1
显然n0=n2+1成立。
(4)对于具有n个结点的完全二叉树,如果按照从上到下、同一层次上的结点按从左到右的顺序对二叉树中的所有结点从1开始顺序编号,则对于序号为i的结点,有:
-
**满二叉树:**二叉树中所有终端结点均位于同一层次, 而其它非终端结点的度数均为2。(范围更小)
-
完全二叉树:二叉树扣除其最大层次那层后即成为一棵满二叉树,且层次最大那层的所有结点均向左靠齐。即完全二叉树中只有最下面的两层结点的度数可以小于2,且最下面一层的结点都集中在该层最左边的若干位置上。(如图二) (范围更大)
-
**注意:**满二叉树一定是完全二叉树,但完全二叉树不一定是满二叉树。
一般二叉树的存储结构
一般二叉树顺序存储:
#define MAXSIZE 20
typedef char datatype; /*结点值的类型*/
typedef struct {
datatype data;
int lchild,rchild;
} node; /*二叉树结点的类型*/
node tree[MAXSIZE];
int n; /*树中实际所含结点的个数*/
int root; /*存放根结点的下标*/
带双亲指示的二叉树顺序存储:
#define MAXSIZE 20
typedef char datatype; /*结点值的类型*/
typedef struct
{
datatype data;
int lchild,rchild;
int parent; /*存放双亲结点的下标*/
} node; /*二叉树结点的类型*/
node tree[MAXSIZE];
int n; /*树中实际所含结点的个数*/
int root; /*存放根结点的下标*/
链式存储方式下二叉树结点数据结构:
typedef char datatype; /*结点属性值的类型*/
typedef struct node{ /*二叉树结点的类型*/
datatype data;
struct node *lchild, *rchild;
} bintnode;
typedef bintnode* bintree;
bintree root;
链式存储方式下带双亲指针的二叉树结点数据结构:
typedef char datatype; /*结点属性值的类型*/
typedef struct node{ /*二叉树结点的类型*/
datatype data;
struct node *lchild, *rchild;
struct node *parent; /*指向双亲的指针*/
} bintnode;
typedef bintnode *bintree; bintree root; /*指向二叉树根结点的指针*/
二叉树的遍历
- 遍历方式
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <queue>
#include <stack>
#define ll long long
#define m 3
const int maxn=100010;
const int inf=0x3f3f3f3f;
const double eps=1e-8;
using namespace std;
ll mod=1000000007;
using namespace std;
typedef struct node{
char data;
struct node *lchild,*rchild;
}bintnode;
typedef bintnode* bintree;
bintree root;//tree是node*的
void create(bintree *t)
{
char ch;
if((ch=getchar())==' ')
*t=NULL;
else
{
*t=(bintnode*)malloc(sizeof(bintnode));//生成二叉树的根节点
(*t)->data=ch;
create(&(*t)->lchild);//递归实现左子树
create(&(*t)->rchild);//递归实现右子树
}
}
/*递归版遍历*/
void preorder(bintree t)
{
if(t)
{
printf("%c",t->data);
preorder(t->lchild);
preorder(t->rchild);
}
}
void midorder(bintree t)
{
if(t)
{
midorder(t->lchild);
printf("%c",t->data);
midorder(t->rchild);
}
}
void postorder(bintree t)
{
if(t)
{
postorder(t->lchild);
postorder(t->rchild);
printf("%c",t->data);
}
}
/*非递归版*/
int main()
{
ios::sync_with_stdio(false);
create(&root);
preorder(root);
midorder(root);
postorder(root);
return 0;
}
三、二叉搜索树(又称二叉排序树)
性质:
- 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
- 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
- 任意节点的左、右子树也分别为二叉查找树;
- 没有键值相等的节点。
//这里还应该写一个二叉搜索树的实现及其算法
四、树堆(Treap)
Treap是一棵二叉排序树,它的左子树和右子树分别是一个Treap,和一般的二叉排序树不同的是,Treap纪录一个额外的数据,就是优先级。Treap在以关键码构成二叉排序树的同时,还满足堆的性质(在这里我们假设节点的优先级大于该节点的孩子的优先级)。
但是这里要注意的是Treap和二叉堆有一点不同,就是二叉堆必须是完全二叉树,而Treap可以并不一定是。
//各种树什么的开始蒙圈了 溜
留几个看着不错的博客吧,下次继续
敲黑板!!