二叉搜索树(Binary Search Tree)
一. 定义, 性质
二叉查找树(Binary Search Tree),也称二叉搜索树、有序二叉树(ordered binary tree),排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树:
- 若任意节点的左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;
- 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 任意节点的左、右子树也分别为二叉查找树;
- 没有键值相等的节点(no duplicate nodes)
- 中序遍历为递增序列
搜索、插入、删除的复杂度等于树高,期望O(logN),最坏O(N)(数列有序,树退化成线性表)。
二. 查询操作
Node* search(int key) { Node* n = root; while(n!=NULL && key!=n->data) { if (key<n->data) n = n->left; else n = n->right; } return n; } // 找到以x为根的(子)树的最小结点 Node* minimum(Node* x) { if (x == 0) return 0; while (x->left!=NULL) x = x->left; return x; } // find the minimum node in the whole tree Node* minimum() {return minimum(root);} // 找到以x为根的(子)树的最大结点 Node* maximum(Node* x) { if (x == 0) return 0; while (x->right!=NULL) x = x->right; return x; } // find the maximum node in the whole tree Node* maximum() {return maximum(root);}
三. 插入操作
插入的步骤如下:
1) 若树是空树, 则申请一个新节点, 赋给root
2) 若在树中找到key, 说明树中已经有该字段, 不能再插入, 否则
3) 若key小于当前结点的值, 则检查左孩子. 否则
4) 若key大于当前结点的值, 则检查右孩子.
递归算法:
Node* BinarySearchTree::insert(int key, Node*& n) { if (n == 0) { n = new Node(key); return n; } else if (key < n->data) return insert(key, n->left); else if (key > n->data) return insert(key, n->right); else return n; }
非递归算法:
Node* BinarySearchTree::insert_iterative(int key) { if (root == 0) { root = new Node(key); return root; } Node* n = root; // parent总是指向当前结点的父节点 Node* parent = 0; while (n != 0) { parent = n; if (key < n->data) n = n->left; else if (key > n->data) n = n->right; else; } if (key < parent->data) { parent->left = new Node(key); return parent->left; } else { parent->right = new Node(key); return parent->right; } }
四. 删除操作
由于删除操作要保证不破坏二叉搜索树树的性质, 因此要分两种情况考虑
1. 至多有一棵非空子树:
如果左子树非空, 那么用左子树替换当前结点. 否则用右子树替换当前结点.
2. 有两棵非空子树:
要转化为第一种情况------用该节点的直接后继来代替该节点, 并在该节点的右子树中删除其直接后继. 由于中序遍历的直接后继必然没有左子树, 因此能够转化为第一种情况
递归:
void BinarySearchTree::remove(int key, Node*& n) { if (n == 0) return; if (key < n->data) remove(key, n->left); else if (key > n->data) remove(key, n->right); else if (n->left != 0 && n->right != 0) //having two children { // 由于节点没有parent属性, 用重新安排指针的方法来移除结点很困难. // 因此把直接后继结点的值赋给待删除结点 // 并且在右子树中删除该直接后继结点 n->data = minimum(n->right)->data; remove(n->data, n->right); } else { Node* tmp = n; n = (n->left != 0) ? n->left : n->right; delete tmp; } }
非递归:
void BinarySearchTree::remove_iterative(int key) { // STEP 1 : 找到待删除结点和它的父节点 #pragma region STEP 1 Node *replacer, *successor, *successor_parent, *toRemove, *parent_toRemove; toRemove = root; parent_toRemove = 0; while (toRemove != NULL && key != toRemove->data) { parent_toRemove = toRemove; if (key < toRemove->data) toRemove = toRemove->left; else toRemove = toRemove->right; } if (toRemove == 0) return; #pragma endregion STEP 1 #pragma region STEP 2 : 如果是第二种情况 if (toRemove->left && toRemove->right) { successor = toRemove->right; successor_parent = toRemove; while (successor->left) { successor_parent = successor; successor = successor->left; } // 把直接后继的值赋给待删除结点 toRemove->data = successor->data; // 待删除结点改为直接后继 toRemove = successor; parent_toRemove = successor_parent; } #pragma endregion if (toRemove->left) replacer = toRemove->left; else replacer = toRemove->right; if (toRemove == root) root = replacer; else if (toRemove == parent_toRemove->left) parent_toRemove->left = replacer; else parent_toRemove->right = replacer; delete toRemove; }