【问题标题】:Implementing a complete binary tree, not binary search tree in C#在 C# 中实现完整的二叉树,而不是二叉搜索树
【发布时间】:2018-01-19 13:10:05
【问题描述】:

我正在尝试在 C# 中实现二叉树,而不是二叉搜索树。我实现了下面的代码,它工作正常,但不是我想要的。基本上我正在尝试实现一个完整的二叉树,但是使用下面的代码,我得到了一个不平衡的二叉树。

Input : 10, 20, 30, 40, 50, 60, 70, 80, 90, 100
Desired Output : 

                        10
                     /       \
                 20            30
               /    \         /  \
            40        50    60    70
           /  \      /
         80    90  100     


Current Output : 
                                10
                              /    \
                            20      30
                                  /    \
                                40      50    
                                       /   \
                                     60     70
                                           /  \
                                         80    90  
                                              /
                                            100   

这是我的代码:

  class Node 
  {
    public int data;
    public Node left;
    public Node right;

    public Node() 
    {
      data = 0;
      left = null;
      right = null;
    }
  }

  class Tree 
  {
    private Node root;

    public Tree() 
    {
      root = null;
    }

    public void AddNode(int data)
    {
      root = AddNode(root, data);
    }

    public Node AddNode(Node node, int data) 
    {
      if (node == null)
      {
        node = new Node();
        node.data = data;
      }
      else
      {
        if (node.left == null)
        {
          node.left = AddNode(node.left, data);
        }
        else
        {
          node.right = AddNode(node.right, data);
        }
      }
      return node;
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      int[] nodeData = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
      Tree tree1 = new Tree();
      foreach (int i in nodeData)
      {
        tree1.AddNode(i);
      }
      Console.ReadKey();
    }
  }

我知道问题出在我的 AddNode(Node node, int data) {...} 函数的 else 块中,但我无法找出解决方案。

我试图在网上寻找解决方案,但大多数地方都是它的二叉搜索树实现。我喜欢的解决方案之一是here,但解决方案是将输入数组作为递归调用的参数传递,我不知道在非常大的树的情况下这是否有效。还有其他几个帖子,但没有一个可以解决我的问题。

虽然我在 C# 中实现它,但更具体地说,我正在寻找修复我的 AddNode(...) 函数的逻辑,所以如果不是代码实现,我对算法很好。

有什么帮助吗?

【问题讨论】:

  • 是否需要使用节点?可以用数组代替吗?
  • 您希望树对输​​入数据进行排序还是直接添加?

标签: c# algorithm tree binary-tree


【解决方案1】:

根据定义,树是递归数据结构。

class Node<T>
{
    public Node(T data)
    {
        Data = data;
    }
    public T Data { get; }
    public Node<T> Left { get; set; }
    public Node<T> Right { get; set; }
}

因此,使用递归构造它们要直观得多。

Input: 10, 20, 30, 40, 50, 60, 70, 80, 90, 100

Desired output --complete binary tree:

               10
           /        \
         20          30
      /     \     /      \
    40       50  60      70
  /   \     /    
80     90  100

Matching index of input:

               0
           /       \
          1         2
      /     \     /     \
    3        4   5       6
  /   \     /    
 7     8   9

出现一个模式,对于索引为 i 的节点:

  • 左孩子的索引为 2*i + 1
  • 右孩子的索引为 2*i + 2

使用递归的基本情况,

i >= input.length

我们需要做的就是在根上调用递归方法。

class TreeBuilder<T>
{
    public Node<T> Root { get; }

    public TreeBuilder(params T[] data)
    {
        Root = buildTree(data, 0);
    }

    private Node<T> buildTree(T[] data, int i)
    {
        if (i >= data.Length) return null;
        Node<T> next = new Node<T>(data[i]);
        next.Left = buildTree(data, 2 * i + 1);
        next.Right = buildTree(data, 2 * i + 2);
        return next;
    }
}

用法:

int[] data = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
TreeBuilder<int> builder = new TreeBuilder<int>(data);
Node<int> tree = builder.Root;

切换API,两种方式逐个添加节点:

  • 从根向下遍历树(绝对)
  • 从之前添加的节点(相对)出发
  • 维护没有两个子节点的有序节点集合

由于第二个涉及更长的行进距离(2 * 树的高度)并且第三个已经实施(用于记账的队列),让我们看看第一个。

这一次,可视化给定位置的节点数:

               1
           /        \
         2           3
      /     \     /     \
    4        5   6       7 
  /   \     /    
8      9   10 

映射到二进制表示:

               1
           /        \
         10          11
      /     \     /     \
   100     101  110     111 
  /   \     /    
1000  1001 1010 

如果我们再次忽略最左边的位,就会出现一个模式。我们可以将这些位用作路线图,或者在本例中是节点图。

class TreeBuilder<T>
{
    private int level;
    private int nodeCount;
    public Node<T> Root { get; }

    public TreeBuilder(T data)
    {
        Root = new Node<T>(data);
        nodeCount = 1;
        level = 0;
    }

    public void addNode(T data)
    {
        nodeCount++;
        Node<T> current = Root;
        if (nodeCount >= Math.Pow(2, level + 1)) level++;
        for (int n=level - 1; n>0; n--)
            current = checkBit(nodeCount, n) ? current.Left : current.Right;
        if (checkBit(nodeCount, 0))
            current.Left = new Node<T>(data);
        else
            current.Right = new Node<T>(data);
    }

    private bool checkBit(int num, int position)
    {
        return ((num >> position) & 1) == 0;
    }
}

用法:

int[] data = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
TreeBuilder<int> builder = new TreeBuilder<int>(data[0]);
for (int i=1; i<data.Length; i++)
{
    builder.addNode(data[i]);
}
Node<int> tree = builder.Root;

【讨论】:

  • 该问题明确质疑passing the input array as [an argument in recursive calls]的效率。
【解决方案2】:

您需要逐级填充树。一个级别n2^n 节点,即从根有2^n 路径。每条路径都可以编码为n位数(0 表示左分支,1 表示右分支)。也就是说,要填充nth 级别,

    for path from 0 to 2^n - 1
        value = get_next_value()
        node = root
        for level = 0 to n - 1
            if path & 0x1 == 0
                node = node->left
            else
                node = node->right
            ++level
            path >>= 1
        if path == 0
            node->left = new node(value)
        else
            node->right = new node(value)

【讨论】:

  • 这可能有效,但看起来并不完全是 C#。如果您坚持要从左到右填充树,请处理最重要(但一个)到最不重要的位。
  • @greybeard 为什么?我看不出有什么区别。没有要求填充级别的顺序。是的,它不是 C#,不是 C,不是 Python。这是伪代码。
  • (Why 从左到右?问题中的草图看起来是类型,并且 elsewhere 我已经看到 complete binary 定义了 自上而下,从左到右 - 这提出了在条件句中以相反顺序处理位的建议。)
【解决方案3】:

此算法可以非常简单有效地解决您的问题。

考虑这个 Node 类:

public class Node<T>
{
     public Node(T data) 
     {
         Data = data;
     }

    public T Data { get; }

    public Node<T>  Left { get; set;}

    public Node<T>  Right { get; set;}
}

本课程将帮助您组成树:

public class TreeBuilder<T>
{
    private readonly Queue<Node<T>> _previousNodes;

    public Node<T> Root { get; }

    public TreeBuilder(T rootData)
    {
        Root = new Node<T>(rootData)
        _previousNodes = new Queue<Node<T>>();
        _previousNodes.Enqueue(Root);
    }

    public void AddNode(T data)
    {
        var newNode = new Node<T>(data);
        var nodeToAddChildTo = _previousNodes.Peek();
        if(nodeToAddChildTo.Left == null)
        {
           nodeToAddChildTo.Left = node; 
        }
        else
        {
            nodeToAddChildTo.Right = node;
            _previousNodes.Dequeue();
        }      
        _previousNodes.Enqueue(newNode);
    } 
}

AddNode 方法背后的逻辑基于FIFO 方法,因此我们将在实现中使用Queue&lt;T&gt;

我们将从第一个节点开始,首先附加一个左子节点(然后将其添加到队列中),然后我们将附加一个右节点(然后将其添加到队列中),并且仅在之后我们将附加两个我们将从队列中删除它并开始将孩子附加到它的左孩子(这是队列中的下一个),当我们完成它时,我们将开始将孩子附加到它的右孩子(这将是队列中的下一个),我们会从左到右从上到下不断地做这个操作,直到组成树。

现在你可以像这样在你的 main 方法中使用它:

public class Program
{
    public static void Main(string[] args)
    {
        int[] values = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
        var treeBuilder = new TreeBuilder<int>(values[0]);
        foreach (int value in values.Skip(1))
        {
            treeBuilder.AddNode(value);
        }
        //here you can use treeBuilder Root property as an entry point to the tree
    }
}

【讨论】:

  • (鉴于 2016/10/21 有两个有效答案,attentiondo 你认为enough 是什么?user58697' s binary little endian approach 仅使用节点总数作为附加信息构建一棵树,其左右子节点的数量可能差异最小,这个树使用包含大约一半节点的队列从左到右填充级别。“二进制大端" 也从左到右填充,“队列”可以修改为始终Dequeue()Enqueue() 旧节点除非“满”,在这种情况下所有后代。)
【解决方案4】:

这是另一种无需跟踪左、右和/或父母即可获得答案的方法。事实上,Node 类变得非常简单,当您调用 tree1.Root 时,Tree 类完成了工作……下面我使用构造函数来添加值,但我已经包含了 AddNodes 方法,您可以在其中添加一个新节点作为您想通过一次调用添加许多值。

  class Node<T>
  {
    public T Data { get; set; }
    public Node<T> Left { get; set; }
    public Node<T> Right { get; set; }

    public override string ToString()
    {
      return Data.ToString();
    }
  }


  class Tree<T>
  {
    private readonly List<T> list;

    private static readonly Func<List<T>, int, Node<T>> LeftFunc = (l, i) =>
    {
      var lix = Convert.ToInt32(Convert.ToString(i, 2) + "0", 2) - 1;
      return l.Count > lix ? new Node<T> {Data = l[lix], Left = LeftFunc(l, lix + 1), Right = RightFunc(l, lix + 1) } : null;
    };

    private static readonly Func<List<T>, int, Node<T>> RightFunc = (l, i) =>
    {
      var rix = Convert.ToInt32(Convert.ToString(i, 2) + "1", 2) - 1;
      return l.Count > rix ? new Node<T> { Data = l[rix], Left = LeftFunc(l, rix + 1), Right = RightFunc(l, rix + 1) } : null;
    };

    public Node<T> Root => list.Any() ? new Node<T>{Data=list.First(), Left = LeftFunc(list,1), Right= RightFunc(list,1)} : null;

    public Tree(params T[] data)
    {
      list = new List<T>(data);
    }

    public int Count => list.Count;

    public void AddNodes(params T[] data)
    {
      list.AddRange(data);
    }


    public double Levels => Math.Floor(Math.Log(list.Count,2))+1;

    public override string ToString()
    {
      return  (list?.Count ?? 0).ToString();
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      var nodeData = new [] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
      var tree1 = new Tree<int>(nodeData);



      Console.ReadKey();
    }

为了好玩,我创建了一个将树写入控制台的扩展,以帮助可视化树。为了使用它,您需要确保 Node 和 Tree 类是公共的,并向 Tree 类添加 Values 属性。所以你的 Tree 类看起来像这样:

  public class Tree<T>
  {
    private readonly List<T> list;

    private static readonly Func<List<T>, int, Node<T>> LeftFunc = (l, i) =>
    {
      var lix = Convert.ToInt32(Convert.ToString(i, 2) + "0", 2) - 1;
      return l.Count > lix ? new Node<T> {Data = l[lix], Left = LeftFunc(l, lix + 1), Right = RightFunc(l, lix + 1) } : null;
    };

    private static readonly Func<List<T>, int, Node<T>> RightFunc = (l, i) =>
    {
      var rix = Convert.ToInt32(Convert.ToString(i, 2) + "1", 2) - 1;
      return l.Count > rix ? new Node<T> { Data = l[rix], Left = LeftFunc(l, rix + 1), Right = RightFunc(l, rix + 1) } : null;
    };

    public Node<T> Root => list.Any() ? new Node<T>{Data=list.First(), Left = LeftFunc(list,1), Right= RightFunc(list,1)} : null;

    public Tree(params T[] data)
    {
      list = new List<T>(data);
    }

    public int Count => list.Count;

    public void AddNodes(params T[] data)
    {
      list.AddRange(data);
    }

    public IEnumerable<T> Values => list.ToArray();

    public double Levels => Math.Floor(Math.Log(list.Count,2))+1;

    public override string ToString()
    {
      return  (list?.Count ?? 0).ToString();
    }
  }

这里是扩展类:

  public static class TreeAndNodeExtensions
  {
    public static void Write<T>(this Tree<T> tree)
    {
      var baseMaxNodes = Convert.ToInt32(Math.Pow(2, tree.Levels - 1));

      // determine the required node width based on the the last two levels and their value lengths...
      var nodeWidth = Math.Max(tree.Values.Skip(Convert.ToInt32(Math.Pow(2, tree.Levels - 2) - 1)).Max(n => n.ToString().Length), tree.Values.Skip(Convert.ToInt32(Math.Pow(2, tree.Levels - 1) - 1)).Max(n => n.ToString().Length) + 1) + 1;

      var baseWidth = baseMaxNodes * nodeWidth;
      Console.CursorLeft = baseWidth/2;
      tree.Root.Write(baseWidth);
    }

    private static void Write<T>(this Node<T> node, int nodeWidth, int level=0)
    {
      var cl = Console.CursorLeft;
      var ct = Console.CursorTop;

      if (Console.CursorLeft >= Convert.ToInt32(Math.Ceiling(node.Data.ToString().Length / 2.0)))
      {
        Console.CursorLeft -= Convert.ToInt32(Math.Ceiling(node.Data.ToString().Length / 2.0));
      }
      Console.Write(node.Data);
      if (node.Left != null)
      {
        var numCenter = cl - nodeWidth/4;
        Console.CursorLeft = numCenter;
        Console.CursorTop = ct + 2;
        Console.Write('/');
        Console.CursorTop = ct + 1;
        Console.Write(new string('_',cl-Console.CursorLeft));
        Console.CursorLeft = numCenter;
        Console.CursorTop = ct+3;
        node.Left.Write(nodeWidth/2, level+1);
      }

      if (node.Right != null)
      {
        var numCenter = cl + nodeWidth/4;
        Console.CursorLeft = cl;
        Console.CursorTop = ct + 1;
        Console.Write(new string('_', numCenter-cl-1));
        Console.CursorTop = ct + 2;
        Console.Write('\\');
        Console.CursorLeft = numCenter;
        Console.CursorTop = ct+3;
        node.Right.Write(nodeWidth/2,level + 1);
      }

      Console.SetCursorPosition(cl,ct);
    }
  }

然后你可以更新你的程序来使用这个扩展:

  class Program
  {
    static void Main(string[] args)
    {
      var nodeData = new [] { 10, 20, 30, 40, 50, 60, 70, 80,90,100 };
      var tree1 = new Tree<int>(nodeData);

      tree1.Write();

      Console.ReadKey();
    }
  }

你应该看到这个:

                   10
           __________________
          /                  \
         20                  30
      ________            ________
     /        \          /        \
    40        50        60        70
    __        _
   /  \      /
  80  90   100

【讨论】:

    【解决方案5】:

    每个节点都需要知道它的父节点,根节点是例外,因为它的父节点为空。然后每个节点都需要知道它最后向下传递值的路径。然后,当一个节点被要求添加一个子节点时,它将按以下顺序进行:左、右、父、左、右、父,...(根节点是例外,因为它会跳过父节点,只会左右交替,左,右...)

    我快速调整了您的代码,使其按您的预期工作,这可能会在一段时间内变得更简洁。

     class Node
      {
        private readonly Node parent;
        private Direction lastPath;
    
        public int Data { get; set; }
        public Node Left { get; set; }
        public Node Right { get; set; }
    
        public Node(int data, Node parent = null)
        {
          Data = data;
          this.parent = parent;
        }
    
        public Node AddChild(int data)
        {
          if (Left == null)
          {
            lastPath = Direction.Left;
            Left = new Node(data, this);
            return this;
          }
          if (Right == null)
          {
            lastPath = Direction.Right;
            Right = new Node(data, this);
            return parent ?? this;
          }
    
          if (lastPath == Direction.Parent || parent==null && lastPath == Direction.Right)
          {
            lastPath = Direction.Left;
            return Left.AddChild(data);
          }
    
          if (lastPath == Direction.Left)
          {
            lastPath = Direction.Right;
            return Right.AddChild(data);
          }
    
          lastPath = Direction.Parent;
          return parent?.AddChild(data);
        }
      }
    
      enum Direction
      {
        Left,
        Right,
        Parent
      }
    
      class Tree
      {
        public Node Root { get; private set; }
        private Node current;
    
        public void AddNode(int data)
        {
          if (current == null)
          {
            Root = new Node(data);
            current = Root;
          }
          else
          {
            current = current.AddChild(data);
          }
        }
      }
    
      class Program
      {
        static void Main(string[] args)
        {
          var nodeData = new [] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
          var tree1 = new Tree();
    
          foreach (var data in nodeData)
          {
            tree1.AddNode(data);
          }
          Console.ReadKey();
        }
      }
    

    【讨论】:

    • Each node will need to know it's parent 哪里需要? you 使用的Binary Tree 的定义是什么?我承认这个问题暗示了Node AddNode(Node node, int data) public.
    • 当一个节点被要求添加一个子节点时,它可以将它设置在它的左边或右边,但是一旦这两个槽被填满,它必须移交给它的左边、右边或父节点。这允许树水平遍历每个级别。要么是这样,要么是对树/节点结构的完全重写。我试图保持最初帖子的逻辑。我已经解决了一种更好的方法来构建基于从列表中生成树的方法。我会将其作为单独的答案发布。
    • 我没有拿public Node AddNode(Node node, int data)一个可以有意义实现的签名;从不是Node 的实例方法开始,在不了解Node 的呈现成员如何以及如果新的Node 没有以@ 结尾987654330@ 的子树,它与node 有什么关系不知道每个节点中没有#children(或等效项)会有什么影响 :尝试在实例化后立即将 -1 和 0 添加到树中,获取它的 Root.Left 并让循环向其中添加数据。
    【解决方案6】:

    回答您的问题:

    传递数组是通过向函数传递对数组的引用来完成的。所以在性能方面,它与传递任意类型的对象完全一样(https://msdn.microsoft.com/en-us/library/bb985948.aspx)。

    就我个人而言,我不喜欢总是传递同一个对象的递归函数,也不喜欢调用构造函数的递归函数的构造函数。

    没有数组,这里有一个解决方案:

    需要注意的一个有趣的事情是,在这样的树中,如果从 1 开始寻址,则可以使用节点地址来查找节点:

                    0001
            0010            0011
        0100    0101    0110   0111
    1000
    

    因此,如果您跟踪树中的节点数,您就知道下一个要添加的项目将位于地址 1001。

    要正确地做到这一点,我们可以找到父节点(即 1001 右移一次:100)然后决定我们是向左还是向右(取决于 1001 模 2 = 1:必须向右添加)。

    所以这是可以做到的代码:

    节点类

    //Having a generic here allows you to create a tree of any data type
    class Node<T> 
    {
        public T Data { get; set; }
        public Node<T> Left { get; set; }
        public Node<T> Right { get; set; }
        public Node(T Data)
        {
            this.Data = Data;
        }
    }
    

    树类

    class Tree<T>
    {
        Node<T> root = null;
        int nodeCount = 0;
    
        public void AddNode(T Data)
        {
            AddNode(new Node<T>(Data));
        }
    
        public void AddNode(Node<T> Node)
        {
            nodeCount++;
            if (root == null)
                root = Node;
            else
            {
                //First we find the parent node
                Node<T> parent = FindNodeWithAddress(nodeCount >> 1);
                //Then we add left or right
                if (nodeCount % 2 == 0)
                    parent.Left = Node;
                else
                    parent.Right = Node;
            }
        }
    
        private Node<T> FindNodeWithAddress(int address)
        {
            if (address == 1)
                return root;
            //To find the proper address we use the same trick
            //We first find our parent's address
            Node<T> parent = FindNodeWithAddress(address >> 1);
            //Then we go left or right
            return (address % 2 == 0 ? parent.Left : parent.Right);
        }
    }
    

    【讨论】:

    • 考虑到FindNodeWithAddress()Node&lt;T&gt;s 开始处理root,这与user58697's 2016 answer 有何不同?
    • @greybeard 首先是因为我回答了 OP 的问题。然后,虽然算法在概念上是相同的,但我以 OOP、递归和 C# 的方式呈现它。
    【解决方案7】:

    您是否尝试过使用数组来实现它?

    您将使用一个数组和一个 int 来保存上次使用的位置 对于任何位置pos,左侧的“子节点”将在位置2*pos 正确的“子节点”将在位置2*pos+1 并且“父节点”将在位置pos/2

    (不要认为这段代码在语法上是正确的,这只是一个例子)

    template<class T>
    class CompleteBinTree<T>{
        private int[] arr;
        private int arrSize;
        private int pos;
    
        public CompleteBinTree(){
            arrSize = 100;
            arr = new int[arrSize]//you can always change this number
            int pos = 1; //you can use 0 instead of 1, but this way it's easier to understand
        }
    
        public AddNode(T t){
            if(pos + 1 == arrSize){
                int[] aux = int[arrSize];
                for(int i = 0; i < arrSize; i++)
                    aux[i] = arr[i];
                arr = aux[i];
                arrSize = arrSize * 2;
            }
                arr[pos] = t;
                pos++;
        }
    
        public IndexOfLeftSon(int x){
            return 2*x;
        }
    
        public IndexOfRightSon(int x){
            return 2*x + 1;
        }
    
        public DeleteNode(int x){
            for(int i = x; i < pos; i++)
                arr[i] = arr[i+1];
        }
    }
    

    【讨论】:

      【解决方案8】:

      鉴于您确实想要构建分配节点的树,而不是其他人建议的数组,因此有一个非常简单的算法:使用位置队列(给定节点的左或右子节点或根节点)在自上而下的级别展开树。在尾部添加新位置并从头部移除以添加每个连续节点。没有递归。

      抱歉,我目前无法访问 C# 环境,所以我将在 Java 中展示它。翻译应该很简单。

      import java.util.ArrayDeque;
      import java.util.Deque;
      
      public class CompleteBinaryTree {
        final Deque<Location> queue = new ArrayDeque<>();
      
        /** Build a tree top-down in levels left-to-right with given node values. */
        Node build(int [] vals) {
          Node root = null;   
          queue.clear();
          queue.add(new Location(root, Location.Kind.ROOT));
          for (int val : vals) {
            Location next = queue.pollFirst();
            switch (next.kind) {
            case ROOT: root = addNode(val); break;
            case LEFT: next.node.left = addNode(val); break;
            case RIGHT: next.node.right = addNode(val); break;
            }
          }
          return root;
        } 
      
        /** Create a new node and queue up locations for its future children. */
        Node addNode(int val) {
          Node node = new Node(val);
          queue.addLast(new Location(node, Location.Kind.LEFT));
          queue.addLast(new Location(node, Location.Kind.RIGHT));
          return node;
        }
      
        static class Node {
          final int val;
          Node left, right;
          Node(int val) {
            this.val = val;
          }
          void print(int level) {
            System.out.format("%" + level + "s%d\n", "", val);
            if (left != null) left.print(level + 1);
            if (right != null) right.print(level + 1);
          }
        }
      
        static class Location {
          enum Kind { ROOT, LEFT, RIGHT }
          final Node node;
          final Kind kind;
          Location(Node node, Kind kind) {
            this.node = node;
            this.kind = kind;
          }
        }
      
        public static void main(String [] args) {
          int [] vals = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
          new CompleteBinaryTree().build(vals).print(1);
        }
      }
      

      运行时,如您所愿,这会产生

      10
       20
        40
         80
         90
        50
         100
       30
        60
        70
      

      【讨论】:

      【解决方案9】:

      二叉树是一种非平衡结构。树的布局取决于您插入值的顺序。您可以有两棵树,它们的值完全相同,但插入顺序不同,这棵树看起来会完全不同。

      对于平衡树,看看 AVL 树。这是一种流行的自我平衡实现。

      实际上,在实际使用中,树木已经过时了。字典更好,但如果您正在学习树,请查看 AVL 树:)。

      【讨论】:

      • 一个 AVL 树将是一个 Complete Binary Tree,只是偶然(并且从不按顺序插入键(具有非搜索 BST 的键?)。Dictionary 用于查找内容,a NOT Binary Search Tree(很可能)不是。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-21
      • 2013-05-03
      • 1970-01-01
      • 1970-01-01
      • 2013-04-14
      • 2015-07-23
      相关资源
      最近更新 更多