二叉树学习:从零基础到代码实现

二叉树的定义:

二叉树是节点的有限集合,该集合或者为空集,或者是由一个根和两颗互不相交的、称为该根的左子树和右子树的二叉树组成。

二叉树的性质:

1、二叉树第i(i>=1)层上至多有2^(i-1)个结点。

2、高度为h的二叉树至多有2^(h)-1个结点。

3、包含n个元素的二叉树的高度至少为log2(n+1)向上取整。

4、任意一颗二叉树中,若叶节点的个数为n0,度为2的结点的个数为n2,则必有n0=n2+1.

5、高度为h的二叉树恰好有2^(h)-1个结点时称为满二叉树。满二叉树是完全二叉树,也是扩充二叉树。

6、一颗二叉树中,只有最下面两层结点的度可以小于2,并且最下面一层的叶节点集中在靠左的若干位置上,这样的二叉树称为完全二叉树。

7、扩充二叉树也称2-树,扩充二叉树中除叶子结点外,其余结点都必须有两个孩子。

8、具有n个结点的完全二叉树的高度为log2(n+1)向上取整。

9、假定对一颗有n个结点的完全二叉树中的结点,按从上到下,从左到右的顺序,从0到n-1编号,设树中某个结点的序号为i,0<=i<n,则有以下关系成立:(下面建二叉树的时候会用到)

(1)当i=0时,该节点为二叉树的根。

(2)若i>0,则该结点的双亲的序号为(i-1)/2向下取整。

(3)若2i+1<n,则该结点的左孩子的序号为2i+1,否则该结点无左孩子。

(4)若2i+2<n,则该结点的右孩子的序号为2i+2,否则该结点无右孩子。

C#构建二叉树

1、建立二叉树的类

class Node
    {
        private object _data;//本节点数据
        private Node _left;//左孩子
        private Node _right;//右孩子
        public object Data
        {
            get { return _left; }
            set { _data = value; }
        }
        public Node Left
        {
            get { return _left; }
            set { _left = value; }
        }
        public Node Right
        {
            get { return _right; }
            set { _right = value; }
        }
        public Node(object data)
        {
            this._data = data;
        }
        public override string ToString()
        {
            return _data.ToString();
        }
    }
2、构建二叉树

class BinaryTreeFull
{
    private Node _head;//头节点,是整个二叉树的入口
    private string cStr;//将传入的初始化字符串转换为全局变量
    public Node Head    //调用Head就相当于拿到了整棵树
    {
        get { return _head; }
        set { _head = value; }
    }
    public BinaryTreeFull()
    {
        _head = null;
    }
    public BinaryTreeFull(string constructStr)//默认以程序遍历构造二叉树
    {
        cStr = constructStr;//赋值给全局变量
        if (cStr[0] == '#')//#表示空节点
        {
            _head = null;
            return;//根节点为空
        }
        _head = new Node(cStr[0]);//把第一个字符加入二叉树,作为根结点
        Add(_head, 0);
    }
    private void Add(Node parent, int index)
    {
        int leftIndex = 2 * index + 1;
        if (leftIndex < cStr.Length)//存在左孩子
        {
            if (cStr[leftIndex] != '#')//不为空
            {
                parent.Left = new Node(cStr[leftIndex]);//加入该结点的左孩子
                Add(parent.Left, leftIndex);//继续查找
            }
        }
        int rightIndex = 2 * index + 2;
        if (rightIndex < cStr.Length)//存在右孩子
        {
            if (cStr[rightIndex] != '#')//不为空
            {
                parent.Right = new Node(cStr[rightIndex]);//加入该结点的右孩子
                Add(parent.Right, rightIndex);//继续查找
            }
        }
    }
}
递归的方法遍历二叉树:

1、先序遍历

public void PreOrder(Node node)
        {
            if (node != null)//只要结点不为空,就输出结点值,然后查找左结点和右结点
            {
                Console.Write(node);//我在开始
                PreOrder(node.Left);
                PreOrder(node.Right);
            }
        }
2、中序遍历

public void InOrder(Node node)
        {
            if (node != null)
            {
                InOrder(node.Left);
                Console.Write(node);//我在中间
                InOrder(node.Right);
            }
        }
3、后续遍历

public void AfterOrder(Node node)
        {
            if (node != null)
            {
                AfterOrder(node.Left);
                AfterOrder(node.Right);
                Console.Write(node);//我在最后
            }
        }
4、计算叶子结点个数(先序遍历)
public void CountLeaf(Node node, ref int count)//count用于返回结点个数
        {
            if (node != null)
            {
                if ((node.Left == null) && (node.Right == null))
                {
                    count++;
                }
                CountLeaf(node.Left, ref count);
                CountLeaf(node.Right, ref count);
            }
        }
5、计算节点数

public int Count(Node root)
        {
            if (root == null) return 0;
            return Count(root.Right) + Count(root.Left) + 1;
        }
6、计算二叉树的高度

public int Height(Node root)
        {
            int a, b;
            if (root == null) return 0;
            a = Height(root.Left);
            b = Height(root.Right);
            if (a > b)
            {
                return a + 1;
            }
            else
            {
                return b + 1;
            }
        }
7、复制二叉树

public Node CopyTree(Node root)
        {
            Node newroot;
            if (root == null)
            {
                newroot = null;
            }
            else
            {
                CopyTree(root.Left);
                CopyTree(root.Right);
                newroot = root;
            }
            return newroot;
        }
8、先序遍历建立二叉树

public static BinaryTreeFull CreateByPre(string s)
        {
            BinaryTreeFull tree = new BinaryTreeFull(s);//先以层序初始化一个树,再调整
            int _count = 0;
            Node node = tree.Head;
            Stack<Node> stack = new Stack<Node>();
            while (node != null || stack.Count > 0)
            {
                while (node != null)
                {
                    node.Data = s[_count++];
                    stack.Push(node);
                    node = node.Left;
                }
                if (stack.Count > 0)
                {
                    node = stack.Pop();
                    node = node.Right;
                }
            }
            return tree;
        }
9、中序遍历建立二叉树

public static BinaryTreeFull CreateByIn(string s)
        {
            BinaryTreeFull tree = new BinaryTreeFull(s);
            int _count = 0;
            Node node = tree.Head;
            Stack<Node> stack = new Stack<Node>();
            while (node != null || stack.Count > 0)
            {
                while (node != null)
                {
                    stack.Push(node);
                    node = node.Left;
                }
                if (stack.Count > 0)
                {
                    node = stack.Pop();
                    node.Data = s[_count++];
                    node = node.Right;
                }
            }
            return tree;
        }
10、后序遍历建立二叉树

public static BinaryTreeFull CreateByAfter(string s)
        {
            BinaryTreeFull tree = new BinaryTreeFull(s);
            int _count = 0;
            Node node = tree.Head;
            Node pre = tree.Head;
            //pre指针指向“之前出栈节点”,如果为null有问题,因为后序遍历中头结点肯定值最后被访问的
            Stack<Node> stack = new Stack<Node>();
            while (node != null || stack.Count > 0)
            {
                while (node != null)
                {
                    stack.Push(node);
                    node = node.Left;
                }
                if (stack.Count > 0)
                {
                    Node temp = stack.Peek().Right;
                    if (temp == null || temp == pre)
                    {
                        node = stack.Pop();
                        node.Data = s[_count++];
                        pre = node;
                        node = null;
                    }
                    else
                    {
                        node = temp;
                    }
                }
            }
            return tree;
        }
非递归方法遍历二叉树:

1、先序遍历

public void PreStackOrder()
        {
            Node node = _head;
            Stack<Node> stack = new Stack<Node>();
            while (node != null || stack.Count > 0)
            {
                while (node != null)
                {
                    Console.Write(node);
                    stack.Push(node);
                    node = node.Left;
                }
                if (stack.Count > 0)
                {
                    node = stack.Pop();
                    node = node.Right;//如果出栈节点没有右孩子的话则继续出栈操作
                }
            }
        }
2、中序遍历

public void InStackOrder()
        {
            Node node = _head;
            Stack<Node> stack = new Stack<Node>();
            while (node != null || stack.Count > 0)
            {
                while (node != null)
                {
                    stack.Push(node);
                    node = node.Left;
                }
                if (stack.Count > 0)
                {
                    node = stack.Pop();
                    Console.Write(node);
                    node = node.Right;
                }
            }
        }
3、后续遍历(比较难,先存起来)
     3.1利用两个栈

public void AfterStackOrder()
        {
            Stack<Node> lstack = new Stack<Node>();//用于存放父节点
            Stack<Node> rstack = new Stack<Node>();//用于存放右孩子
            Node node = _head, right;//right用于存放右栈出栈的节点
            do
            {
                while (node != null)
                {
                    right = node.Right;
                    lstack.Push(node);
                    rstack.Push(right);
                    node = node.Left;//沿左孩子的方向继续循环
                }
                node = lstack.Pop();
                right = rstack.Pop();
                if (right == null)//如果右出栈的元素为空则访问左边出栈的元素
                {
                    Console.Write(node);
                }
                else
                {
                    lstack.Push(node);//左边出栈元素退回栈
                    rstack.Push(null);//右边补充一个空元素
                }
                node = right;//如果右边出栈的部位空,则以上面的规则访问这个右孩子节点
            }
            while (lstack.Count > 0 || rstack.Count > 0);
        }
    3.2利用一个栈

public void AfterStackOrder2()//性能更优的单栈非递归算法
        {
            Node node = _head, pre = _head;
            //pre指针指向“之前出栈节点”,如果为null有问题,这里指向头结点,因为后续遍历中头结点肯定值最后被访问的。
            Stack<Node> stack = new Stack<Node>();
            while (node != null || stack.Count > 0)
            {
                while (node != null)
                {
                    stack.Push(node);
                    node = node.Left;
                }
                if (stack.Count > 0)
                {
                    Node temp = stack.Peek().Right;//获取栈顶元素的右孩子
                    if (temp == null || temp == pre)//满足规则1
                    {
                        node = stack.Pop();
                        Console.Write(node);
                        pre = node;
                        node = null;
                    }
                    else
                    {
                        node = temp;//将栈顶节点的右孩子入栈
                    }
                }
            }
        }


最后,感谢这位博主的文章,博主主页:http://www.cnblogs.com/zhanjindong





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值