我目前正在开发一个用c ++处理BST的程序。我的所有函数当前都在工作,但removeNode除外,它删除了树中给定键值的节点。我认为前两个案例有效,但第三个案件给我带来了麻烦。我知道如何删除有两个孩子的节点的逻辑,但目前代码对我不起作用。这是节点
struct node{
int key;
node* left;
node* right;
};
以下是删除功能的代码,底部有一个以上子节点的节点
node* Tree::removeKey(node*& t, int k)
{
if(t == nullptr){
return root;
}
else if(k < t->key){
root->left = removeKey(t->left, k);
}
else if (k > t->key){
root->right = removeKey(t->right, k);
}
else {
node* parent = getParent(root, k);
// Case 1: No child
if(t->left == nullptr && t->right == nullptr) {
if(parent->left->key == t->key){
parent->left = nullptr;
return t;
}
if(parent->right->key == t->key){
parent->right = nullptr;
return t;
}
}
//Case 2: One child
else if(t->left != nullptr && t->right == nullptr) {
parent->left = t->left;
parent->right = nullptr;
return t;
}
else if(t->right != nullptr && t->left == nullptr) {
parent->right = t->right;
parent->left = nullptr;
return t;
}
// case 3: 2 children
else {
// node *temp = nullptr;
// temp->key = findMin(root->right);
// t->key = temp->key;
// t->right = removeKey(t->right,temp->key);
// return t;
}
}
还有,辅助函数getParent,
node* Tree::getParent(node* t, int k)
{
if(t == nullptr){
return nullptr;
}else
if((t->left != nullptr && k == t->left->key ) ||
(t->right != nullptr && k == t->right->key)){
return t;
}else
if(k < t->key){
return getParent(t->left, k);
}
if(k > t->key){
return getParent(t->right, k);
}
return nullptr;
}
今天我已经多次重写这篇文章了。我需要它删除(显然)而不破坏它正在做的树的完整性。
答案 0 :(得分:1)
首先,我自己阅读了我在评论中推荐的二元搜索树中的维基百科文章Deletion。
提问者提到
我知道如何删除有两个孩子的节点的逻辑,但目前代码对我不起作用。
所以看来,提问者相信理解算法,但不知道如何正确实现最后一部分。因此,我试着帮助:
node *tOld = t; // remember current top node (which shall be removed)
// find left-most child in right sub-tree
t = t->right;
if (!t->left) {
// special case: old right child becomes new top node
} else {
// traverse right sub-tree left down
node *parent = t; // old parent of new top node
for (; t->left; parent = t, t = t->left);
// swap nodes
parent->left = t->right; // old parent of left most child gets right child (or nullptr)
t->right = tOld->right; // new top node gets old right sub-tree
}
t->left = tOld->left; // new top node gets old left sub-tree
// return remove node
return tOld;
仔细观察整个功能,我意识到剩下的似乎是错误的:
在OP的样本中有一个root
没有公开?缺少全局变量?忘了重命名?
我对如何删除当前节点感到困惑。一方面,提供当前节点的指针作为参考(我个人也会这样做)。另一方面,为了替换当前节点,在父节点中标识当前节点(使用无效的getParent()
辅助函数)。这不是必需的,因为可以直接更改节点指针(并且将影响原始指针)。这就是 (node* &t
)的原因。
风格问题:
if (t == nullptr)
可以写成if (t)
if (t != nullptr)
可以写成if (!t)
。
所以,我完全回顾了这个功能:
node* Tree::removeKey(node *&t, int k)
{
// This happens if key is not found.
if (!t) return t;
// This is traversal by recursion to find the node to remove.
if (k < t->key) return removeKey(t->left, k);
if (k > t->key) return removeKey(t->right, k);
// This is the case where node with k has been found:
node *tOld = t; // remember current top node for return
// Case 1: No child
if (!t->left && !t->right) {
/* Override t
* if t is root -> tree becomes empty
* if t is left of parent node -> parent->left becomes empty
* if t is right of parent node -> parent->right becomes empty
*/
t = nullptr;
return tOld;
}
// Case 2: One child
if (t->left && !t->right) {
t = t->left;
return tOld;
}
if (t->right && !t->left) {
t = t->right;
return tOld;
}
// Case 3: 2 children
// find left-most child in right sub-tree
t = t->right;
if (!t->left) {
// special case: old right child becomes new top node
} else {
// traverse right sub-tree left down
node *parent = t; // old parent of new top node
for (; t->left; parent = t, t = t->left);
// swap nodes
parent->left = t->right; // old parent of left most child gets right child (or nullptr)
t->right = tOld->right; // new top node gets old right sub-tree
}
t->left = tOld->left; // new top node gets old left sub-tree
return tOld;
}
removeKey()
返回已删除的节点(如果未找到密钥,则返回nullptr
)。这很重要,因为可能需要一些后处理来释放节点。如果删除的节点是使用new
创建的,则必须为delete
d。 (否则,将为任何已删除的节点生成内存泄漏。)
未重置返回的left
的{{1}}和right
指针。这可能是也可能不是问题(取决于返回的指针是如何进行后处理的)。偏执的开发者[wc]可以取代任何一个
node *tOld
通过
return tOld;
样本包含
return tOld->left = tOld->right = nullptr, tOld;
显然可以缩写为
if (!t->left) {
// special case: old right child becomes new top node
} else {
这是在改变代码片段的过程中演变而来的。我决定将其置于当前状态,因为对于有入门级别的人来说,整个代码可能不容易理解。
OP没有公开MCVE。因此,我采用了我的一个旧样本并添加了if (t->left) {
之前不存在的内容:
BSTreeT::remove()
- 二叉搜索树的模板类:
BSTreeT.h
#ifndef B_S_TREE_T_H
#define B_S_TREE_T_H
/* provides a less functor which simply wraps operator < for a certain
* type
*
* VALUE ... C++ type of value to less-compare
*/
template <typename VALUE>
struct lessFunc {
bool operator()(const VALUE &value1, const VALUE &value2) const
{
return value1 < value2;
}
};
/* provides a class template for a binary search tree.
*
* KEY ... C++ type of the key values of nodes
* VALUE ... C++ type of the other values of nodes
* COMP ... C++ type of
*/
template <typename KEY, typename VALUE, typename COMP = lessFunc<KEY> >
class BSTreeT {
public:
// node type
class Node {
/* This friend shall ensure that the corresponding
* BSTreeT template class may access private _pLeft and _pRight.
*/
friend class BSTreeT<KEY, VALUE, COMP>;
public:
// the key value of node (used to define an order)
const KEY key;
// other values of nodes
VALUE value;
private:
// pointers to left and right child nodes
Node *_pLeft, *_pRight;
private: // Construction/destruction is for exclusive use of BSTreeT.
// constructor.
Node(const KEY &key, const VALUE &value):
key(key), value(value), _pLeft(nullptr), _pRight(nullptr)
{ }
// destructor.
~Node() { delete _pLeft; delete _pRight; }
// disabled:
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
public:
// returns pointer to left child node (or nullptr if there is none).
const Node* getLeft() const { return _pLeft; }
// returns pointer to right child node (or nullptr if there is none).
const Node* getRight() const { return _pRight; }
};
public:
// less functor used to compare node keys
const COMP ∁
private:
// root pointer
Node *_pRoot;
public:
/* constructor.
*
* comp ... a less comparator to define order of nodes
*/
explicit BSTreeT(const COMP &comp = COMP()):
comp(comp), _pRoot(nullptr)
{ }
// destructor.
~BSTreeT() { delete _pRoot; }
// disabled:
BSTreeT(const BSTreeT&) = delete;
BSTreeT& operator=(const BSTreeT&) = delete;
public:
/* inserts a node.
*
* key ... the key value of node
* value ... the other value of node
* return: true ... key/value inserted
* false ... Error! Possible reasons:
* - duplicated key
* - allocation of node failed.
*/
bool insert(const KEY &key, const VALUE &value)
{
return insert(_pRoot, key, value);
}
/** removes a node.
*
* key ... the key value of node to remove
* return: true ... key/value inserted
* false ... Error! Possible reasons:
* - key not found
*/
bool remove(const KEY &key)
{
return remove(_pRoot, key);
}
/* provides a functor-like type which is applied to every node
* in traverse().
*
* If an instance of this class is provided the traverse() does nothing
* else than the pure traversal.
*/
struct Apply {
// pre-order access to node
virtual void preNode(Node &node) { }
// in-order access to node
virtual void inNode(Node &node) { }
// post-order access to node
virtual void postNode(Node &node) { }
};
/* traverses the tree and applies the provided object to every node.
*
* apply ... the action object applied to every node
*/
void traverse(Apply &apply)
{
if (_pRoot) traverse(_pRoot, apply);
}
/* provides a functor-like type which is applied to every const node
* in traverse().
*
* If an instance of this class is provided the traverse() does nothing
* else than the pure traversal.
*/
struct ConstApply {
// pre-order access to node
virtual void preNode(const Node &node) { }
// in-order access to node
virtual void inNode(const Node &node) { }
// post-order access to node
virtual void postNode(const Node &node) { }
};
/* traverses the tree and applies the provided object to every node.
*
* apply ... the action object applied to every node
*/
void traverse(ConstApply &apply) const
{
if (_pRoot) traverse(_pRoot, apply);
}
private:
// inserts a node.
bool insert(Node *&pTree, const KEY &key, const VALUE &value)
{ /* Every if-branch ends with return.
* Thus, no explict else is needed.
*/
if (!pTree) { /* (!pTree) ... (pTree == nullptr) */
return !!(pTree = new Node(key, value));
}
if (comp(key, pTree->key)) return insert(pTree->_pLeft, key, value);
if (comp(pTree->key, key)) return insert(pTree->_pRight, key, value);
return false;
}
// removes a node.
bool remove(Node *&pNode, const KEY &key)
{
// This happens if key is not found.
if (!pNode) return false;
// This is traversal by recursion to find the node to remove.
if (key < pNode->key) return remove(pNode->_pLeft, key);
if (key > pNode->key) return remove(pNode->_pRight, key);
// This is the case where node with key has been found:
Node *pNodeOld = pNode; // remember current node for delete
// Case 1: No child
if (!pNode->_pLeft && !pNode->_pRight) pNode = nullptr;
/* Override pNode
* if pNode is _pRoot -> tree becomes empty
* if pNode is _pLeft of parent node -> parent->_pLeft becomes empty
* if pNode is _pRight of parent node -> parent->_pRight becomes empty
*/
// Case 2: One child
else if (pNode->_pLeft && !pNode->_pRight) pNode = pNode->_pLeft;
else if (pNode->_pRight && !pNode->_pLeft) pNode = pNode->_pRight;
// Case 3: 2 children
else {
// find left-most child in right sub-tree
pNode = pNode->_pRight;
if (pNode->_pLeft) {
// traverse right sub-tree left down
Node *pParent = pNode; // old parent of new top node
for (; pNode->_pLeft; pParent = pNode, pNode = pNode->_pLeft);
// swap nodes
pParent->_pLeft = pNode->_pRight; // old parent of left most child gets right child (or nullptr)
pNode->_pRight = pNodeOld->_pRight; // new top node gets old right sub-tree
} // else: special case: old right child becomes new top node
// new top node gets old left sub-tree
pNode->_pLeft = pNodeOld->_pLeft;
}
// delete old node
pNodeOld->_pLeft = pNodeOld->_pRight = nullptr;
delete pNodeOld;
// done with success
return true;
}
// tries to find a node by key.
Node* find(Node *pTree, const KEY &key) const
{
if (comp(key, pTree->key)) {
return pTree->_pLeft ? find(pTree->_pLeft, key) : nullptr;
}
if (comp(pTree->key, key)) {
return pTree->_pRight ? find(pTree->_pRight, key) : nullptr;
}
return pTree;
}
// traverses the tree.
void traverse(Node *pTree, Apply &apply)
{
apply.preNode(*pTree);
if (pTree->_pLeft) traverse(pTree->_pLeft, apply);
apply.inNode(*pTree);
if (pTree->_pRight) traverse(pTree->_pRight, apply);
apply.postNode(*pTree);
}
// traverses the tree.
void traverse(const Node *pTree, ConstApply &apply) const
{
apply.preNode(*pTree);
if (pTree->_pLeft) traverse(pTree->_pLeft, apply);
apply.inNode(*pTree);
if (pTree->_pRight) traverse(pTree->_pRight, apply);
apply.postNode(*pTree);
}
};
#endif // B_S_TREE_T_H
- 用某种ASCII艺术打印二叉搜索树的模板函数:
BSTreePrint.h
#ifndef B_S_TREE_PRINT_H
#define B_S_TREE_PRINT_H
#include <cassert>
#include <iostream>
#include <string>
#include "BSTreeT.h"
namespace {
/* a derived tree-traversal action
* for graphical (i.e. ASCII-art) pre-order output of tree
*/
template <typename K, typename V, typename C>
struct PrintPreT: public BSTreeT<K, V, C>::ConstApply {
typedef BSTreeT<K, V, C> Tree;
std::ostream &out;
std::string indent;
explicit PrintPreT(std::ostream &out): out(out), indent(" ") { }
~PrintPreT() = default;
PrintPreT(const PrintPreT&) = delete;
PrintPreT operator=(const PrintPreT&) = delete;
virtual void preNode(typename Tree::Node const &node)
{
indent.pop_back(); char c = indent.back(); indent.pop_back();
out << indent << "+-"
<< (node.getLeft() || node.getRight() ? '+' : '-')
<< '-' << node << '\n';
indent += c; indent += ' ';
indent += node.getRight() ? "| " : " ";
}
virtual void inNode(typename Tree::Node const &node)
{
indent.pop_back(); indent.pop_back();
indent += " ";
}
virtual void postNode(typename Tree::Node const &node)
{
indent.pop_back(); indent.pop_back();
}
};
/* a derived tree-traversal action
* for graphical (i.e. ASCII-art) in-order output of tree
*/
template <typename K, typename V, typename C>
struct PrintInT: public BSTreeT<K, V, C>::ConstApply {
typedef BSTreeT<K, V, C> Tree;
std::ostream &out;
std::string indent;
explicit PrintInT(std::ostream &out): out(out), indent(" ") { }
~PrintInT() = default;
PrintInT(const PrintInT&) = delete;
PrintInT operator=(const PrintInT&) = delete;
virtual void preNode(typename Tree::Node const&)
{
indent += " ";
}
virtual void inNode(typename Tree::Node const &node)
{
popIndent();
const char l = popIndent() == ' ' ? '|' : ' ';
const bool root = indent.empty();
out << indent
<< (root ? "--" : "+-")
<< (node.getLeft() || node.getRight() ? "+-" : "--")
<< node << '\n';
indent += root ? ' ' : l; indent += ' ';
indent += "| ";
}
virtual void postNode(typename Tree::Node const&)
{
popIndent();
}
char popIndent()
{
indent.pop_back(); const char c = indent.back(); indent.pop_back();
return c;
}
};
} // namespace
template <typename K, typename V, typename C>
std::ostream& printPre(
std::ostream &out, const BSTreeT<K, V, C> &tree)
{
PrintPreT<K, V, C> printer(out);
tree.traverse(printer);
return out;
}
template <typename K, typename V, typename C>
std::ostream& printIn(
std::ostream &out, const BSTreeT<K, V, C> &tree)
{
PrintInT<K, V, C> printer(out);
tree.traverse(printer);
return out;
}
enum BSTreePrintStyle {
PrintBSTreePreOrder,
PrintBSTreeInOrder
};
template <typename K, typename V, typename C>
std::ostream& print(
std::ostream &out, const BSTreeT<K, V, C> &tree,
BSTreePrintStyle style = PrintBSTreePreOrder)
{
switch (style) {
case PrintBSTreePreOrder: return printPre(out, tree);
case PrintBSTreeInOrder: return printIn(out, tree);
default: assert(false);
}
return out;
}
#endif // B_S_TREE_PRINT_H
- 用于构建示例树并删除各种节点以测试各个案例的测试程序:
testRemove.cc
在Windows 10上的cygwin中进行了编译和测试:
#include <iostream>
#include "BSTreeT.h"
#include "BSTreePrint.h"
using namespace std;
// template instances (for convenience)
struct Empty { };
typedef BSTreeT<char, Empty>::Node BSTreeNode;
typedef BSTreeT<char, Empty> BSTree;
ostream& operator<<(ostream &out, const BSTreeNode &node)
{
return out << node.key;
}
ostream& operator<<(ostream &out, const BSTree &tree)
{
return printIn(out, tree);
}
// recursive method to build balanced tree
void buildTree(BSTree &tree, char begin, char end)
{
char middle = (begin + end) / 2;
tree.insert(middle, Empty());
if (begin < middle) buildTree(tree, begin, middle);
if (middle < end) buildTree(tree, middle + 1, end);
}
// helper function
void remove(BSTree &tree, char key)
{
cout << "Remove node '" << key << "': "
<< (tree.remove(key) ? "done" : "failed") << '\n'
<< tree << endl;
}
int main()
{
BSTree tree;
buildTree(tree, 'A', 'Z');
cout << "Initial tree:\n" << tree << endl;
// Test Cases
// test key not found
remove(tree, '?');
// test case 1
remove(tree, 'K');
// test case 2
remove(tree, 'I');
remove(tree, 'H'); // intermediate step (case 1)
remove(tree, 'J');
// test cases 3
remove(tree, 'G');
remove(tree, 'T');
// done
return 0;
}
注意:
左边的孩子印在父母身上,右下面的孩子。我在准备测试代码时意识到了这一点。 (因此,将树头向左转以获得树木的自上而下的视图在树木出现时不起作用#34;镜像&#34;在这种情况下。)请在检查结果时记住这一点。 / p>