数据结构之树型结构1------树与二叉树

知识结构
在这里插入图片描述

1.树与二叉树

1.1树的定义与性质

树型结构与线性表都是一种数据结构,是由若干个结点和若干条边组成的数据结构。

涉及的概念有 :结点(node)、根结点(root)、叶子结点(leaf)、边、子结点、子树。

常用性质:
1.无结点时为空树;
2.层次从根结点开始算;
3.结点的子树数量是结点的度;
4.n个结点的树,定有n-1条边,反之亦成立;
5.叶子结点度为1,只有一个结点时,根结点同时也是叶子结点;
6.结点深度从上到下,结点高度从下到上;
7.森林是若干树的集合。

1.2二叉树的递归定义

递归定义:1.要么二叉树是空树;2.要么二叉树由左子树、右子树组成,且左子树、右子树都是二叉树。

满二叉树:每层节点都达到了该层节点能达到的最大结点数。

完全二叉树:与满二叉树结点对应,只可能在最后一层少。

1.3二叉树的存储结构与基本操作

1.3.1 一般二叉树使用链表定义。(二叉链表)

struct node {
	typename data;//数据域
	node *lchild;//左子树根结点
	node *rchild;//右子树根结点
};

二叉树建树前根结点不存在,地址一般设为node *root = NULL;如需要新建结点(如往二叉树插入结点时),可用该插入结点函数:

//生成一个新结点,V为结点权值
node* newNode ( int v ) {
	node* Node = new node;//申请一个Node型变量的地址空间 
	Node->data = v;
	Node->lchild = Node->rchild = NULL;
	
	return Node;           //返回新建结点地址 
} 

主要操作还是创销、增删、改查

1.3.2二叉树结点的查找、修改

给定数据域,在二叉树中找到所有数据域为给定数据域的结点,并将他们的数据域修改为给定数据域。
先判断当前结点是否是所需查找结点,如果是,对其进行修改操作;如果不是,则分别往该结点左孩子和右孩子递归,直到当前结点为NULL为止。

//二叉树结点的查找、修改
void Search ( node* root, int x, int newdata ){
//传入三个参数树、待查树、需改数 
	if ( root == NULL )
		return 0;
	if ( root->data == x ){
		root->data = newdata;
	}
	Search ( root->lchild, x, newdata );
	Search ( root->rchild, x, newdata );
} 

1.3.3二叉树结点的插入

由于二叉树形态众多,对于某个不知其特点的二叉树,难以给出具体插入方法。

//二叉树的插入
void Insert ( node* &root, int x ){  //注意加引用 
	if ( root == NULL ){            //空树,查找失败,即插入位置 
		root = newNode(x);
		return;
	}
	if ( 由二叉树性质,x应该插在左子树){
		Insert ( root->lchild, x ); //往左子树搜索(递归) 
	}else{
		Insert ( root->rchild ,x );//往右子树搜索(递归) 
	} 
} 

1.3.4二叉树的创建

二叉树的创建其实就是二叉树结点的插入,一般题目给出数据,可将之存入一个数组,再将数组元素一个一个插进二叉树,最终返回根结点指针root。也可边输入数据边插入结点。

//二叉树的创建
node* Creat ( int data[], int n ){ //数组传入数据,n为元素个数 
	node* root = NULL;             //新建空结点 root
	for ( int i=0; i<n; i++ ){
		Insert ( root, data[i] );
	}
	return root;       //返回根节点 
} 

注意root = NULL; 与 *root = NULL; 的区别结点不存在与结点存在但没有内容。

1.3.5完全二叉树的存储结构

完全二叉树可用大小为2的k次方的数组来存(K为完全二叉树最大宽度),1号位放根结点,每个结点的左右孩子编号都可以计算得出。

数组中元素存放的顺序,为该树层序遍历序列。

若结点左孩子编号的2倍大于结点总个数n,则该结点为叶子结点。

若结点编号x大于总结点个数n,则该节点为空。

2.二叉树的遍历(四种)

先序遍历(DFS)、中序遍历(DFS)、后序遍历(DFS)、层序遍历(BFS)

2.1先序遍历(根左右)

//先序遍历
void preorder ( node* root ) {
    if ( root == NULL ) {
        return;                  //到达空树,递归边界
    }
    printf( "%d\n", root->data );//访问根结点,如将其输出
    preorder ( root->lchild );
    preorder ( root->rchild );
}

2.2中序遍历(左根右)

//中序遍历
void inOrder ( node* root ){
    if ( root == NULL ){
        return;               //到达递归边界,空树
    }
    inOrder ( root->lchild );      //先访问左子树
    printf ( "%d\n", root->data );//访问根节点,打印
    inOrder ( root->rchild );
}

2.3后序遍历(左右根)

//后根遍历
void postorder ( node* root ){
    if ( root == NULL ){
        return;
    }
    postorder ( root->lchild );
    postorser ( root->rchild );
    printf ( "%d\n", root->data );
}

2.4层序遍历

2.4.1如何实现层序遍历

  1. 将根结点加入到队列q。
  2. 取出队首结点,访问它。
  3. 如果该结点有左孩子,将左孩子入队。
  4. 如果该结点有右孩子,将右孩子入队。
  5. 返回2,直到队列为空。
//层序遍历
void LayerOrder ( node* root ){
    queue < node* > q;              //队列中存放地址
    q.push ( root );                //根结点地址入队
    while ( !q.empty () ){
        node* now = q.front ();
        q.pop();
        printf ( "%d\n ", now->data );
        if ( now->lchild != NULL ) q.push (now->lchild);
        if ( now->rchild != NULL ) q.push (now->rchild);
    }
}

2.4.2如何计算结点层次

当要求计算结点的层次时,在定义二叉树结点时要加入记录层次的变量layer。

struct node {
	typename data;
	int layer;
	node* lchild;
	nose* rchild;
}

当根结点入队前,将根结点layer赋为1(或0)

在左(now->lchild)右(now->rchild)孩子入队前,将他们的层号都记为当前now层号加1.

void LayerOrder ( node* root ){
    queue < node* > q;   
    root->layer = 1;           //队列中存放地址
    q.push ( root );                //根结点地址入队
    while ( !q.empty () ){
        node* now = q.front ();
        q.pop();
        printf ( "%d\n ", now->data );
        if ( now->lchild != NULL ) {
        	now->lchild->layer = now->layer+1;
        	q.push (now->lchild);
        }
        if ( now->rchild != NULL ){
        	q.push (now->rchild);
        	now->rchild->layer = now->layer;
        }
    }
}

2.4.3给定先、中序列,如何重建二叉树

先序序列:根、左子树、右子树
中序序列:左子树、根、右子树

//先序序列[preL, preR], 中序序列[inL, inR]
node* create ( int preL, int preR, int inL, int inR ){
    if ( preL > preR ){
        return NULL;       //左大于右,序列不存在
    }
    node* root = new node;
    root->data = pre[preL];//先序第一个结点为根结点
    int k;
    for ( k=inL; k<=inR; k++ ){//在中序序列中找到根结点
        if ( in[k] == root->data ){
            break;
        }
    }
    int numLeft = k-inL;//左子树结点个数
    root->lchild = creat ( preL+1, preL+numLeft, inL, k-1 );//递归处理左子树
    r00t->rchild = creat ( preL+numLeft+1, preR, k+1, inR );//递归处理右子树
    
    return root;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值