Data Structure and Algorithm Fundamentals (Wang Zhuo) (19): Implementation of Traversal Binary Tree Algorithm

Table of contents

Definition of binary linked list and triple linked list:

The specific operation of traversing the binary tree:

Final Results:

Implementation of traversal:

Recursive traversal program implementation:

Code modules for three traversal algorithms

For the sake of brevity in the document and to avoid repeating it, we only write the algorithm of pre-order traversal here: (Just switch the order of the statements in the in-order and post-order)

Non-recursive traversal program algorithm implementation:

Preconditions (for binary trees and sequential stacks):

Recursive traversal program algorithm implementation:

Take inorder traversal as an example:

Ideas:

ISSUES:

The finished program without constructing new variables:

The final program product for constructing new variables: (standard answer)

Binary tree level traversal algorithm:

Core ideas:

Preconditions: (original version)

Function implementation: (original)

ISSUES:


Definition of binary linked list and triple linked list:

#include<iostream>
using namespace std;

typedef int Status;
#define MAXTSIZE 100

typedef char TElemtype;//Tree Elemtype
//根据需求情况修改
typedef TElemtype SqBiTree;//sequence binary tree
//binary:二进制的; 二元的; 由两部分组成的;
SqBiTree bt;//binary tree

struct BiNode//二叉链表存储结构
{
	TElemtype data;
	struct
		BiNode* lchild, * rchild;
	//左右孩子指针
};
typedef BiNode * BiTree;

struct TriNode//trinary:三元的
{
	TElemtype data;
	struct
		BiNode* lchild, * rchild,* parent;
};
typedef TriNode* TriTree;

int main()
{

}

The specific operation of traversing the binary tree:

Final Results:

Implementation of traversal:

Recursive traversal program implementation:

Code modules for three traversal algorithms

For the sake of brevity in the document and to avoid repeating it, we only write the algorithm of pre-order traversal here: (Just switch the order of the statements in the in-order and post-order)

void Pre(BiTree& T)
{
	if (T != NULL) 
	{
		cout << T->data;
		//printf("%d\t{,T->data);
		Pre(T->lchild);
		Pre(T->rchild);
	}
}

//注意Pre函数必须要写在遍历函数前面

Status PreOrderTraverse(BiTree T)
{
	if (T == NULL)
		return 0;//空二叉树
	else
	{
		Pre(T);
		//visit(T);//访问根结点
		PreOrderTraverse(T->lchild);//递归遍历左子树
		PreOrderTraverse(T->rchild); //递归遍历右子树
	}
}

Note:

//What is written on the PPT is
//void Pre(BiTree* T)
//But our BiTree is already a pointer type, so there is no need to add more

Here:
void Pre(BiTree& T)

void Pre(BiTree T)

It will be all right


Non- recursive traversal program algorithm implementation:

Preconditions (for binary trees and sequential stacks):

//顺序栈
#include<iostream>
using namespace std;
#include<stdlib.h>//存放exit
#include<math.h>//OVERFLOW,exit

#define TRUE        1
#define FALSE       0
#define OK          1
#define ERROR       0
#define INFEASIBLE  -1
//#define OVERFLOW   -2   

#define MAXlength 100  
//可按需修改,PPT中写的是MAXlength

typedef int Status;
typedef char  SElemType; 
typedef char TElemtype;//根据需求情况修改

struct SqStack
{
	SElemType* base; //栈底指针  
	SElemType* top;//栈顶指针
	int stacksize; //栈可用最大容量
};

//二叉树
#define MAXTSIZE 100

typedef TElemtype SqBiTree;//sequence binary tree
//binary:二进制的; 二元的; 由两部分组成的;
SqBiTree bt;//binary tree

struct BiNode//二叉链表存储结构
{
	TElemtype data;
	struct
		BiNode* lchild, * rchild;
	//左右孩子指针
};
typedef BiNode* BiTree;

//函数
Status InitStack(SqStack& S)//构造一个空栈
{
	S.base = new SElemType[MAXlength];
	//或
	//S.base = (SElemType*)malloc(MAXlength * sizeof(SElemType));
	if (!S.base) exit(OVERFLOW);// 存储分配失败
	S.top = S.base;
	//栈顶指针等于栈底指针
	S.stacksize = MAXlength;
	return true;
}

Status StackEmpty(SqStack S)
{
	// 若栈为空,返回TRUE;否则返回FALSE 
	if (S.top == S.base)
		return TRUE;
	else
		return FALSE;
}

int StackLength(SqStack S)
{
	return S.top - S.base;
}

Status ClearStack(SqStack S)//清空顺序栈
{
	if (S.base)
		S.top = S.base;
	return OK;
}

Status DestroyStack(SqStack& S)//销毁
{
	if (S.base)
	{
		delete S.base;
		S.stacksize = 0;
		S.base = S.top = NULL;
	}
	return OK;
}

Status Push(SqStack& S, SElemType e)
{
	if (S.top - S.base == S.stacksize)
		//不是MAXlength
		return OVERFLOW;
	*S.top = e;
	S.top++;
	//也可以写成:
	//*S.top++ = e;
	return true;
}

Status Pop(SqStack& S, SElemType& e)
//若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;	否则返回ERROR
{
	if (S.top == S.base) // 等价于 if(StackEmpty(S))
		return UNDERFLOW;//ERROR;
	e = *S.top;
	S.top--;
	//e = *--S.top;
	return true;
}

Recursive traversal program algorithm implementation:

Take inorder traversal as an example:

Ideas:

There is a (root) node:

  1. Store the root node on the stack
  2. Visit (pointer to) the left subtree
  3. Output the left subtree (after thinking about it later, I think it is wrong, if this is the case, the first output is the left child of the second layer anyway)

The (root) node is empty:

  1. Output the root node of the previous layer (at the top of the stack)
  2. The pointer points to the right subtree of the root node

Project 1:

Status InOrderTraverse(BiTree T)
{
	BiTree p=T;//用于访问结点的指针
	SqStack S;
	InitStack(S);
	if (p)//p不为空,有(根)结点
		//另外这里注意,我们无法构造定义出“空结点/空树”,不要浪费力气了
	{
		Push(S, p->data);//		把根结点存到栈里
		p->lchild;//		访问(指针指向)左子树
	}
	else//(根)结点为空
	{
		Pop(S, p->data);
		cout << p->data;//		输出上一层的根节点(位于栈顶)
		p = p->rchild;//指针指向根节点的右子树

	}
	return true;	
}

ISSUES:

1. You also need to:

Finally, add a limit condition to determine the end of the program (stop exiting the loop):

After all the nodes are output, the program stops running, that is to say:

The stack is empty and the pointer is empty

2. Is it necessary to construct a new variable q to store the root node of the stack?

Is it necessary to construct a variable to store the root node when popping the stack, just like (and) the PPT answer?

If not like the above Project 1:

I feel like there's nothing wrong with:

During the Pop process, the original value of p is overwritten, but the final result of the two writing methods seems to be that p points to the right subtree.

But these are all my own guesses, doubts, I hope everyone can see if there is any problem

It's (alternatively) worth mentioning though:

If you do not construct a new variable q, the following warning will appear: [But if you construct a new variable q, the following warning will also appear, so it is useless hahaha]

The finished program without constructing new variables:

Status InOrderTraverse(BiTree T)
{
	BiTree p = T;//用于访问结点的指针
	SqStack S;
	InitStack(S);
	while (p || StackEmpty(S))
	{
		if (p)//p不为空,有(根)结点
				//另外这里注意,我们无法构造定义出“空结点/空树”,不要浪费力气了
		{
			Push(S, p->data);//		把根结点存到栈里
			p->lchild;//		访问(指针指向)左子树
		}
		else//(根)结点为空
		{
			Pop(S, p->data);
			cout << p->data;//		输出上一层的根节点(位于栈顶)
			p = p->rchild;//指针指向根节点的右子树
		}
	}	
	return true;	
}

The final program product for constructing new variables: (standard answer)

Status InOrderTraverse(BiTree T)
{
	BiTree p = T;
	SqStack S;
	InitStack(S);
	while (p || StackEmpty(S))
	{
		if (p)
		{
			Push(S, p->data);
			p->lchild;
		}
		else
		{
			BiTree q = NULL;
			Pop(S, q->data);
			cout << q->data;
			p = q->rchild;
		}
	}	
	return true;	
}

Annotated version:

Status InOrderTraverse(BiTree T)
{
	BiTree p = T;//用于访问结点的指针
	SqStack S;
	InitStack(S);
	while (p || StackEmpty(S))
	{
		if (p)//p不为空,有(根)结点
				//另外这里注意,我们无法构造定义出“空结点/空树”,不要浪费力气了
		{
			Push(S, p->data);//		把根结点存到栈里
			p->lchild;//		访问(指针指向)左子树
		}
		else//(根)结点为空
		{
			BiTree q = NULL;//用于访问结点的指针
			Pop(S, q->data);
			cout << q->data;//		输出上一层的根节点(位于栈顶)
			p = q->rchild;//指针指向根节点的右子树
		}
	}	
	return true;	
}

Binary tree level traversal algorithm:


Core ideas:

We write a hierarchical traversal algorithm here, which is different from the in-order traversal we wrote earlier.

not via:

  1. storage root node
  2. Let the pointer keep looping
  3. Visit the left subtree and output the left subtree

as well as

  1. output root node
  2. Visit and output the right subtree

To achieve access to traverse all nodes of the entire tree, but through continuous looping:

  1. Dequeue (queue head node)

    Note: In the process of dequeuing in the first step of each cycle
    At the same time (during the process of dequeuing), we also assign the head node of the queue to the pointer (a)

    That is to say, in the process of each cycle, a will continue to change, constantly pointing to the head node at the beginning of the cycle (the first node of the queue)

  2. Output queue head node (root node)

  3. Enqueue the left child (if any)

  4. Enqueue the right child (if any)


    Preconditions: (original version)

#include<iostream>
using namespace std;
#include<stdlib.h>//存放exit

#define TRUE        1
#define FALSE       0
#define OK          1
#define ERROR       0
#define INFEASIBLE  -1
#define OVERFLOW   -2   

typedef char QElemType;
typedef int Status;         //函数调用状态
#define MAXQSIZE 100  //初始大小为100,可按需修改

struct QNode
{
    QElemType data;
    QNode* next;
};
typedef QNode* QuenePtr;//Pointer

struct LinkQueue
{
    QuenePtr front; // 队头指针
    QuenePtr rear; // 队尾指针
   // QNode* front; // 队头指针
    //QNode* rear; // 队尾指针
};


typedef QElemType TElemtype;//根据需求情况修改
typedef TElemtype SqBiTree;//sequence binary tree
//binary:二进制的; 二元的; 由两部分组成的;
SqBiTree bt;//binary tree

struct BiNode//二叉链表存储结构
{
    TElemtype data;
    struct
        BiNode* lchild, * rchild;
    //左右孩子指针
};
typedef BiNode* BiTree;

struct TriNode//trinary:三元的
{
    TElemtype data;
    struct
        BiNode* lchild, * rchild, * parent;
};
typedef TriNode* TriTree;

Status InitQueue(LinkQueue& Q)//初始化
{
    Q.front = Q.rear = new QNode;
    if (!Q.front)
        return false;
    Q.rear->next = NULL;
    return true;
}

Status DesQueue(LinkQueue& Q)//销毁
{
    while (Q.front)
    {
        QNode* p = Q.front->next;
        delete (Q.front);
        Q.front = p;
    }
    //也可以直接指定指针rear暂时储存Q.front->next的地址,反正他放在这闲着也没事
    //Q.rear=Q.front->next; free(Q.front); Q.front=Q.rear;
    return OK;
}

Status EnQueue(LinkQueue& Q, QElemType e)//入队
{
    QNode* p = new QNode;
    //QNode* p = (QuenePtr)malloc(sizeof(QNode));
    p->data = e;
    p->next = NULL;
    Q.rear->next = p;
    Q.rear = p;
    return OK;
}

Status DeQueue(LinkQueue& Q, QElemType e)//出队
{
    if (Q.front == Q.rear) return ERROR;
    QNode* p = Q.front->next;
    e = p->data;//保存删除的信息
    Q.front->next = p->next;
    if (Q.rear == p)
        Q.rear = Q.front;
    delete p;
    return true;
}

Status GetHead(LinkQueue Q, QElemType& e)
{
    if (Q.front == Q.rear) return ERROR;
    e = Q.front->next->data;
    return OK;
}

Status QueneEmpty(LinkQueue& Q)
{
    if (Q.front == Q.rear)
        return true;
    else
        return false;
 }

Function implementation: (original)

void LevelOrder(BiNode* b)//层次遍历
{
    BiNode* a = b;
    LinkQueue Q;
    InitQueue(Q);
    EnQueue(Q, b->data);
    //注意:不是    EnQueue(Q, a->data);     !!!!!!
    //把头结点放进队列(入队)
    while (!QueneEmpty(Q))
    {
        DeQueue(Q,a->data);
        cout << a->data;
        if(a->lchild)
            EnQueue(Q, a->lchild->data);
        if (a->rchild)
            EnQueue(Q, a->rchild->data);
    }
}

ISSUES:

(1):    BiNode* a = b;

    In fact, I didn't want to assign an initial value to him here, but if I don't do it, I will report an error. whereas if I write

    BiTree a = NULL;

Although the format will not report an error, the following warning will also appear:


As for the standard answer on the PPT, I think it’s a pile of shit, so you don’t need to read it, just look at my version.

Guess you like

Origin blog.csdn.net/Zz_zzzzzzz__/article/details/129633127
Recommended