树
-树没有节点—空树
-树的层次(根节点第一层)
-节点的度(节点的字数个数)----树的度(树中节点最大的度)
-树的节点是n,边数是n-1(每2个点连接1条边)
-节点的深度 (根节点(深度为1)自顶向下到该节点的深度值)
-节点的高度(最底层叶子结点(高度为1)自底向上逐层累加到该点的高度值)
-树的(节点最大)深度和树的(节点最大)高度,相等,但是某个具体节点不一定。
二叉树
递归定义:
二叉树没有根节点,空树
二叉树由根节点,左子树,右子树组成,左子树和右子树都是二叉树
二叉树和度为2的树的区别:
虽然每个节点的子节点个数都是不超过2的,
但是二叉树的左右子树是严格区分的,不能随意交换左子树和右子树的位置
满二叉树:
每1层的节点个数都到达了当层能到达的最大节点数
完全二叉树:
除了最下面一层,其余层的节点个数都到达了当层能到达的最大节点数
层次-----辈分
孩子/父亲/兄弟/祖先/子孙节点
二叉树的存储结构
struct node{
typename data;//数据域
node* lchild;//指向左子树根节点的指针
node* rchile;//指向右子树节点的指针
}
//由于在二叉树建立树之前,节点不存在,因此其地址一般设为NULL
node* root=NULL
//创建新的节点(往二叉树中插入节点),使用下面的函数
node * newNode(int v){
//生成一个新节点,v为节点权值
node * Node =new node;//申请一个node型变量的地址空间
Node->data=v;//节点权值为v
Node->lchild=Node->rchild=NULL;//初始状态下没有左右孩子
return Node;//返回新建节点的地址
}
//二叉树节点的查找和修改
//插入:在给定数据域的条件下,在二叉树中找到所有数据域为给定数据域的节点
//并将它们的数据域修改为给定的数据域
//数据域为int例子
void search(node *root,int x,int newdata){
if(root==NULL)
return;//空树
if(root->data==x)//找到数据域为x的节点,把他们修改为newdata
root->data=newdatda;
search(root->lchild,x,newdata);
search(rooot->rchild,x,newdata);
}
//二叉节点的插入
//insert函数将在二叉树中插入一个数据域为x的新节点
//注意 根节点指针要用引用,否则插入不成
void insert(node* &root,int x){
if(root==NULL){//空树 说明查找失败,插入位置是查找失败的位置
root=newNode(x);//创建一个新节点,root指向新节点
return;
}
if(由于二叉树的性质,x插在左子树){
insert(root->lchild,x);
}else{
insert(root->rchild,x);
}
}
//函数中需要新建节点,对二叉树结构做出修改,加引用
//修改原有节点内容/遍历树,不用加引用
//创建新节点之后,左右指针为NULL
//二叉树的创建
node* Create(int data[],int n){
node* root = NULL;//新建空节点root
for(int i=0;i<n;i++)
insert(root,data[i]);//将data[0]-data[n-1]插入二叉树
return root;//返回根节点
}
层次遍历
从根节点向下逐层进行遍历,且对同一层的节点从左到右遍历
层次遍历相当于对二叉树从根节点开始的广度优先搜索BFS
void LayerOrder(node* root){
queue<node*> q;//注意队列里面是存放地址
q.push(root);//1.将根节点地址入队
while(!q.empty()){
node *now = q.front();//2.取出队首元素
q.pop();
printf("%d",now->data);//访问队首元素
if(now->lchild!=NULL)q.push(now->lchild);
if(now->rchild!=NULL)q.push(now->rchild);
}
}
//广度优先(BFS)一般由队列实现,总是按层次的顺序进行遍历,基本写法如下(可作为模板)
void BFS(int s){
queue<int> q;
q.push(s);
while(!q.empty()){
取出队首元素top;
访问队首元素top;
将队首元素出队;
将top的下一层节点未曾入队的节点全部入队,并设置为已经入队;
}
}
//模板的每一个步骤的说明
//1.定义队列q,起点s入队
//2.写一个while循环,循环条件是q非空
//3.在while循环中,取出队首元素top,访问他(可以做任何事),访问完后将其出队
//4.将top的下一层节点中未曾入队的节点入队,并标记他们的层号为now的层号加一,并且设置这些入队的节点已经入过队
//5.返回2循环
//求给定矩阵中(1)块的个数
//枚举每一个位置的元素,如果是0,就跳过
//如果是1,就用BFS查询与该位置相邻的4个位置(前提是不过界)
//判断他们是否是1(如果某个相邻元素时1,就同样去查询与该位置相邻的4个位置,直到1访问完毕)
//为了防止走回头路,一般可以设置一个bool数组inq(in queue简写)来记录每一个位置是否在BFS中已经入过队
//广度搜索例子
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn=100;
int n,m;//矩阵大小n*m
int matrix[maxn][maxn];
bool inq[maxn][maxn]={false};//记录位置(x,y)是否已经入过队
struct node{
int x,y;
}Node;
int X[4]={0,0,1,-1};
int Y[4]={1,-1,0,0};
bool judge(int x,int y){//判断坐标(x,y)是否需要访问
//越界返回false
if(x>=n||x<0||y>=m||y<0)
return false;
//当时位置为0或者(x,y)已经入过队,返回false
if(matrix[x][y]==0||inq[x][y]==true)return false;
//以上条件不满足,返回true
return true;
}
//BFS函数访问位置(x,y)所在的块,将该块所有1的inq设置为true
void BFS(int x,int y){
queue<node> Q;//定义队列
Node.x=x,Node.y=y;//当前节点坐标是(x,y)
Q.push(Node);//将节点Node入队
inq[x][y]=true;//设置(x,y)入过队
while(!Q.empty()){
node top = Q.front();//取出队首元素
Q.pop();//队首元素出队
for(int i=0;i<4;i++){
int newX=top.x+X[i];
int newY=top.y+Y[i];
if(judge(newX,newY)){//如果新位置(newX,newY)需要访问
//设置Node的坐标是{newX,newY}
Node.x=newX;
Node.y=newY;
Q.push(Node);//将节点Node加入队列
inq[newX][newY]=true;
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);//6 7
for(int x=0;x<n;x++){
for(int y=0;y<m;y++){
scanf("%d",&matrix[x][y]);//读入01矩阵//xy代表下标,真正的值是你输入的数字01
}
}
int ans = 0;//存放块数
for(int x=0;x<n;x++){//枚举每一个位置
for(int y=0;y<m;y++){
//如果元素为1,则还没有入队
if(matrix[x][y]==1&&inq[x][y]==false){
ans++;//块数+1
BFS(x,y);//访问整个块,将该块所有1的inq都标记为true
}
}
}
printf("%d\n",ans);
return 0;
}