数据结构-二叉树的添加和遍历-C++实现

(文章遍历算法参考自该链接

一、树

1.1 Concept: tree is a finite set of nodes.
  • Child: all nodes except the parents node.

  • Parents:the original root.

  • Degree: its value is equal to how many children it has.

  • Leaf(terminal node): leaf node has no child.

  • Root(non-terminal node): all nodes except leaf nodes.

  • Ordered tree: if nodes in the same level can not change their positions, then this tree is an ordered tree.

  • Unordered tree:if nodes in the same level can change their positions and it will not influence the logic, then it’s called unordered tree.

  • Ancestor:it’s a generic term, all the elders nodes of one node, including their grandparents nodes and even higher nodes.

  • Offspring: it’s a genetic term, too. All the offsprings of one root.

  • degree:

    • node degree: equal to which layer it’s at in the tree.
    • tree degree:the number of the deepest layer of one tree.
  • Binary tree: all nodes have degrees less than or equal to two!

  • Traverse of binary tree:

    • preorder traverse: divided by access order. first access the root node is preorder traverse.
    • inorder line travers: secondly access the root node
    • postorder traverse: finally access the root node.

< 以下补充性 文字 来着 严蔚敏《数据结构(C语言版)》>


1.2 其他概念

  • 满二叉树:一颗深度为k且有2k12^k-1个结点的二叉树称为满二叉树;通俗理解就是除了最后一层,其他每一层的结点全部都有左右结点。
  • 完全二叉树:当且仅当每一个结点都与深度为k的满二叉树中编号为从1至N的结点一一对应时。也就是完全二叉树除了最后一层,其他层都是满的,并且最后一层与满二叉树相比缺少的结点,在编号上与满二叉树完全相同。
    • (1) 具有N个结点的完全二叉树的深度为log2n+1\lfloor log_2n\rfloor+1;
    • (2) 对于一颗有n个结点的完全二叉树,则对其任一结点i,有
      • 如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲parent的编号为i/2\lfloor i/2 \rfloor;
      • 如果2i>n2i>n,则结点i无左孩子,或者说其为叶子结点;否则其左节点编号为2i2i.

1.3二叉树的存储结构

  • 顺序存储结构:按照顺序将所有结点的值存储,不存在的结点用0代替。
  • 链式存储结构:
    • 二叉链表:数据+左孩子指针+右孩子指针
    • 三叉链表:数据+左孩子指针+右孩子指针+父结点指针
    • 在含有n个结点的二叉链表中有n+1个空链域(左孩子指针或右孩子指针为空)

1.4 遍历二叉树

  • 每个节点被访问一次,且仅访问一次;
  • 前序遍历:根->左->右
  • 中序遍历:左->根->右(最常用)
  • 后续遍历:左->右->根
  • 对于多层的的树,按照不同遍历方法,如果被访问的子结点还有左/右孩子结点,则按照对应访问顺序先访问完其下一级,如果下一级还有子结点,则继续深挖,先把最深层次的结点访问完毕再访问最初结点的下一个该访问的结点。
  • 比如:

        3                       前序遍历;3-2-7-6-4-1-9
     /     \                    
    2       4                   中序遍历:7-2-6-3-1-4-9
   / \     / \
  7   6   1   9                 后续遍历:7-6-2-1-9-4-3

  • 遍历算法一:
    • 递归算法:
    • 非递归算法一:
    • 非递归算法二:

1.5 树的存储结构

  • 双亲表示法:以一组连续空间存储树的结点,同时在每个结点种附加一个指示其双亲结点在链表中的位置:

    • 该方法在找parent中可以快速实现,实现ROOT(x)的操作,但是求结点的孩子时需要遍历整个结构

          R
       /  |  \
      A   B    C
    /  \
    D   E
    
    数组下标 结点 父节点
    0 R -1
    1 A 0
    2 B 0
    3 C 0
    4 D 1
    5 E 1

  • 孩子表示法

    • 将每个结点的孩子结点排列起来,以单链表存储。n个头指针又组成一个线性表;
    • 与双亲表示法相反,,该操作便于涉及孩子的操作的实现,却不利于PARENT(x)的操作;
    • 可以将双亲和孩子表示法结合起来。

            R
        /   |   \
        A   B    C
      /  \       |
     D    E      F
               / | \
              G  H  K
    
    数组下标 父结点 结点 子结点链表
    0 4 A ->(3)->(5)->X
    1 4 B X
    2 4 C ->(6)->X
    3 0 D X
    4 -1 R-1 ->(3)->(5)->x
    5 0 E X
    6 2 F ->(7)->(8)->(9)->X
    7 6 G X
    8 6 H X
    9 6 K X

  • 孩子兄弟表示法(利用该结构可以导出森林-树-二叉树之间的转换):

  • 又称二叉链表法,以二叉链表作为树的存储结构。链表中结点的两个链域分别指向该结点的第一个孩子结点和下一个兄弟结点。分别命名为firstchild 和 nextsibling域


          R
      /   |   \
      A   B    C
    /  \       |
   D    E      F
             / | \
            G  H  K
孩子兄弟表示法为:

        R
       /   
      A       
    /   \     
   D     B    
    \     \   
     E     C  
          /
         F
        /
       G 
        \
         H
          \
           K

  • 森林-树与二叉树的转换
  • 由上图其实不难得出,孩子兄弟表示法可以将树转变为二叉树,或者二叉树转变为树。森林与二叉树的转换也是类似,只不过把所有树的首结点作为相互的sibling。
    < 以下补充性 文字 来自《算法导论》>
1.6 删除函数

在这个代码中没有实现删除函数,因为严的数据结构没有介绍删除二叉树一个结点的规则,然后在《算法导论》这本书中有讲,摘录如下,感觉实现起来也不太难,所以以下代码中没有删除函数。

假设删除结点z:
(1)如果z没有孩子结点,则直接删除,并修改其父节点指向NULL;
(2)如果z有一个孩子,则将孩子提升到树中z的位置,并修改z的父节点将其孩子指针替换为新z;
(3)如果z有两个孩子,在z的右子树中找一个结点,然后让其在z的位置。并将其左右指针指向z的左右孩子。(这条还需要分几种情况,具体参照《算法导论》P167)

二、二叉树的编码

2.1二叉树顺序遍历-数组实现

数组与树之间的转换: int tree[n] 3 5 8 2 6 9 7 //用0来表示此处无节点
父节点下标*2+1=该点左节点;
父节点下表*2+1=该点右节点;
        3(0)

    5(1)    8(2)

2(3)  6(4)  9(5) 7(6)

****************************
应该有的函数:
树的创建;
销毁树
根据索引寻找节点
添加节点
删除节点
遍历
*****************************

2.2 二叉树的链表编码

  • 一般根节点不存有意义的值;
* 每个节点所包含的信息:索引;数据;左孩子指针;右孩子指针;父节点指针。
* 一个树所具有的函数:
Tree();//创建
~Tree();//销毁
search_node(int index);//搜索结点
add_node(int index, int direction, Node* node)//在哪个结点的哪个子树上,添加什么结点。
pre_node();//前序遍历
mid_node();//中序遍历
post_node();//后序遍历

代码如下:

//Node.h
#pragma once
class Node
{
public:
	Node();
	Node(int i, int d);
	~Node();
	Node* search_node(int nodeIndex);
	bool t_mid_tra();
	void t_pre_tra();//前序遍历
	void t_post_tra();//后序遍历
	int data;
	int index;
	Node* lchild;
	Node* rchild;
	Node* parent;
private:

};
//Node.cpp
#include"Node.h"
#include<iostream>
using namespace std;
#include<stack>
Node::Node()
{
	int data=0;
	int index=0;
	Node* lchild=NULL;
	Node* rchild=NULL;
	Node* parent=NULL;
}
Node::Node(int i, int d) :index(i), data(d) {}
Node* Node::search_node(int nodeIndex)
{
	stack<Node*> s;
	Node* p = new Node;
	p = this;
	while (!s.empty() || p)
	{
		if (p)
		{
			s.push(p);
			p = p->lchild;
		}
		else
		{
			p = s.top();
			s.pop();
			if (p->index == nodeIndex)
			{
				//cout << p->index << " " << p->data << endl;
				return p;
			}
			p = p->rchild;
		}
	}
	return NULL;
	
}
bool Node::t_mid_tra()
{
	stack<Node*> s;
	Node* p = new Node;
	p = this;
	while (!s.empty() || p)
	{
		if (p)
		{
			s.push(p);
			p = p->lchild;
		}
		else
		{
			p = s.top();
			s.pop();
			cout <<p->data << endl;
			p = p->rchild;
		}
	}
	return true;
}
void Node::t_pre_tra()
{
	stack<Node*> s;
	Node* p = new Node;
	p = this;
	while (!s.empty() || p)
	{
		if (p)
		{
			s.push(p);
			cout << p->data << endl;
			p = p->lchild;
		}
		else
		{
			p = s.top();
			s.pop();
			p = p->rchild;
		}
	}
}//前序遍历
void Node::t_post_tra()
{
	Node* p_last_node = new Node;
	Node* p_current = new Node;
	p_current = this;
	stack<Node*> s;
	while (p_current)
	{
		s.push(p_current);
		p_current = p_current->lchild;
	}
	while (!s.empty())
	{
		p_current = s.top();
		s.pop();
		if (p_current->rchild == NULL || p_current->rchild == p_last_node)
		{
			cout << p_current->data << endl;
			p_last_node = p_current;
		}
		else
		{
			s.push(p_current);
			p_current = p_current->rchild;
			while (p_current)
			{
				s.push(p_current);
				p_current = p_current->lchild;
			}
		}
	}

}//后序遍历
Node::~Node()
{
}
//Tree.h
#pragma once
#include<iostream>
using namespace std;
#include"Node.h"

class Tree
{
public:
	Tree();
	~Tree();
	//static int numb;//统计元素的序号
	Node* t_search_node(int nodeIndex);//搜索结点,over,利用先序遍历搜索结点,区别就是遇到想要的结点即返回,不再继续遍历
	bool t_add(int index, bool direction, Node* node);//添加元素
	bool t_delete(int index);//删除元素
	
	//非递归遍历
	void t_mid_tra();//中序遍历,over
	void t_pre_tra();//前序遍历
	void t_post_tra();//后序遍历

private:
	Node* t_head;
};

//Tree.cpp
#include"Tree.h"

Tree::Tree()
{
	t_head = new Node();
}
Node* Tree::t_search_node(int nodeIndex)
{
	return t_head->search_node(nodeIndex);
}
Tree::~Tree()
{
}
void Tree::t_mid_tra()
{
	 t_head->t_mid_tra();
}
bool Tree::t_add(int index, bool direction, Node* node)
{
	Node* node1 = new Node();
	node1 = this->t_search_node(index);
	if (node1 == NULL)
	{
		return false;
	}
	else
	{
		if (direction == 1)//右
		{
			if (node1->rchild == NULL)
			{
				node1->rchild = node;
				return true;
			}
			else
			{
				Node* l_node = node1->lchild;
				Node* r_node = node1->rchild;
				node->lchild = l_node;
				node->rchild = r_node;
				node1->rchild = node;
				return true;
			}
		}
		else//左
		{
			if (node1->lchild == NULL)
			{
				node1->lchild = node;
				return true;
			}
			else
			{
				Node* l_node = node1->lchild;
				Node* r_node = node1->rchild;
				node->lchild = l_node;
				node->rchild = r_node;
				node1->lchild = node;
				return true;
			}
		}
	}
}//添加元素
void  Tree::t_pre_tra()
{
	t_head->t_pre_tra();
}//前序遍历
void  Tree::t_post_tra()
{
	t_head->t_post_tra();
}//后序遍历
//main.cpp
#include"Tree.h"

int main()
{
	Tree* t = new Tree;
	Node* n1 = new Node(1, 13);
	Node* n2 = new Node(2, 3);
	Node* n3 = new Node(3, 4);
	Node* n4 = new Node(4, 6);
	Node* n5 = new Node(5, 8);
	Node* n6 = new Node(6, 11);
	//t->t_search_node(0);
	t->t_add(0, 1, n1);
	t->t_add(0, 0, n2);
	t->t_add(2, 1, n3);
	t->t_add(2, 0, n4);
	t->t_mid_tra();
	cout << endl;
	t->t_pre_tra();
	cout << endl;
	t->t_post_tra();
	return 0;
}



posted on 2019-12-22 20:50  Nancy_Fighting  阅读(398)  评论(0编辑  收藏  举报

导航