树的定义
树是由一个集合以及在该集合上定义的一种关系构成的,集合中的元素称为树的结点,所定义的关系称为父子关系。父子关系在树的结点之间建立了一个层次结构,在这种层次结构中有一个结点具有特殊的地位,这个结点称为该树的根结点。
数据结构中有很多树的结构,其中包括二叉树、二叉搜索树、2-3树、红黑树等等,本文着重介绍二叉树。
树的基本术语
节点的度:一个节点含有的子树的个数称为该节点的度;
叶节点或终端节点:度为0的节点称为叶节点;
非终端节点或分支节点:度不为0的节点;
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
兄弟节点:具有相同父节点的节点互称为兄弟节点;
树的度:一棵树中,最大的节点的度称为树的度;
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
树的高度或深度:树中节点的最大层次;
堂兄弟节点:双亲在同一层的节点互为堂兄弟;
节点的祖先:从根到该节点所经分支上的所有节点;
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。
森林:由m(m>=0)棵互不相交的树的集合称为森林;
树的存储结构
双亲表示法
孩子表示法
二叉树
二叉树是数据结构中一种重要的数据结构,也是树表家族最为基础的结构。
二叉树的定义:二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。
满二叉树
一棵深度为k且有2 k − 1 2^k-12 k−1 个结点的二叉树称为满二叉树。
完全二叉树
深度为k的,有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时,称之为完全二叉树。
**注:**完全二叉树是效率很高的数据结构,堆是一种完全二叉树或者近似完全二叉树,所以效率极高,像十分常用的排序算法、Dijkstra算法、Prim算法等都要用堆才能优化,二叉排序树的效率也要借助平衡性来提高,而平衡性基于完全二叉树。
二叉排序树
二叉查找树定义:又称为是二叉排序树(Binary Sort Tree)或二叉搜索树。
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
它的左、右子树也分别为二叉排序树。
平衡二叉树
平衡二叉树(Balanced Binary Tree)又被称为AVL树。它或者是一棵空树,或者是具有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。(注:平衡二叉树应该是一棵二叉排序树)
例题1:
给定一棵树,输出树的根root,孩子最多的结点max以及他的孩子。
第一行:n(结点个数≤100),m(边数≤200)。
以下m行:每行两个结点x和y,表示y是x的孩子(x,y≤1000)。
第一行:树根:root;
第二行:孩子最多的结点max;
第三行:max的孩子(按编号由小到输出)。
8 7 4 1 4 2 1 3 1 5 2 6 2 7 2 8
4 2 6 7 8
#include
using namespace std;
struct NODE
{
int cnt_son;
int son[201];
int father;
}parent[1001];
int main()
{
int x, y, n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++)
{
scanf("%d%d", &x, &y);
parent[y].father = x;
parent[x].cnt_son++;
parent[x].son[parent[x].cnt_son] = y;
}
int root = 0;
for(int i = 1; i <= n; i++)
{
if(parent[i].father == 0)
{
root = i;
break;
}
}
int maxi = 0;
for(int i = 1; i <= n; i++)
{
if(parent[maxi].cnt_son < parent[i].cnt_son)
maxi = i;
}
printf("%d\n%d\n", root, maxi);
for(int i = 1; i <= parent[maxi].cnt_son; i++)
{
printf("%d ", parent[maxi].son[i]);
}
return 0;
}
例题2:
设有一棵二叉树(如图3-8,其中圈中的数字表示结点中居民的人口,圈边上数字表示结点编号。现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻结点之间的距离为1。就本图而言,若医院建在1处,则距离和=4+12+2*20+2*40=136;若医院建在3处,则距离和=4*2+13+20+40=81……
第一行一个整数n,表示树的结点数(n≤100)。接下来的n行每行描述了一个结点的状况,包含三个整数,整数之间用空格(一个或多个)分隔,其中:第一个数为居民人口数;第二个数为左链接,为0表示无链接;第三个数为右链接,为0表示无链接。
一个整数,表示最小距离和。
5 13 2 3 4 0 0 12 4 5 20 0 0 40 0 0
81
#include "bits/stdc++.h"
using namespace std;
//1、设计一种数据类型,存储本题“图中节点的连接关系”。参看本题题目中的提示
//采用的数据结构:采用二维数组,若i节点->j节点有直接连接,则在相应位置ljjz[i][j]赋值(距离),
//i == j的位置:写0,表示距离为0;其他无直接连接位置:ljjz[i][j] = 无穷大(暂时);
int ljjz[101][101]; //存储各个节点之间的(最短)距离(邻接矩阵)
int people[101]; //各节点对应的人口数
int cnt; //输入节点个数、后续数据的行数
int main()
{
//2、数据输入。数据插入到distance[][]中
//题目明确给定:所有直接相连的节点距离为distance[][]=1
//2.1输入节点总数、后续数据的行数(题目给定:cnt <=100,所有节点均为连接在一体)
cin >> cnt;
//2.1 初始化data[][]数据为cnt * 1,100为本题目中的无穷大,即大于最不利原则距离
for(int i = 0; i <= cnt; i++){
for(int j = 0; j <= cnt; j++){
if(i == j)
ljjz[i][j] = 0; //distance
else
ljjz[i][j] = 100; //cnt * 1;
}
}
//2.2 数据插入到ljjz[][]中,数据输入完成。
int p, i = 1, j1, j2;//p人口数
for(int n = 1; n <= cnt; n++, i++){
cin >> p; //该行第1个数据,人口数
cin >> j1; //该行第2个数据,left节点地址
cin >> j2; //该行第3个数据,right节点地址
people[i] = p; //记录该节点的人口数
if(j1 != 0){
ljjz[i][j1] = 1; //存放--直接相连的两点距离,1
ljjz[j1][i] = 1;
}
if(j2 != 0){
ljjz[i][j2] = 1; //存放--直接相连的两点距离,1
ljjz[j2][i] = 1;
}
}
//3、foled最短路径
//迭代条件:如果 i->j 的间接距离([i][j])大于间接路径距离(i->k->j,[i][k]+[k][j]),
//将更小的间接路径距离赋给[i][j]
for(int i = 1; i <= cnt; i++){
for(int j = 1; j <= cnt; j++){
if((i != j) ||(ljjz[i][j] != 1)){ //本身或则直接连接就不用再迭代了
for(int k = 1; k <= cnt; k++){
if(ljjz[i][j] > (ljjz[i][k] + ljjz[k][j]))
ljjz[i][j] = ljjz[i][k] + ljjz[k][j];
}
}
}
}
//4、依次以每个节点为医院位置,求出所对应的所有居民所走的路程之和, 存于ljjz[0][j]
//若以节点1为医院,则所有居民所走的路程之和存放于ljjz[0][1]
//ljjz[0][1] += people[i]*ljjz[i][1] (i from 1 to cnt)
for(int j = 1; j <= cnt; j++){
ljjz[0][j] = 0;
for(int i = 1; i <= cnt; i++){
ljjz[0][j] += people[i]*ljjz[i][j];
}
}
/*----------------------测试用-------------------------------//
//2.3 输出ljjz[][],检查数据输入环节是否正确。
cout << endl << endl << "邻接矩阵输出" << endl << endl;
for(int i = 0; i <= cnt; i++)
{
cout << setw(6) << people[i] << " ";
for(int j = 1; j <= cnt; j++)
cout << setw(6) << ljjz[i][j] << " ";
cout << endl;
}
cout << endl << endl << "最短路径输出" << endl << endl;
//----------------------测试用-------------------------------*/
//5、求出 ljjz[0][j]的最小值,输出(j from 1 to cnt)
int minx = ljjz[0][1];
for(int j = 2; j <= cnt; j++){
if(minx > ljjz[0][j])
minx = ljjz[0][j];
}
cout << minx << endl;
/*------------------foled最短路径5行代码-----------------------//
//迭代条件:如果 i->j 的间接距离([i][j])大于间接路径距离(i->k->j,[i][k]+[k][j]),
//将更小的间接路径距离赋给[i][j]
for(int i = 1; i <= cnt; i++){
for(int j = 1; j <= cnt; j++){
for(int k = 1; k <= cnt; k++){
if(ljjz[i][j] > (ljjz[i][k] + ljjz[k][j]))
ljjz[i][j] = ljjz[i][k] + ljjz[k][j];
}
}
}
//----------------------------------------*/
/*----------------------测试用-------------------------------//
//2.3 输出ljjz[][],检查数据输入环节是否正确。
cout << endl << endl << "邻接矩阵输出" << endl << endl;
for(int i = 0; i <= cnt; i++)
{
cout << people[i] << " ";
for(int j = 1; j <= cnt; j++)
cout << ljjz[i][j] << " ";
cout << endl;
}
//----------------------测试用-------------------------------*/
return 0;
}