数据结构与算法(二叉树)

二叉树的存储结构

二叉树的存储可分为两种:顺序存储结构和链式存储结构。

1.      顺序存储结构

把一个满二叉树自上而下、从左到右顺序编号,依次存放在数组内,可得到图6.8(a)所示的结果。设满二叉树结点在数组中的索引号为i,那么有如下性质。

(1) 如果i = 0,此结点为根结点,无双亲。

(2) 如果i > 0,则其双亲结点为(i -1) / 2 。(注意,这里的除法是整除,结果中的小数部分会被舍弃。)

(3) 结点i的左孩子为2i + 1,右孩子为2i + 2。

(4) 如果i > 0,当i为奇数时,它是双亲结点的左孩子,它的兄弟为i + 1;当i为偶数时,它是双新结点的右孩子,它的兄弟结点为i – 1。

(5) 深度为k的满二叉树需要长度为2 k-1的数组进行存储。

通过以上性质可知,使用数组存放满二叉树的各结点非常方便,可以根据一个结点的索引号很容易地推算出它的双亲、孩子、兄弟等结点的编号,从而对这些结点进行访问,这是一种存储二叉满二叉树或完全二叉树的最简单、最省空间的做法。

为了用结点在数组中的位置反映出结点之间的逻辑关系,存储一般二叉树时,只需要将数组中空结点所对应的位置设为空即可,其效果如图6.8(b)所示。这会造成一定的空间浪费,但如果空结点的数量不是很多,这些浪费可以忽略。

一个深度为k的二叉树需要2 k-1个存储空间,当k值很大并且二叉树的空结点很多时,最坏的情况是每层只有一个结点,再使用顺序存储结构来存储显然会造成极大地浪费,这时就应该使用链式存储结构来存储二叉树中的数据。

1.      链式存储结构

二叉树的链式存储结构可分为二叉链表和三叉链表。二叉链表中,每个结点除了存储本身的数据外,还应该设置两个指针域left和right,它们分别指向左孩子和右孩子(如图6.9(a)所示)。

当需要在二叉树中经常寻找某结点的双亲,每个结点还可以加一个指向双亲的指针域parent,如图6.9(b)所示,这就是三叉链表。

图6.10所示的是二叉链表和三叉链表的存储结构,其中虚线箭头表示parent指针所指方向。

二叉树还有一种叫双亲链表的存储结构,它只存储结点的双亲信息而不存储孩子信息,由于二叉树是一种有序树,一个结点的两个孩子有左右之分,因此结点中除了存放双新信息外,还必须指明这个结点是左孩子还是右孩子。由于结点不存放孩子信息,无法通过头指针出发遍历所有结点,因此需要借助数组来存放结点信息。图6.10(a)所示的二叉树使用双亲链表进行存储将得到图6.11所示的结果。由于根节点没有双新,所以它的parent指针的值设为-1。

双亲链表中元素存放的顺序是根据结点的添加顺序来决定的,也就是说把各个元素的存放位置进行调换不会影响结点的逻辑结构。由图6.11可知,双亲链表在物理上是一种顺序存储结构。

二叉树存在多种存储结构,选用何种方法进行存储主要依赖于对二叉树进行什么操作来确定。而二叉链表是二叉树最常用的存储结构,下面几节给出的有关二叉树的算法大多基于二叉链表存储结构。

6.3 二叉树的遍历

二叉树遍历(Traversal)就是按某种顺序对树中每个结点访问且只能访问一次的过程。访问的含义很广,如查询、计算、修改、输出结点的值。树遍历本质上是将非线性结构线性化,它是二叉树各种运算和操作的实现基础,需要高度重视。

6.3.1  二叉树的深度优先遍历


图6.12二叉树的递归定义


D


L


R

我们是用递归的方法来定义二叉树的。每棵二叉树由结点、左子树、右子树这三个基本部分组成,如果遍历了这三部分,也就遍历了整个二叉树。如图6.12所示,D为二叉树中某一结点,L、R分别为结点D的左、右子树,则其遍历方式有6种:

先左后右   先右后左

先序       DLR       DRL

中序       LDR       RDL

后序       LRD       RLD

这里只讨论先左后右的三种遍历算法。

如图6.13所示,在沿着箭头方向所指的路径对二叉树进行遍历时,每个节点会在这条搜索路径上会出现三次,而访问操作只能进行一次,这时就需要决定在搜索路径上第几次出现的结点进行访问操作,由此就引出了三种不同的遍历算法。

1.      先序遍历

若二叉树为非空,则过程为:

(1) 访问根节点。

(2) 先序遍历左子树。

(3) 先序遍历右子树。

图6.13中,先序遍历就是把标号为(1)的结点按搜索路径访问的先后次序连接起来,得出结果为:ABDECF。

2.      中序遍历

若二叉树为非空,则过程为:

(1) 按中序遍历左子树。

(2) 访问根结点。

(3) 按中序遍历右子树。

图6.13中,先序遍历就是把标号为(2)的结点按搜索路径访问的先后次序连接起来,得出结果为:DBEACF。

3.      后序遍历

若二叉树为非空,则过程为:

(1) 按后序遍历左子树。

(2) 按后序遍历右子树

(3) 访问根结点。

图6.13中,先序遍历就是把标号为(3)的结点按搜索路径访问的先后次序连接起来,得出结果为:DEBFCA。

using System;
using System.Collections.Generic;
namespace NET.MST.Thirteenth.BinaryTree
{
    class MainClass
    {
        /// <summary>
        /// 测试二叉树,和其中序、后序遍历
        /// </summary>
        static void Main(string[] args)
        {
            int[] data = new int[] { 6, 1, 3, 9, 2, 7, 11 };
            BinaryTree root = BinaryTree.GenerateBinaryTree(data);
            Console.Write("中序遍历:");
            root.InOrder();
            Console.Write("\r\n");
            Console.Write("后序遍历:");
            root.LastOrder();
            Console.Write("\r\n");
            Console.Read();
        }
    }
    /// <summary>
    /// 二叉树的实现
    /// </summary>
    partial class BinaryTree
    {
        //左子树指针
        private BinaryTree _left = null;
        //右子树指针
        private BinaryTree _right = null;
        //节点的值,这里以整数表示
        private int _value;
        /// <summary>
        /// 构造方法,左右子树设为null
        /// </summary>
        /// <param name="val">节点值</param>
        public BinaryTree(int val)
        {
            _value = val;
        }
        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="val">节点值</param>
        /// <param name="left">左子树指针</param>
        /// <param name="right">右子树指针</param>
        public BinaryTree(int val, BinaryTree left, BinaryTree right)
        {
            _value = val;
            _left = left;
            _right = right;
        }
        //读写属性
        public BinaryTree Left
        {
            get
            {
                return _left;
            }
            set
            {
                _left = value;
            }
        }
        public BinaryTree Right
        {
            get
            {
                return _right;
            }
            set
            {
                _right = value;
            }
        }
        public int Value
        {
            get
            {
                return _value;
            }
            set
            {
                _value = value;
            }
        }
    }
    /// <summary>
    /// 二叉树的生成和插入
    /// </summary>
    partial class BinaryTree
    {
        /// <summary>
        /// 静态方法用以从一个数组生成一颗二叉树
        /// 这里采用的是有序的插入
        /// </summary>
        /// <param name="data">输入数组</param>
        /// <returns>返回根节点</returns>
        public static BinaryTree GenerateBinaryTree(int[] data)
        {
            //确保数组非空
            if (data.Length <= 0)
                return null;
            //生成根节点
            BinaryTree root = new BinaryTree(data[0]);
            //确保需要生成左子树或者右子树
            if (data.Length <= 1)
                return root;
            //逐一插入整个数组
            for (int i = 1; i < data.Length; i++)
                root.InsertElement(data[i]);
            return root;
        }
        /// <summary>
        /// 有序地插入元素,插入的结果是中序遍历该二叉树可以获得一个有序序列
        /// </summary>
        /// <param name="val">需要插入的值</param>
        public void InsertElement(int val)
        {
            //需要插在左子树中
            if (val <= _value)
            {
                //左子树为空,插入新元素
                if (_left == null)
                {
                    BinaryTree node = new BinaryTree(val);
                    _left = node;
                }
                //左子树非空,递归
                else
                    _left.InsertElement(val);
            }
            //需要插在右子树中
            else
            {
                //右子树为空,插入新元素
                if (_right == null)
                {
                    BinaryTree node = new BinaryTree(val);
                    _right = node;
                }
                //右子树非空,递归
                else
                    _right.InsertElement(val);
            }
        }
    }
    /// <summary>
    /// 遍历二叉树
    /// </summary>
    partial class BinaryTree
    {
        /// <summary>
        /// 中序遍历二叉树
        /// </summary>
        public void InOrder()
        {
            //使用递归算法
            if (_left != null)
                _left.InOrder();
            Console.Write("{0},", _value);
            if (_right != null)
                _right.InOrder();
        }
        /// <summary>
        /// 后序遍历二叉树
        /// </summary>
        public void LastOrder()
        {
            //使用递归算法
            if (_left != null)
                _left.LastOrder();
            if (_right != null)
                _right.LastOrder();
            Console.Write("{0},", _value);
        }
    }
}

数据结构与算法(二叉树)

时间: 2024-10-26 09:59:26

数据结构与算法(二叉树)的相关文章

javascript数据结构与算法--二叉树(插入节点、生成二叉树)

javascript数据结构与算法-- 插入节点.生成二叉树 二叉树中,相对较小的值保存在左节点上,较大的值保存在右节点中 /* *二叉树中,相对较小的值保存在左节点上,较大的值保存在右节点中 * * * */ /*用来生成一个节点*/ function Node(data, left, right) { this.data = data;//节点存储的数据 this.left = left; this.right = right; this.show = show; } function sh

[数据结构与算法] 二叉树及其遍历方式

声明:原创作品,转载时请注明文章来自SAP师太技术博客:www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4289830.html 一.数据结构分类 (一)按逻辑结构 集合(无辑关系) 线性结构(线性表):数组.链表.栈.队列 非线性结构:树.图.多维数组 (二)按存储结构 顺序(数组)储结构.链式储结构.索引储结构.散列储结构 二.二叉树相关性质

数据结构与算法--二叉树(一)

1 基于二叉链表的有序二叉树 1.1 问题 BST是Binary Search Tree的缩写,译为二叉搜索树,或有序二叉树,是二叉树的一种,它的定义如下: 1)或者是一棵空树: 2)或者是具有下列性质的二叉树: I) 若左子树不空,则左子树上所有结点的值均小于它的根结点的值: II) 若右子树不空,则右子树上所有结点的值均大于它的根结点的值: III)左.右子树也分别为二叉排序树: BST在查找一个结点或插入一个结点时,具有极大的优势,速度非常快.是一种基础性数据结构,广泛应用于更加抽象的集合

数据结构与算法 —— 二叉树

二叉树 定义: 来自于百度百科. 在计算机科学中,二叉树是每个节点最多有两个子树的树结构.通常子树被称作"左子树"(left subtree)和"右子树"(right subtree).二叉树常被用于实现二叉查找树和二叉堆. 二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒.二叉树的第i层至多有2^{i-1}个结点:深度为k的二叉树至多有2^k-1个结点:对任何一棵二叉树T,如果其终端结点数为n_0,度为2的结点数为n_

小甲鱼数据结构和算法-----二叉树的构建和前序遍历

题目要求:建立二叉树并输出每个字符所在的层数.如下图要求输出 A 在第一层 B.C 在第二层 D.E在第三层 代码如下: #include <stdio.h> #include <stdlib.h> typedef struct BiTNode { char data; struct BiTNode *lchild,*rchild; }BiTNode,*BiTree; // 创建一棵二叉树,约定用户遵照前序遍历的方式输入数据 void CreateBiTree(BiTree *T)

数据结构和算法——二叉树

树1.树的优点有序数组: 查找很快,二分法实现的查找所需要的时间为O(logN),遍历也很快,但是在有序数组中插入,删除却需要先 找到位置, 在把数组部分元素后移,效率并不高. 链表: 链表的插入和删除都是很快速的,仅仅需要改变下引用值就行了,时间仅为O(1),但是在链表中查找数据却需要遍历所有的元素, 这个效率有些慢了.树的优点: 树结合了有序数组和链表的优点,可以实现快速的查找,也可以快速的删除,查找. 树的一些专用术语: 路径: 顺着连接节点的边从一个节点到另一个节点的,所经过的所有节点的

python数据结构与算法——二叉树结构与遍历方法

先序遍历,中序遍历,后序遍历 ,区别在于三条核心语句的位置 层序遍历  采用队列的遍历操作第一次访问根,在访问根的左孩子,接着访问根的有孩子,然后下一层 自左向右一一访问同层的结点 # 先序遍历 # 访问结点,遍历左子树,如果左子树为空,则遍历右子树, # 如果右子树为空,则向上走到一个可以向右走的结点,继续该过程 preorder(t):    if t:       print t.value       preorder t.L       preorder t.R # 中序遍历 # 从根

java数据结构和算法------二叉树基本操作

1 package iYou.neugle.tree; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 public class Binary_Tree<T> { 7 private Tree tree = new Tree(); 8 9 class Tree { 10 public T data; 11 public Tree left; 12 public Tree right; 13 } 14 15 public

js数据结构和算法---二叉树

原文: https://segmentfault.com/a/1190000000740261 //前序遍历 function preOrder(node) { if (node != null) { node.style.background = "black"; setTimeout(function () { preOrder(node.children[0]); },1500); setTimeout(function () { preOrder(node.children[1

js数据结构与算法——二叉树

function BinaryTree(){ var Node = function(key){ this.key = key; //值 this.left = null; //左箭头 this.right = null; //右箭头 } //根节点 var root = null; var insertNode = function(oldNode,newNode){ if(newNode.key < oldNode.key){ if(oldNode.left === null){ oldNo