求满二叉树两个节点之间的最短距离

求满二叉树两个节点之间的最短距离

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FirstSolver
{
    internal class Program
    {
        static void Main(string[] args)
        {
            BinaryTreeNode<int> root1 = FullBinaryTree.CreateTree(1, 4);
            Console.WriteLine(FullBinaryTree.PrintTree(root1));
            Console.WriteLine();

            BinaryTreeNode<int> root2 = FullBinaryTree.CreateTree(101, 4);
            Console.WriteLine(FullBinaryTree.PrintTree(root2));
            Console.WriteLine();

            BinaryTreeNode<int> root3 = FullBinaryTree.CreateTree(201, 4);
            Console.WriteLine(FullBinaryTree.PrintTree(root3));
            Console.WriteLine();

            root1.ParentNode = root2;
            root2.ParentNode = root3;
            root3.ParentNode = root1;

            BinaryTreeNode<int> nodeA = FullBinaryTree.FindNode(root1, 5);
            BinaryTreeNode<int> nodeB = FullBinaryTree.FindNode(root1, 7);
            Console.WriteLine($"[A({nodeA.Value})]->[B({nodeB.Value})]");
            Stack<BinaryTreeNode<int>> path = FullBinaryTree.FindPath(nodeA, nodeB);
            Console.WriteLine(FullBinaryTree.PrintPath(path));
            Console.WriteLine(FullBinaryTree.PrintPath(FullBinaryTree.QuickFindPath(nodeA, nodeB)));
            Console.WriteLine();

            nodeB = FullBinaryTree.FindNode(root2, 107);
            Console.WriteLine($"[A({nodeA.Value})]->[B({nodeB.Value})]");
            path = FullBinaryTree.FindPath(nodeA, nodeB);
            Console.WriteLine(FullBinaryTree.PrintPath(path));
            Console.WriteLine(FullBinaryTree.PrintPath(FullBinaryTree.QuickFindPath(nodeA, nodeB)));
            Console.WriteLine();

            nodeB = FullBinaryTree.FindNode(root3, 207);
            Console.WriteLine($"[A({nodeA.Value})]->[B({nodeB.Value})]");
            path = FullBinaryTree.FindPath(nodeA, nodeB);
            Console.WriteLine(FullBinaryTree.PrintPath(path));
            Console.WriteLine(FullBinaryTree.PrintPath(FullBinaryTree.QuickFindPath(nodeA, nodeB)));
            Console.WriteLine();

            // 测试是否死循环
            nodeB = new BinaryTreeNode<int>(301);
            Console.WriteLine($"[A({nodeA.Value})]->[B({nodeB.Value})]");
            path = FullBinaryTree.FindPath(nodeA, nodeB);
            Console.WriteLine(FullBinaryTree.PrintPath(path));
            Console.WriteLine(FullBinaryTree.PrintPath(FullBinaryTree.QuickFindPath(nodeA, nodeB)));
            Console.WriteLine();

            Console.ReadKey();
        }
    }

    /// <summary>
    /// 满二叉树
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    public static class FullBinaryTree
    {
        /// <summary>
        /// 创建深度为depth的满二叉树
        /// </summary>
        /// <param name="intial">初始值</param>
        /// <param name="depth">深度</param>
        /// <returns>满二叉树头结点</returns>
        public static BinaryTreeNode<int> CreateTree(int intial, int depth)
        {
            BinaryTreeNode<int> header = new BinaryTreeNode<int>(intial);
            CreateSub(header, intial, 2, depth);
            return header;
        }

        /// <summary>
        /// 创建子树
        /// </summary>
        /// <param name="node">父节点</param>
        /// <param name="initial">根节点初始值</param>
        /// <param name="level">层级</param>
        /// <param name="depth">深度</param>
        private static void CreateSub(BinaryTreeNode<int> node, int initial, int level, int depth)
        {
            if (level > depth) return;
            int value = (node.Value << 1) - (initial - 1);
            BinaryTreeNode<int> leftChildNode = new BinaryTreeNode<int>(value);
            BinaryTreeNode<int> rightChildNode = new BinaryTreeNode<int>(value + 1);
            node.LeftChildNode = leftChildNode;
            node.RightChildNode = rightChildNode;
            leftChildNode.ParentNode = node;
            rightChildNode.ParentNode = node;
            CreateSub(leftChildNode, initial, level + 1, depth);
            CreateSub(rightChildNode, initial, level + 1, depth);
        }

        /// <summary>
        /// 打印二叉树
        /// </summary>
        /// <param name="header">节点</param>
        /// <returns>二叉树字符串</returns>
        public static string PrintTree(BinaryTreeNode<int> header)
        {
            StringBuilder sb = new StringBuilder($"{header.Value}");
            if (header.LeftChildNode != null)
            {
                sb.Append($"({header.LeftChildNode.Value},{header.RightChildNode.Value})");
                PrintTreeSub(header.LeftChildNode, sb);
                PrintTreeSub(header.RightChildNode, sb);
            }
            return sb.ToString();
        }

        private static void PrintTreeSub(BinaryTreeNode<int> node, StringBuilder sb)
        {
            sb.Append($" {node.Value}");
            if (node.LeftChildNode != null)
            {
                sb.Append($"({node.LeftChildNode.Value},{node.RightChildNode.Value})");
                PrintTreeSub(node.LeftChildNode, sb);
                PrintTreeSub(node.RightChildNode, sb);
            }
        }

        /// <summary>
        /// 查找指定值的节点
        /// </summary>
        /// <param name="node">二叉树</param>
        /// <param name="value">要匹配的值</param>
        /// <returns>找到的节点</returns>
        public static BinaryTreeNode<int> FindNode(BinaryTreeNode<int> node, int value)
        {
            if (Equals(node.Value, value)) return node;
            if (node.LeftChildNode != null)
            {
                return FindNode(node.LeftChildNode, value) ?? FindNode(node.RightChildNode, value);
            }
            return null;
        }

        /// <summary>
        /// 定位根节点
        /// </summary>
        /// <param name="node">初始节点</param>
        /// <returns>根节点</returns>
        public static BinaryTreeNode<int> GetRootNode(BinaryTreeNode<int> node)
        {
            while (true)
            {
                if (node.ParentNode == null) break;
                if (node.ParentNode.LeftChildNode != node && node.ParentNode.RightChildNode != node) break;
                node = node.ParentNode;
            }
            return node;
        }

        /// <summary>
        /// 寻找节点A到节点B的最短路径
        /// </summary>
        /// <param name="nodeA">节点A</param>
        /// <param name="nodeB">节点B</param>
        /// <returns>最短路径</returns>
        public static Stack<BinaryTreeNode<int>> FindPath(BinaryTreeNode<int> nodeA, BinaryTreeNode<int> nodeB)
        {
            BinaryTreeNode<int> rootA = GetRootNode(nodeA);

            Stack<BinaryTreeNode<int>> path = new Stack<BinaryTreeNode<int>>();

            BinaryTreeNode<int> node = nodeA;
            path.Push(node);
            bool find = FindPathSub(node, nodeB, path);
            if (!find)
            {
                while (!find)
                {
                    if (node.ParentNode == null) break;

                    // 避免死循环
                    if (node.ParentNode == rootA && (node != rootA.LeftChildNode && node != rootA.RightChildNode)) break;

                    path.Push(node.ParentNode);

                    int state = 0; // 进入另一颗二叉树
                    if (node.ParentNode.LeftChildNode == node)
                        state = 1;
                    else if (node.ParentNode.RightChildNode == node)
                        state = 2;

                    if (state == 0 || state == 1)
                    {
                        find = FindPathSub(node.ParentNode.RightChildNode, nodeB, path);
                        if (find) break;
                    }

                    if (state == 0 || state == 2)
                    {
                        find = FindPathSub(node.ParentNode.LeftChildNode, nodeB, path);
                        if (find) break;
                    }
                    node = node.ParentNode;
                }
            }
            return find ? path : null;
        }

        private static bool FindPathSub(BinaryTreeNode<int> node, BinaryTreeNode<int> search, Stack<BinaryTreeNode<int>> path)
        {
            path.Push(node);
            if (node == search) return true;
            if (node.LeftChildNode != null)
            {
                if (FindPathSub(node.LeftChildNode, search, path)) return true;
                if (FindPathSub(node.RightChildNode, search, path)) return true;
            }
            path.Pop();
            return false;
        }

        /// <summary>
        /// 获取到根节点的路径
        /// </summary>
        /// <param name="node">起始节点</param>
        /// <returns>到根节点路径</returns>
        public static List<BinaryTreeNode<int>> GetRootNodePath(BinaryTreeNode<int> node)
        {
            List<BinaryTreeNode<int>> path = new List<BinaryTreeNode<int>>();
            path.Add(node);
            while (true)
            {
                if (node.ParentNode == null) break;
                if (node.ParentNode.LeftChildNode != node && node.ParentNode.RightChildNode != node) break;
                node = node.ParentNode;
                path.Add(node);
            }
            return path;
        }

        /// <summary>
        /// 快速寻找节点A到节点B的最短路径
        /// </summary>
        /// <param name="nodeA">节点A</param>
        /// <param name="nodeB">节点B</param>
        /// <returns>最短路径</returns>
        public static List<BinaryTreeNode<int>> QuickFindPath(BinaryTreeNode<int> nodeA, BinaryTreeNode<int> nodeB)
        {
            List<BinaryTreeNode<int>> pathA = GetRootNodePath(nodeA);
            List<BinaryTreeNode<int>> pathB = GetRootNodePath(nodeB);
            BinaryTreeNode<int> rootA = pathA[pathA.Count - 1];
            BinaryTreeNode<int> rootB = pathB[pathB.Count - 1];
            if (rootA == rootB)
            {
                int i = pathA.Count - 2;
                int j = pathB.Count - 2;
                for (; i >= 0 && j >= 0; i--, j--)
                {
                    if (pathA[i] != pathB[j]) break;
                }

                if (pathA.Count - 2 > i) pathA.RemoveRange(i + 2, pathA.Count - i - 2);
                pathB.RemoveRange(j + 1, pathB.Count - j - 1);
                pathB.Reverse();
                pathA.AddRange(pathB);
                return pathA;
            }
            else
            {
                BinaryTreeNode<int> node = rootA;
                while (node.ParentNode != null)
                {
                    node = node.ParentNode;
                    if (node == rootB)
                    {
                        pathB.Reverse();
                        pathA.AddRange(pathB);
                        return pathA;
                    }
                    else if (node == rootA)
                    {   // 避免死循环
                        break;
                    }
                    pathA.Add(node);
                }
                return null;
            }            
        }

        /// <summary>
        /// 打印路径
        /// </summary>
        /// <param name="path">路径</param>
        /// <returns>路径字符串</returns>      
        public static string PrintPath(IEnumerable<BinaryTreeNode<int>> path)
        {
            if (path == null) return "No Path!"; // 没有路径到达
            IEnumerable<BinaryTreeNode<int>> p = path;
            if (path is Stack<BinaryTreeNode<int>>)
            {
                p = p.Reverse();
            }            
            StringBuilder sb = new StringBuilder();
            foreach (var item in p)
            {
                sb.Append($"{item.Value}->");
            }
            sb.Remove(sb.Length - 2, 2);
            return sb.ToString();
        }
    }

    /// <summary>
    /// 节点定义
    /// </summary>
    /// <typeparam name="T">节点数据类型:要求值类型</typeparam>
    public class BinaryTreeNode<T> where T : struct
    {
        public T Value { get; set; }
        public BinaryTreeNode<T> ParentNode { get; set; }
        public BinaryTreeNode<T> LeftChildNode { get; set; }
        public BinaryTreeNode<T> RightChildNode { get; set; }

        public BinaryTreeNode() { Value = default; }
        public BinaryTreeNode(T value) { Value = value; }
    }
}
  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值