本文主要来讲解二叉排序树的构建、遍历(非递归实现)以及结点删除的算法。
1、构建二叉排序树
1.1引言
对于一个已经排序好的顺序表来说,进行查找其中的某个元素,采用二分查找的效率非常高,但是如果要插入或者删除元素的话,就可能要移动大量的元素。对链表进行插入和删除不需要移动元素,效率很高,但是链表不能进行二分查找。因此,如何来解决这个问题呢,这时就需要构建二叉排序树了。
1.2什么是二叉排序树
二叉排序树是一棵空树或者是满足下列条件的的二叉树:
1)若左子树不空,则左子树上所有节点的值均小于或等于它的根节点的值;
2)若右子树不空,则右子树上所有节点的值均大于或等于它的根节点的值;
3)左、右子树也分别为二叉排序树。
1.3如何构建二叉排序树
以数据集第一个元素为根节点,之后将比根节点小的元素放在左子树中,将比根节 点大的元素放在右子树中,在左右子树中同样采取此规则。那么在查找x时,若 x比根节点小可以排除右子树所有元素, 去左子树中查找(类似二分查找),这样查找的效率非常好,而且插入的时间复杂度为O(h),h为树的高度,较O(n) 来说效率提高不少。故二叉搜索树用作一些查找和插入使用频率比较高的场景。
如下列数据:
构建的二叉排序树:
2、二叉树的遍历
以下将会讲解二叉树的前序遍历、中序遍历、后序遍历以及层序遍历,采用非递归的方式(由于递归实现较为容易,而且递归的效率很低,在此就不做讲解)。
2.1前序遍历
前序遍历 - 先访问根节点,然后前序遍历左子树,再前序遍历右子树
非递归方法需要用到一个栈来辅助遍历。
代码如下:
void preVisit(BTree *root)
{
if (root == NULL) return;
stack<const BTreeNode *> nodeStack;
nodeStack.push(root); //先将根节点压入栈
const BTreeNode *tmp;
while (!nodeStack.empty())
{
tmp = nodeStack.top(); //弹出栈中结点,先访问
nodeStack.pop();
cout << tmp->data << "--";
if (tmp->RChild != NULL) //如果该结点的左右儿子存在,则将其压入栈
{
nodeStack.push(tmp->RChild);
}
if (tmp->LChild != NULL)
{
nodeStack.push(tmp->LChild);
}
}
cout << endl;
}
2.2中序遍历
中序遍历 - 先访问根节点的左子树,然后访问根节点,最后遍历右子树
中序遍历跟前序遍历差不多
代码如下:
void midVisit(const BTree *root)
{
if (root == NULL) return;
stack<const BTreeNode *> nodeStack; //创建一个栈
const BTreeNode *tmp = root;
while (tmp!=NULL||!nodeStack.empty())
{
while (tmp != NULL)
{
nodeStack.push(tmp);
tmp = tmp->LChild;
}
if (!nodeStack.empty())
{
tmp = nodeStack.top();
nodeStack.pop();
cout << tmp->data << "--";
tmp = tmp->RChild;
}
}
cout << endl;
}
2.3后序遍历
后序遍历 - 从左到右,先叶子后节点的方式遍历访问左右子树,最后访问根节点
后续遍历较为复杂,需要两个栈来辅助遍历。
树中任一结点q都需要进栈三次,出栈三次。第一次出栈是为遍历q的左子树,第二次出栈是为了遍历右子树,第三次出栈是为了访问结点q。第一个栈用来存结点,第二个栈用来存该节点进出栈的次数。
代码如下:
void lastVisit1(const BTree *root)
{
if (root == NULL) return;
stack<const BTreeNode*> nodeStack;
stack<int> nodeNum;
int count;
nodeStack.push(root);
nodeNum.push(0);
while (!nodeStack.empty())
{
root = nodeStack.top();
nodeStack.pop();
count = nodeNum.top();
nodeNum.pop();
if (count == 0)
{
nodeStack.push(root);
nodeNum.push(1);
if (root->LChild != NULL)
{
nodeStack.push(root->LChild);
nodeNum.push(0);
}
}
else if (count == 1)
{
nodeStack.push(root);
nodeNum.push(2);
if (root->RChild != NULL)
{
nodeStack.push(root->RChild);
nodeNum.push(0);
}
}
else if (count == 2)
{
cout << root->data << "--";
}
}
cout << endl;
}
2.4层序遍历
层序遍历 - 从根节点从上往下逐层遍历,在同一层,按从左到右的顺序对节点逐个访问
层序遍历需要一个队列来进行辅助遍历。
代码如下:
void levelVisit(const BTree *root)
{
if (root == NULL) return;
queue<const BTreeNode *> queNode;
queNode.push(root);
while (!queNode.empty())
{
root = queNode.front();
queNode.pop();
cout << root->data << "--";
if (root->LChild != NULL) queNode.push(root->LChild);
if (root->RChild != NULL) queNode.push(root->RChild);
}
cout << endl;
}
3、二叉树删除结点
将要删除的节点的值,与节点root节点进行比较,若小于则去到左子树进行比较,若大于则去到右子树进行比较,重复以 上操作直到找到一个节点的值等于删除的值,则将此节点删除。删除时有4中情况须分别处理:
###3.1
3.2
3.3
3.4
代码如下:
bool deleteNode(BTree *root, DataType data)
{
if (root == NULL) return false;
BTreeNode *parent;
BTreeNode *temp = root;
isRorL RorL = LEFT; //记录要删除的结点是左儿子还是右儿子
parent = temp;
while (temp->data != data)
{
parent = temp;
if (temp->data > data)
{
temp = temp->LChild;
RorL = LEFT;
}
else if (temp->data < data)
{
temp = temp->RChild;
RorL = RIGHT;
}
if (temp == NULL)
{
cout << "error,没有此数据结点,无法删除" << endl;
return false;
}
}
//要删除的结点没有左儿子且没有右儿子
if (temp->LChild == NULL && temp->RChild == NULL)
{
if (RorL == LEFT) parent->LChild = NULL;
else parent->RChild = NULL;
delete temp;
}
else if (temp->LChild == NULL) //要删除的结点只有右儿子
{
if (RorL == LEFT) parent->LChild = temp->RChild;
else parent->RChild = temp->RChild;
delete temp;
}
else if (temp->RChild == NULL) //要删除的结点只有左儿子
{
if (RorL == LEFT) parent->LChild = temp->LChild;
else parent->RChild = temp->LChild;
delete temp;
}
else
{
BTreeNode *maxDataNode = temp->LChild;
parent = temp;
while (maxDataNode->RChild != NULL)
{
parent = maxDataNode;
maxDataNode = maxDataNode->RChild;
}
temp->data = maxDataNode->data;
if (parent->LChild == maxDataNode)
{
parent->LChild = maxDataNode->LChild;
}
else if (maxDataNode->LChild != NULL)
{
parent->RChild = maxDataNode->LChild;
}
else
parent->RChild = NULL;
delete maxDataNode;
}
return true;
}
完整代码实现:
#include <iostream>
#include <stack>
#include <queue>
using namespace std;
#define isLess(BTree1,BTree2) (BTree1->data < BTree2->data)
typedef int DataType;
enum isRorL
{
RIGHT,
LEFT
};
typedef struct BSTree
{
DataType data;
BSTree *LChild;
BSTree *RChild;
}BTree,BTreeNode;
bool insertBTree(BTree **root, BTreeNode *treeNode)
{
if (!treeNode)
{
return false;
}
else
{
treeNode->LChild = NULL;
treeNode->RChild = NULL;
}
BTreeNode *parent = NULL;
BTreeNode *temp = NULL;
//bool LorR = true; //true 为左,false 为右
isRorL RorL = LEFT;
if (!(*root))
{
*root = treeNode;
return true;
}
else
{
temp = *root;
}
while (temp)
{
parent = temp;
if (isLess(treeNode, temp))
{
RorL = LEFT;
temp = temp->LChild;
}
else
{
temp = temp->RChild;
RorL = RIGHT;
}
}
if (RorL==LEFT)
{
parent->LChild = treeNode;
}
else
{
parent->RChild = treeNode;
}
return true;
}
bool deleteNode(BTree *root, DataType data)
{
if (root == NULL) return false;
BTreeNode *parent;
BTreeNode *temp = root;
isRorL RorL = LEFT; //记录要删除的结点是左儿子还是右儿子
parent = temp;
while (temp->data != data)
{
parent = temp;
if (temp->data > data)
{
temp = temp->LChild;
RorL = LEFT;
}
else if (temp->data < data)
{
temp = temp->RChild;
RorL = RIGHT;
}
if (temp == NULL)
{
cout << "error,没有此数据结点,无法删除" << endl;
return false;
}
}
//要删除的结点没有左儿子且没有右儿子
if (temp->LChild == NULL && temp->RChild == NULL)
{
if (RorL == LEFT) parent->LChild = NULL;
else parent->RChild = NULL;
delete temp;
}
else if (temp->LChild == NULL) //要删除的结点只有右儿子
{
if (RorL == LEFT) parent->LChild = temp->RChild;
else parent->RChild = temp->RChild;
delete temp;
}
else if (temp->RChild == NULL) //要删除的结点只有左儿子
{
if (RorL == LEFT) parent->LChild = temp->LChild;
else parent->RChild = temp->LChild;
delete temp;
}
else
{
BTreeNode *maxDataNode = temp->LChild;
parent = temp;
while (maxDataNode->RChild != NULL)
{
parent = maxDataNode;
maxDataNode = maxDataNode->RChild;
}
temp->data = maxDataNode->data;
if (parent->LChild == maxDataNode)
{
parent->LChild = maxDataNode->LChild;
}
else if (maxDataNode->LChild != NULL)
{
parent->RChild = maxDataNode->LChild;
}
else
parent->RChild = NULL;
delete maxDataNode;
}
return true;
}
//前序遍历
void preVisit(BTree *root)
{
if (root == NULL) return;
stack<const BTreeNode *> nodeStack;
nodeStack.push(root);
const BTreeNode *tmp;
while (!nodeStack.empty())
{
tmp = nodeStack.top();
nodeStack.pop();
cout << tmp->data << "--";
if (tmp->RChild != NULL)
{
nodeStack.push(tmp->RChild);
}
if (tmp->LChild != NULL)
{
nodeStack.push(tmp->LChild);
}
}
cout << endl;
}
//中序遍历
void midVisit(const BTree *root)
{
if (root == NULL) return;
stack<const BTreeNode *> nodeStack;
const BTreeNode *tmp = root;
while (tmp!=NULL||!nodeStack.empty())
{
while (tmp != NULL)
{
nodeStack.push(tmp);
tmp = tmp->LChild;
}
if (!nodeStack.empty())
{
tmp = nodeStack.top();
nodeStack.pop();
cout << tmp->data << "--";
tmp = tmp->RChild;
}
}
cout << endl;
}
//后序遍历
void lastVisit(const BTree *root)
{
if (root == NULL) return;
stack<const BTreeNode*> nodeStack;
stack<int> nodeNum;
int count;
nodeStack.push(root);
nodeNum.push(0);
while (!nodeStack.empty())
{
root = nodeStack.top();
nodeStack.pop();
count = nodeNum.top();
nodeNum.pop();
if (count == 0)
{
nodeStack.push(root);
nodeNum.push(1);
if (root->LChild != NULL)
{
nodeStack.push(root->LChild);
nodeNum.push(0);
}
}
else if (count == 1)
{
nodeStack.push(root);
nodeNum.push(2);
if (root->RChild != NULL)
{
nodeStack.push(root->RChild);
nodeNum.push(0);
}
}
else if (count == 2)
{
cout << root->data << "--";
}
}
cout << endl;
}
//层序遍历
void levelVisit(const BTree *root)
{
if (root == NULL) return;
queue<const BTreeNode *> queNode;
queNode.push(root);
while (!queNode.empty())
{
root = queNode.front();
queNode.pop();
cout << root->data << "--";
if (root->LChild != NULL) queNode.push(root->LChild);
if (root->RChild != NULL) queNode.push(root->RChild);
}
cout << endl;
}
int main()
{
BTree *root = NULL;
BTreeNode *treeNode;
DataType data[] = { 19,7,25,5,11,15,21,61 };
for (int i = 0; i < 8; i++)
{
treeNode = new BTreeNode;
treeNode->data = data[i];
insertBTree(&root, treeNode);
}
cout << "前序遍历结果为:" << endl;
preVisit(root);
cout << "中序遍历结果为:" << endl;
midVisit(root);
cout << "后序遍历结果为:" << endl;
lastVisit(root);
cout << "层序遍历结果为:" << endl;
levelVisit(root);
deleteNode(root, 25);
cout << "删除25后,前序遍历结果:" << endl;
preVisit(root);
cout << "删除25后,中序遍历结果为:" << endl;
midVisit(root);
cout << "删除25后,后序遍历结果:" << endl;
lastVisit(root);
cout << "删除25后,层序遍历结果:" << endl;
levelVisit(root);
system("pause");
return 0;
}