目录
一:本周总结:
本周我看完了二叉树,刚看到其他同学的博客已经看完了线段树,感觉自己落后了,下周争取看完AVL树
二:二叉树:
1.1:树的递归定义:
a:空结构是一棵树。
b:如果t1.t2.....tk是不相交的树,那么,根节点以t1.t2......tk作为子节点的数据结构也是一棵树
c:只有通过a,b产生的数据结构才是树
注:和现实生活中的树不同,这里的树是倒过来的,根在顶部
1.2:有序二叉树
对于树的每一个节点n,其左子树中的值小于节点n中的值v,其右子树的值大于节点n中的值。
1.2.1二叉树的查找
最坏情况是树转化为链表,复杂度O(n);
将要定位的键值与房前所指向的节点的存储值进行比较,如果键值小于存储值,则转向左子树,如果键值比存储值大,则转向右子树,如果二者相同,搜索可以终止了。如果没有其他节点,搜索也可以终止了,表示键值不在该树中。
template<typename T>
T* BST<T>::BSTsearch(BSTNode<T>* p, const T& el) const
{
while (p!=0)
{
if (el == p->key)
{
return &p->k;
}
else if (el < p->key)
{
p = p->left;
}
else
{
p = p->right;
}
}
return nullptr;
}
1.2.2 二叉树的遍历
节点有多少种排列方式,就有多少种遍历方法,对于有n个节点的树,共有n!种遍历方式,他们大多数无序且混乱。很显然,这样的遍历是毫无意义的。比较有用的是广度优先遍历和深度优先遍历
广度优先遍历:
使用队列进行树的遍历,假设从上到下,从左到右进行广度优先遍历,使用队列,在访问一个节点后,他的子节点就放到队列的末尾,然后访问队列头部的节点。对于层次为n的节点,它的子节点位于第n+1层,如果将该节点的所有子节点都放到了队尾,那么,这些节点就将在第n层节点访问后再访问。
template<typename T>
void BST<T>::breadFirst()
{
queue<BSTNode<T>*>queue;
BSTNode<T>* p = root;
if (p != 0)
{
queue.push(p);
}
while (!queue.empty())
{
p = queue.front();
queue.pop();
cout << p->key << " ";
if (p->left != 0)
{
queue.push(p->left);
}
if (p->right != 0)
{
queue.push(p->right);
}
}
}
深度优先遍历:
深度优先遍历分为前序遍历,中序遍历,后序遍历三种遍历方式
一下是用双重递归实现的三种遍历方式
template<typename T>
void BST<T>::innorder(BSTNode<T>* p)
{
if (p != 0)
{
innorder(p->left);
cout << p->key << " ";
}
}
template<typename T>
void BST<T>::preorder(BSTNode<T>* p)
{
if (p != 0)
{
cout << p->key << " ";
preorder(p->left);
preorder(p->right);
}
}
template<typename T>
void BST<T>::postorder(BSTNode<T>* p)
{
if (p != 0)
{
postorder(p->left);
postorder(p->right);
cout << p->key << " ";
}
}
1.2.3 二叉树的插入
和搜索树相同,在扫描树的过程中,比较键值el与当前节点的键值,如果el小于该键值,就测试当前节点的左节点,否则,就测试当前节点的右节点。如果要测试的p的子节点为空,就停止扫描,使新节点成为p的子节点。
template<class T>
void BST<T>::insert(const T& el) {
BSTNode<T> *p = root, *prev = 0;
while (p != 0) { // find a place for inserting new node;
prev = p;
if (p->key < el)
p = p->right;
else p = p->left;
}
if (root == 0) // tree is empty;
root = new BSTNode<T>(el);
else if (prev->key < el)
prev->right = new BSTNode<T>(el);
else prev->left = new BSTNode<T>(el);
}
1.2.4 合并删除、
找到左子树具有最小值的节点,使之成为右子树的父节点。同理 可以赵右子树的最小值的节点,使之成为左子树的父节点。
把指针一直按照一个方向移动,直到遇到空指针。
template<class T>
void BST<T>::deleteByMerging(BSTNode<T>*& node) {
BSTNode<T> *tmp = node;
if (node != 0) {
if (!node->right) // node has no right child: its left
node = node->left; // child (if any) is attached to its parent;
else if (node->left == 0) // node has no left child: its right
node = node->right; // child is attached to its parent;
else { // be ready for merging subtrees;
tmp = node->left; // 1. move left
while (tmp->right != 0)// 2. and then right as far as possible;
tmp = tmp->right;
tmp->right = // 3. establish the link between the
node->right; // the rightmost node of the left
// subtree and the right subtree;
tmp = node; // 4.
node = node->left; // 5.
}
delete tmp; // 6.
}
}
template<class T>
void BST<T>::findAndDeleteByMerging(const T& el) {
BSTNode<T> *node = root, *prev = 0;
while (node != 0) {
if (node->key == el)
break;
prev = node;
if (node->key < el)
node = node->right;
else node = node->left;
}
if (node != 0 && node->key == el)
if (node == root)
deleteByMerging(root);
else if (prev->left == node)
deleteByMerging(prev->left);
else deleteByMerging(prev->right);
else if (root != 0)
cout << "key " << el << " is not in the tree\n";
else cout << "the tree is empty\n";
}
1.2.5 复制删除
果要删除的节点有两个子节点,该问题就可以简化为如下两个简单问题之一:要删除的节点是叶子节点,或要删除的节点只有一个非空子节点。为此,可以用该节点的直接前驱(或后继)替换要删除的键。如合并删除算法所示,键值的前驱是其左子树中最右侧节点的键值(同样,它的直接后继是其右子树中最左侧节点中的键值)。首先需要定位前驱。为此,仍然需要向左移-步,首先找到要删除节点的左子树的根,再尽可能地向右移动。接下来,用找到的节点的键值替换要删除的键值。这时需要区分两种简单情况:如果最右侧的节点是一个叶子节点,就是第一种情况;如果它有一个子节点,就是第二种情况。这样,复制删除算法用键值k2覆盖键值kq,从而删除键值k1,再删除包含键值k2的节点;而合并删除算法会删除键值k1以及包含该键值的节点。
template<class T>
void BST<T>::deleteByCopying(BSTNode<T>*& node) {
BSTNode<T> *previous, *tmp = node;
if (node->right == 0) // node has no right child;
node = node->left;
else if (node->left == 0) // node has no left child;
node = node->right;
else {
tmp = node->left; // node has both children;
previous = node; // 1.
while (tmp->right != 0) { // 2.
previous = tmp;
tmp = tmp->right;
}
node->key = tmp->key; // 3.
if (previous == node)
previous->left = tmp->left;
else previous->right = tmp->left; // 4.
}
delete tmp; // 5.
}
// findAndDeleteByCopying() searches the tree to locate the node containing
// el. If the node is located, the function DeleteByCopying() is called.
template<class T>
void BST<T>::findAndDeleteByCopying(const T& el) {
BSTNode<T> *p = root, *prev = 0;
while (p != 0 && !(p->key == el)) {
prev = p;
if (p->key < el)
p = p->right;
else p = p->left;
}
if (p != 0 && p->key == el)
if (p == root)
deleteByCopying(root);
else if (prev->left == p)
deleteByCopying(prev->left);
else deleteByCopying(prev->right);
else if (root != 0)
cout << "key " << el << " is not in the tree\n";
else cout << "the tree is empty\n";
}
1.2.6树的平衡
低效率的算法(额外数组 重建树)
template<class T>
void BST<T>::balance (T data[], int first, int last) {
if (first <= last) {
int middle = (first + last)/2;
insert(data[middle]);
balance(data,first,middle-1);
balance(data,middle+1,last);
}
}
DSW算法:
if Par 不是树的根节点
子节点CH的祖父节点Gr成为Ch的父节点;
Ch的右子树成为Ch的父节点Par的左子树;
节点Ch把Par作为它的右子节点;