14.4二叉树层次建树

Posted su-1007

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了14.4二叉树层次建树相关的知识,希望对你有一定的参考价值。

创建function函数

//
// Created by 93757 on 2023/3/21.
//

#ifndef INC_1_TREE_FUNCTION_H
#define INC_1_TREE_FUNCTION_H
#include <stdio.h>
#include <stdlib.h>

typedef char BiElemType;
typedef struct BiTNode
    BiElemType c;   //c就是书籍上的data
    struct BiTNode *lchild;
    struct BiTNode *rchild;
BiTNode,*BiTree;

//tag结构体是辅助队列使用的
typedef struct tag
    BiTree p;     //树的某一节点的地址值
    struct tag *pnext;
tag_t,*ptag_t;
#endif //INC_1_TREE_FUNCTION_H

main函数

#include "function.h"

int main() 
    BiTree pnew;//用来指向新申请的树结点
    BiTree tree=NULL;//tree是指向树根的,代表树
    char c;
    ptag_t phead=NULL,ptail=NULL,listpnew=NULL,pcur=NULL;
    //abcdefghij
    while(scanf("%c",&c))
    
        if(c==\'\\n\')
        
            break;//读取到换行就结束
        
        //calloc申请的空间大小是两个参数直接相乘,并对空间进行初始化,赋值为0
        pnew= (BiTree)calloc(1,sizeof(BiTNode));
        pnew->c=c;
        listpnew= (ptag_t)calloc(1,sizeof(tag_t));//给队列结点申请空间
        listpnew->p=pnew;
        //如果是树的第一个结点
        if(NULL==tree)
        
            tree=pnew;//tree指向树的根结点
            phead=listpnew;//第一个结点即是队列头,也是队列尾
            ptail=listpnew;
            pcur=listpnew;//pcur要指向要进入树的父亲元素
        else
            //让元素先入队列
            ptail->pnext=listpnew;
            ptail=listpnew;
            //接下来把b结点放入树中
            if(NULL==pcur->p->lchild)
            
                pcur->p->lchild=pnew;//pcur->p左孩子为空,就放入左孩子
            else if(NULL==pcur->p->rchild)
            
                pcur->p->rchild=pnew;//pcur->p右孩子为空,就放入右孩子
                pcur=pcur->pnext;//当前结点左右孩子都有了,pcur就指向下一个
            
        
    
    return 0;

 

2023数据结构考研复习-树

2023数据结构考研复习-树(五)

树的定义

#include <iostream>
using namespace std;

struct Node 
    int data;
    Node *l, *r;

struct LinkNode 
    Node *data;  // 存指针
    LinkNode *next;

struct Queue 
    LinkNode *front, *rear;

先序遍历

// 函数调用栈,保存当前调用的函数信息 
void preOrder(Node root) 
    if (root) 
        cout << root.data << endl;
        preOrder(root.l);
        preOrder(root.r);
    

双while循环先序遍历:

public List<Integer> preorderTraversal(Node root) 
	List<Integer> list= new ArrayList<>();
	Stack<Node> stack = new Stack<>();
	while(!stack.isEmpty() || root != null) 
		while(root != null) 
			list.add(root.val);
			stack.push(root);
			root=root.left;
		
		root = stack.pop();
		root = root.right;
	
	return list;

压栈操作:

public List<Integer> preorderTraversal(Node root) 
	if (root == null) return new ArrayLIst<>();
	List<Integer> list= new ArrayList<>();
	Stack<Node> stack = new Stack<>();
	stack.push(root);
	while(!stack.isEmpty()) 
		Node cur = stack.pop();
		list.add(cur.val);
		if(cur.right != null) stack.push(cur.right);
		if(cur.left != null) stack.push(cur.left);

	
	return list;

后序遍历

双while循环后序遍历,左右中,中右左reverse就行:

public List<Integer> preorderTraversal(Node root) 
	List<Integer> list= new ArrayList<>();
	Stack<Node> stack = new Stack<>();
	while(!stack.isEmpty() || root != null) 
		while(root != null) 
			list.add(root.val);
			stack.push(root);
			root=root.right;
		
		root = stack.pop();
		root = root.left;
	
	return list;

双while循环后序遍历:

public List<Integer> preorderTraversal(Node root) 
	List<Integer> list= new ArrayList<>();
	Stack<Node> stack = new Stack<>();
	while(!stack.isEmpty() || root != null) 
		while(root != null) 
			stack.push(root);
			root=root.left;
		
		root = stack.pop();
		list.add(root.val);
		root = root.right;
	
	return list;

树的深度

// 树的深度
int treeDepth (Node root) 
    if (root == NULL)
        return 0;
    int l = treeDepth(root.l);
    int r = treeDepth(root.r);
    return l > r ? l+1 : r+1;

层次遍历

// 层次遍历
void LevelOrder(Node root) 
    Queue Q;
    initQueue(Q);
    Node p;
    add(root);
    while (!isEmpty(Q)) 
        poll(Q, p);
        visit(p);
        if (p.l)
            add(p.l);
        if (p.r)
            add(p.r);
    

线索二叉树

#include <iostream>
using namespace std;

struct Node 
    int data;
    Node *l, *r;
    int ltag, rtag;  // tag=0指向左右孩子结点,tag=1指向线索(l:前驱,r:后继)

// 辅助全局变量,用于查找结点p的前驱
Node *p;    // p指向目标结点
Node *pre = NULL;  // 指向当前访问的前驱
Node *final = NULL;  // 用于记录最终结果

// 中序线索化
void createInThread(Node *root) 
    pre = NULL;
    if (root != NULL)   // 非空二叉树才能线索化
        InThread(root);
        if (pre.r == NULL)
            pre.rtag = 1;  // 处理遍历的最后一个结点
    

// 函数调用栈,保存当前调用的函数信息 
void InThread(Node root) 
    if (root) 
        InThread(root.l);
        visit(root);
        InThread(root.r);
    

// 先序线索化
void InThread(Node root) 
    if (root) 
        visit(root);
        if (root.ltag == 0)  // lchild不是前驱线索
            InThread(root.l);
        InThread(root.r);
    

// 土方法找到中序前驱
void visit(Node *q) 
    if (q == p)  // 当前访问结点刚好是结点p
        final = pre;  // 找到p的前驱
    else
        pre = q;  // pre指向当前结点

// 线索化查找前驱
void visit(Node *q) 
    if (q.l == NULL) 
        q.l = pre;
        q.ltag = 1;
    
    if (pre != NULL && pre.r == NULL) 
        pre.r = q;
        pre.rtag = 1;
    
    pre = q;

// 找到以root为根的子树中,第一个被中序遍历的结点
Node *FirstNode (Node *root) 
    while (root.ltag == 0)
        root = root.l;
    return root;

// 中序线索二叉树中找到结点p的后继结点
Node *NextNode (Node *root) 
    // 右子树中最左下的结点
    if (root.rtag == 0)
        return FirstNode(root.r);
    else 
        return root.r;

// 对中序线索二叉树进行中序遍历(利用线索实现的非递归算法)
void Inorder(Node *root) 
    for (Node *p=Firstnode(root); p!= NULL; p=Nextnode(p))
        visit(p);

// 找到以root为根的子树中,最后一个被中序遍历的结点
Node *LastNode (Node *root) 
    while (root.rtag == 0)
        root = root.r;
    return root;

// 中序线索二叉树中找到结点p的前驱结点
Node *PreNode (Node *root) 
    // 右子树中最右下的结点
    if (root.ltag == 0)
        return LastNode(root.r);
    else 
        return root.l;

// 对中序线索二叉树进行逆向的中序遍历(利用线索实现的非递归算法)
void Rev Inorder(Node *root) 
    for (Node *p=Lastnode(root); p!= NULL; p=Prenode(p))
        visit(p);

双亲表示法

#include <iostream>
using namespace std;
#define Maxsize=100

// 双亲表示法
struct Node 
    int data;
    int parent;  // 双亲位置域
;
struct Tree 
    Node nodes[Maxsize];  // 双亲表示
    int n;  // 结点数
;
// 孩子表示法
struct Node 
    int data;
    Node *next;  // 双亲位置域
;
struct Box 
    int data;
    Node *firstChild;  // 双亲位置域
;
struct Tree 
    Box nodes[Maxsize];  // 双亲表示
    int n, r;  // 结点数和根的位置
;
// 孩子兄弟表示法
struct Node 
    int data;
    Node *firstchild, *nextsibling;  // 二叉链表,第一个孩子和有兄弟指针
;

综合应用

1. 后序遍历二叉树的非递归算法

算法思想:后序非递归遍历二叉树是先访问左子树,再访问右子树,最后访问根结点。结合图5.7来分析:①沿着根的左孩子,依次入栈,直到左孩子为空。此时栈内元素依次为ABD。读栈顶元素:若其右孩子不空且未被访问过,将右子树转执行①;否则,栈顶元素出栈并访问。栈顶D的右孩子为空,出栈并访问,它是后序序列的第一个结点;栈顶B的右孩子不空且未被访问过,E入栈,栈顶E的左右孩子均为空,出栈并访问;栈顶B的右孩子不空但已被访问,B出栈并访问;栈顶A的右孩子不空且未被访问过,C入栈,栈顶C的左右孩子均为空,出栈并访问;栈顶A的右孩子不空但已被访问,A出栈并访问。由此得到后序序列DEBCA。

在上述思想的第②步中,必须分清返回时是从左子树返回的还是从右子树返回的,因此设定一个辅助指针r,指向最近访问过的结点。也可在结点中增加一个标志域,记录是否已被访问。

void postOrder(BiTree T) 
    InitStack(S);
    p = T;
    r = NULL;
    while (p || !IsEmpty(S)) 
        if (p)   // 走到最左边
            push(S, p)
            p = p->lchild;
         else 
            GetTop(S, p);
            if (p->rchild && p->rchild != r)  // 左子树存在且未被访问过
                p = p->rchild;  // 转向右边
            else 
                pop(S, p);
                visit(p->data);
                r = p;
                p = NULL;
            
        
    

注意:每次出栈访问完一个结点就相当于遍历完以该结点为根的子树,需将p置NULL。

2. 二叉树的自下而上、从右到左的层次遍历算法

试给出二叉树的自下而上、从右到左的层次遍历算法。

一般的二叉树层次遍历是自上而下、从左到右,这里的遍历顺序恰好相反。算法思想:利用原有的层次遍历算法,出队的同时将各结点指针入栈,在所有结点入栈后再从栈顶开始依次访问即为所求的算法。具体实现如下:

1)把根结点入队列。
2)把一个元素出队列,遍历这个元素。
3)依次把这个元素的左孩子、右孩子入队列。
4)若队列不空,则跳到2),否则结束。

void InvertLevel(BiTree bt) 
    Stack s, Queue q;
    if (bt) 
        InitStack(s);
        InitQueue(q);
        Enqueue(Q, bt);
        while (Isempty(q) == false) 
            Dequeue(Q, p);
            push(s, p);
            if (p->lchild)
                Enqueue(p->lchild);
            if (p->rchild)
                Enqueue(p->rchild);
        
        while (Isempty(s) == false) 
            Pop(s, p);
            visit(p->data);
        
    

3. 非递归求二叉树高度

假设二叉树采用二叉链表存储结构,设计一个非递归算法求二叉树的高度。

采用层次遍历的算法,设置变量level记录当前结点所在的层数,设置变量last指向当前层的最右结点,每次层次遍历出队时与last指针比较,若两者相等,则层数加1,并让last指向下一层的最右结点,直到遍历完成。level的值即为二叉树的高度。

int Btdepth(BiTree T) 
    if (!T)
        return 0;
    int front = -1, rear = -1;
    int last = 0, level = 0;
    BiTree Q[MAXSIZE];
    Q[++rear] = T;
    Bitree p;
    while (front < rear) 
        p = Q[++front];
        if (p->lchild)
            Q[++rear] = p->lchild;
        if (p->rchild)
            Q[++rear] = p->rchild;
        if (front == last) 
            level++;
            last = rear;
        
    

4. 先序&中序序列建树

设一棵二叉树中各结点的值互不相同,其先序遍历序列和中序遍历序列分别存于两个一维数组A[1…n]和B[ 1…n]中,试编写算法建立该二叉树的二叉链表。

BiTree PreInCreate(int a[], int b[], int a1, int a2, int b1, int b2) 
    // 初始调用:a1 = a2 = 1, b1 = b2 = n;  a1,b1为先序的第一个和最后一个的下标
    root = (BiTNode*)malloc(sizeof(BiTNode));
    root->data = a[a1];  // 先序序列第一个即为根结点
    for (i = l2; b[i] != root->data; i++);  // 根结点在中序序列中的位置
    llen = i - a2;  // 中序中位置-起始位置即为左子树长度
    rlen = b2 - i; // 中序末尾位置-根结点所在位置即为右子树长度
    if (llen)  // 递归建立左子树,先序起始位置+1,末尾为起始位置+llen,中序起始位置不变,末尾为起始位置+llen-1(在先序中划分左子树)
        root->lchild = PreInCreate(a, b, a1+1, a1+llen, b2, b2+llen-1);
    else
        root->lchild = NULL;
    if (rlen) // 递归建立右子树,先序起始位置从尾巴位置前移右子树长度+1,末尾不变,中序起始位置从尾巴位置前移rlen+1,末尾不变(在先序中划分左子树)
        root->rchild = PreInCreate(a, b, b1-rlen+1, h1, h2-rlen+1, h2);
    else
        root->rchild = NULL;

    return root;  // 返回根结点指针

5. 判定完全二叉树

二叉树按二叉链表形式存储,写一个判别给定二叉树是否是完全二叉树的算法。

根据完全二叉树的定义,具有n个结点的完全二叉树与满二叉树中编号从1~~n的结点一一对应。

算法思想:采用层次遍历算法,将所有结点加入队列(包括空结点)。遇到空结点时,查看其后是否有非空结点。若有,则二叉树不是完全二叉树。

bool isComplete(BiTree T) 
    InitQueue(Q);
    if (!T)
        return 1;
    EnQueue(Q, T);
    while(!IsEmpty(Q)) 
        DeQueue以上是关于14.4二叉树层次建树的主要内容,如果未能解决你的问题,请参考以下文章

第04次作业-树

二叉树之建树

2023数据结构考研复习-树

2023数据结构考研复习-树

二叉树的建立和遍历(递归建树&层序遍历建树)

L2-011 玩转二叉树(建树+BFS)