任何 BST 树中节点的正确结构是什么
What is the correct structure for a node in any BST Tree
根据理论为二叉树创建节点的正确方法是什么?
例如:
struct Node
{
int data;
Node *left;
Node *right;
};
我目前面临的问题是我从多个来源(书籍、网站、在线讲座等)得到了 2 个不同的答案。
来自 "Introduction to Algorithms",第 3 版,第 286,287 页:"In addition to a key and satellite data, each node contains attributes left, right, and p that point to the nodes corresponding to its left child,its right child, and its parent, respectively."
意思是这样的:
struct Node
{
int data;
Node *parent;
Node *left;
Node *right;
};
另一方面,我发现有几个 link 不遵循这种设计,例如:
http://algs4.cs.princeton.edu/32bst/
http://math.hws.edu/eck/cs225/s03/binary_trees/
http://www.cprogramming.com/tutorial/lesson18.html
这些实现不会将 link 保留到 parent 并且从一些在线讲座中可以看出树不会向后遍历(也就是看不到 parent ) 这与书中的概念相反!
例如,在 RedBlack 树中,您需要查看该节点的祖父 parent 和叔叔,以确定是否 re-colour and/or 旋转以重新平衡树。
在 AVL 树中你不需要,因为焦点在 sub-trees 的高度上。
四叉树和八叉树是一样的,你不需要 parent.
问题:
有人可以回答我这个问题并提供有效来源解释为二叉树或任何树(B-Trees,..等)设计节点的正确方法是什么?
还有向后遍历的规则是什么?我知道 Pre-order、In-order、Post-order、Breadth-First、Depth-First(Pre-order) 和其他用于遍历的 AI 启发式算法。
是否不允许您在树中向后移动,即从 child 到 parent?如果是这样,那为什么这本书建议 link 到 parent 节点?
没有硬性规定必须 link 返回到树数据结构中的父级。将 link 返回给父级类似于双重 linked 列表。没有 link 回到父级只是一个 linked 列表。使用 back link,显然你获得了更多的灵活性,但代价是(相对)更复杂的实现。许多问题可以用 linked 列表解决,而其他一些问题需要双重 linked 列表。
没有任何规范的定义。
一般来说,命令式语言(例如 C++)倾向于使用父级方法。它简化了高效再平衡的实现,并且正如 Thomas Matthews 指出的那样,促进了常量-space 迭代器。
函数式语言(例如 Haskell)倾向于使用无父方法(参见 Purely Functional Data Structures)。由于不可能进行任何修改,所有重新平衡都是通过沿搜索路径重新复制来完成的,因此不需要后向指针。由于是强递归导向的,常量 space 迭代器的设计也不是什么大问题。
基本的二叉树(基础)需要child个指针:
struct binary_tree_node
{
binary_tree_node * left_child;
binary_tree_node * right_child;
};
可以对有助于搜索或存储的基础进行许多修改。
这些可以包括(但不限于):
- parent指针
- child 个指针数组
- "color"指标
- 专门的叶节点 -- 无 child 链接
便利性取决于数据结构的使用。例如,child 个节点的数组可能有助于加快 I/O 访问速度,其中读取 "page" 个节点与读取单个节点一样高效(参见 B-Tree)。 "color" 指标可能有助于平衡的决定。专门的 "leaf" 节点减少了树占用的内存量。
至于遍历,任何方法都可以遍历一棵树。没有规则阻止从 child 到 parent 的遍历。一些遍历可能包括兄弟姐妹到兄弟姐妹。
一些书籍或网站可能会挑剔传统或基本的 "binary tree" 数据结构。我发现限制妨碍了。
struct tree_node
{
tree_node* left_child;
tree_node* right_child;
int data; // here you can use whatever type or data you want. Even generic type
};
这取决于你的任务
说实话binary search tree
是一个概念,没有严格或标准的数据结构设计规则。但是要了解基本功能(例如插入、删除、查找等),人们会使用非常基本的数据结构,例如
struct Node
{
int data;
Node *left;
Node *right;
};
但是您的任务可能会针对不同的目的进行不同的设计。例如,如果您需要在单个操作中找到它的 parent node
,您可能会考虑将节点结构设计为
struct Node
{
int data;
Node *parent;
Node *left;
Node *right;
};
其他一些复杂的实现可能也需要存储同级列表。就像,
struct Node
{
int data;
Node *parent;
Node *left;
Node *right;
list<Node> *siblings;
};
所以,没有严格的标准
以下节点定义(在 Java 中)是针对平衡二叉树而不是 BST。
// Copyright (C) NNcNannara 2017
public class Node
{
public Node Left;
public Node Right;
public Node Parent;
public State Balance;
public Node()
{
Left = this;
Right = this;
Parent = null;
Balance = State.Header;
}
public Node(Node p)
{
Left = null;
Right = null;
Parent = p;
Balance = State.Balanced;
}
public Boolean isHeader ()
{ return Balance == State.Header; }
}
这针对平衡例程进行了优化。一个set节点派生Node的思路如下
// Copyright (C) NNcNannara 2017
public class SetNode<T> extends Node
{
public T Data;
public SetNode(T dataType, Node Parent)
{
super(Parent);
Data = dataType;
}
}
一个字典节点如下
// Copyright (C) NNcNannara 2017
public class DictionaryNode<K, T> extends Node
{
public T Data;
public K Key;
public DictionaryNode(K keyType, T dataType, Node Parent)
{
super(Parent);
Key = keyType;
Data = dataType;
}
}
平衡和迭代本质上是非通用的,并且是为基础 class 节点定义的。当然磁盘上也可能存在二叉树,其节点类型如下
package persistent;
public class Node
{
public long Left;
public long Right;
public long Parent;
public long Key;
public calculus.State Balance;
public Node()
{
Left = 0;
Right = 0;
Parent = 0;
Balance = calculus.State.Header;
Key = 0;
}
public Node(long p)
{
Left = 0;
Right = 0;
Parent = p;
Balance = calculus.State.Balanced;
Key = 0;
}
public Boolean IsHeader () { return Balance == calculus.State.Header; }
}
不是存在引用,而是存在节点和数据文件中的长整数偏移量。请注意,磁盘上的所有集合只有一种节点类型。
根据理论为二叉树创建节点的正确方法是什么? 例如:
struct Node
{
int data;
Node *left;
Node *right;
};
我目前面临的问题是我从多个来源(书籍、网站、在线讲座等)得到了 2 个不同的答案。
来自 "Introduction to Algorithms",第 3 版,第 286,287 页:"In addition to a key and satellite data, each node contains attributes left, right, and p that point to the nodes corresponding to its left child,its right child, and its parent, respectively."
意思是这样的:
struct Node
{
int data;
Node *parent;
Node *left;
Node *right;
};
另一方面,我发现有几个 link 不遵循这种设计,例如:
http://algs4.cs.princeton.edu/32bst/
http://math.hws.edu/eck/cs225/s03/binary_trees/
http://www.cprogramming.com/tutorial/lesson18.html
这些实现不会将 link 保留到 parent 并且从一些在线讲座中可以看出树不会向后遍历(也就是看不到 parent ) 这与书中的概念相反!
例如,在 RedBlack 树中,您需要查看该节点的祖父 parent 和叔叔,以确定是否 re-colour and/or 旋转以重新平衡树。 在 AVL 树中你不需要,因为焦点在 sub-trees 的高度上。 四叉树和八叉树是一样的,你不需要 parent.
问题:
有人可以回答我这个问题并提供有效来源解释为二叉树或任何树(B-Trees,..等)设计节点的正确方法是什么?
还有向后遍历的规则是什么?我知道 Pre-order、In-order、Post-order、Breadth-First、Depth-First(Pre-order) 和其他用于遍历的 AI 启发式算法。
是否不允许您在树中向后移动,即从 child 到 parent?如果是这样,那为什么这本书建议 link 到 parent 节点?
没有硬性规定必须 link 返回到树数据结构中的父级。将 link 返回给父级类似于双重 linked 列表。没有 link 回到父级只是一个 linked 列表。使用 back link,显然你获得了更多的灵活性,但代价是(相对)更复杂的实现。许多问题可以用 linked 列表解决,而其他一些问题需要双重 linked 列表。
没有任何规范的定义。
一般来说,命令式语言(例如 C++)倾向于使用父级方法。它简化了高效再平衡的实现,并且正如 Thomas Matthews 指出的那样,促进了常量-space 迭代器。
函数式语言(例如 Haskell)倾向于使用无父方法(参见 Purely Functional Data Structures)。由于不可能进行任何修改,所有重新平衡都是通过沿搜索路径重新复制来完成的,因此不需要后向指针。由于是强递归导向的,常量 space 迭代器的设计也不是什么大问题。
基本的二叉树(基础)需要child个指针:
struct binary_tree_node
{
binary_tree_node * left_child;
binary_tree_node * right_child;
};
可以对有助于搜索或存储的基础进行许多修改。
这些可以包括(但不限于):
- parent指针
- child 个指针数组
- "color"指标
- 专门的叶节点 -- 无 child 链接
便利性取决于数据结构的使用。例如,child 个节点的数组可能有助于加快 I/O 访问速度,其中读取 "page" 个节点与读取单个节点一样高效(参见 B-Tree)。 "color" 指标可能有助于平衡的决定。专门的 "leaf" 节点减少了树占用的内存量。
至于遍历,任何方法都可以遍历一棵树。没有规则阻止从 child 到 parent 的遍历。一些遍历可能包括兄弟姐妹到兄弟姐妹。
一些书籍或网站可能会挑剔传统或基本的 "binary tree" 数据结构。我发现限制妨碍了。
struct tree_node
{
tree_node* left_child;
tree_node* right_child;
int data; // here you can use whatever type or data you want. Even generic type
};
这取决于你的任务
说实话binary search tree
是一个概念,没有严格或标准的数据结构设计规则。但是要了解基本功能(例如插入、删除、查找等),人们会使用非常基本的数据结构,例如
struct Node
{
int data;
Node *left;
Node *right;
};
但是您的任务可能会针对不同的目的进行不同的设计。例如,如果您需要在单个操作中找到它的 parent node
,您可能会考虑将节点结构设计为
struct Node
{
int data;
Node *parent;
Node *left;
Node *right;
};
其他一些复杂的实现可能也需要存储同级列表。就像,
struct Node
{
int data;
Node *parent;
Node *left;
Node *right;
list<Node> *siblings;
};
所以,没有严格的标准
以下节点定义(在 Java 中)是针对平衡二叉树而不是 BST。
// Copyright (C) NNcNannara 2017
public class Node
{
public Node Left;
public Node Right;
public Node Parent;
public State Balance;
public Node()
{
Left = this;
Right = this;
Parent = null;
Balance = State.Header;
}
public Node(Node p)
{
Left = null;
Right = null;
Parent = p;
Balance = State.Balanced;
}
public Boolean isHeader ()
{ return Balance == State.Header; }
}
这针对平衡例程进行了优化。一个set节点派生Node的思路如下
// Copyright (C) NNcNannara 2017
public class SetNode<T> extends Node
{
public T Data;
public SetNode(T dataType, Node Parent)
{
super(Parent);
Data = dataType;
}
}
一个字典节点如下
// Copyright (C) NNcNannara 2017
public class DictionaryNode<K, T> extends Node
{
public T Data;
public K Key;
public DictionaryNode(K keyType, T dataType, Node Parent)
{
super(Parent);
Key = keyType;
Data = dataType;
}
}
平衡和迭代本质上是非通用的,并且是为基础 class 节点定义的。当然磁盘上也可能存在二叉树,其节点类型如下
package persistent;
public class Node
{
public long Left;
public long Right;
public long Parent;
public long Key;
public calculus.State Balance;
public Node()
{
Left = 0;
Right = 0;
Parent = 0;
Balance = calculus.State.Header;
Key = 0;
}
public Node(long p)
{
Left = 0;
Right = 0;
Parent = p;
Balance = calculus.State.Balanced;
Key = 0;
}
public Boolean IsHeader () { return Balance == calculus.State.Header; }
}
不是存在引用,而是存在节点和数据文件中的长整数偏移量。请注意,磁盘上的所有集合只有一种节点类型。