[英]c++ delete specific node with two children from binary search tree
我目前正在開發一個程序來處理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;
}
今天我已經重寫了很多次。 我需要它(顯然)刪除而不破壞樹的完整性,而樹的完整性現在正在這樣做。
首先,我讀了自己的Wikipedia文章“在二叉搜索樹中刪除” ,我在評論中建議這樣做。
發問者提到
我知道如何刪除有兩個孩子的節點的邏輯,但是該代碼目前對我不起作用。
如此看來,發問者相信能夠理解算法,但不知道如何正確實現最后一部分。 因此,我嘗試提供幫助:
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;
仔細查看整個函數,我意識到其余的似乎是錯誤的:
有一個root
未在OP樣本中公開? 缺少全局變量? 忘記重命名了?
我對當前節點的刪除方式感到困惑。 一方面,提供了指向當前節點的指針作為參考(我個人也將這樣做)。 另一方面,對於當前節點的替換,當前節點在父節點中標識(使用無效的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
和right
返回的指針node *tOld
不能復位。 這可能是(也可能不是)問題(取決於返回的指針如何進行后處理)。 偏執的開發人員[wc]將替換任何
return tOld;
通過
return tOld->left = tOld->right = nullptr, tOld;
該樣本包含
if (!t->left) { // special case: old right child becomes new top node } else {
顯然可以寫成
if (t->left) {
這是在移動代碼段時演變的。 我決定將其保留為當前狀態,因為對於入門級人員而言,整個代碼可能不容易理解。
OP沒有公開MCVE 。 因此,我采用了我的舊樣本並添加了以前不存在的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
BSTreePrint.h
–模板函數,用於以某種ASCII藝術打印二進制搜索樹:
#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
–一個測試程序,用於構建示例樹並刪除各個節點以測試個別情況:
#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;
}
在Windows 10上的cygwin中進行了編譯和測試:
$ g++ --version
g++ (GCC) 6.4.0
$ g++ -std=c++11 -o testRemove testRemove.cc
$ ./testRemove
Initial tree:
+---A
+-+-B
| +---C
+-+-D
| | +---E
| +-+-F
+-+-G
| | +---H
| | +-+-I
| +-+-J
| | +---K
| +-+-L
--+-M
| +---N
| +-+-O
| | +---P
| +-+-Q
| | | +---R
| | +-+-S
+-+-T
| +---U
| +-+-V
+-+-W
| +---X
+-+-Y
+---Z
Remove node '?': failed
+---A
+-+-B
| +---C
+-+-D
| | +---E
| +-+-F
+-+-G
| | +---H
| | +-+-I
| +-+-J
| | +---K
| +-+-L
--+-M
| +---N
| +-+-O
| | +---P
| +-+-Q
| | | +---R
| | +-+-S
+-+-T
| +---U
| +-+-V
+-+-W
| +---X
+-+-Y
+---Z
Remove node 'K': done
+---A
+-+-B
| +---C
+-+-D
| | +---E
| +-+-F
+-+-G
| | +---H
| | +-+-I
| +-+-J
| +---L
--+-M
| +---N
| +-+-O
| | +---P
| +-+-Q
| | | +---R
| | +-+-S
+-+-T
| +---U
| +-+-V
+-+-W
| +---X
+-+-Y
+---Z
Remove node 'I': done
+---A
+-+-B
| +---C
+-+-D
| | +---E
| +-+-F
+-+-G
| | +---H
| +-+-J
| +---L
--+-M
| +---N
| +-+-O
| | +---P
| +-+-Q
| | | +---R
| | +-+-S
+-+-T
| +---U
| +-+-V
+-+-W
| +---X
+-+-Y
+---Z
Remove node 'H': done
+---A
+-+-B
| +---C
+-+-D
| | +---E
| +-+-F
+-+-G
| +-+-J
| +---L
--+-M
| +---N
| +-+-O
| | +---P
| +-+-Q
| | | +---R
| | +-+-S
+-+-T
| +---U
| +-+-V
+-+-W
| +---X
+-+-Y
+---Z
Remove node 'J': done
+---A
+-+-B
| +---C
+-+-D
| | +---E
| +-+-F
+-+-G
| +---L
--+-M
| +---N
| +-+-O
| | +---P
| +-+-Q
| | | +---R
| | +-+-S
+-+-T
| +---U
| +-+-V
+-+-W
| +---X
+-+-Y
+---Z
Remove node 'G': done
+---A
+-+-B
| +---C
+-+-D
| | +---E
| +-+-F
+-+-L
--+-M
| +---N
| +-+-O
| | +---P
| +-+-Q
| | | +---R
| | +-+-S
+-+-T
| +---U
| +-+-V
+-+-W
| +---X
+-+-Y
+---Z
Remove node 'T': done
+---A
+-+-B
| +---C
+-+-D
| | +---E
| +-+-F
+-+-L
--+-M
| +---N
| +-+-O
| | +---P
| +-+-Q
| | | +---R
| | +-+-S
+-+-U
| +---V
+-+-W
| +---X
+-+-Y
+---Z
$
注意:
左邊的孩子印在父母的上方,右邊的孩子印在父母的上方。 我在准備測試代碼時意識到了這一點。 (因此,將頭向左旋轉以獲得樹的俯視圖是不起作用的,因為在這種情況下樹看起來是“鏡像的”。)請在檢查結果時記住這一點。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.