二叉树的结点表示
左子树,右子树,结点数据
typedef struct TNode *Position;
typedef Position BinTree; /* 二叉树类型 */
struct TNode{ /* 树结点定义 */
ElementType Data; /* 结点数据 */
BinTree Left; /* 指向左子树 */
BinTree Right; /* 指向右子树 */
};
二叉树的遍历(递归)
printf根节点的数据;
中序遍历,左子树->根节点->右子树;
void InorderTraversal( BinTree BT )
{
if( BT ) {
InorderTraversal( BT->Left );
/* 此处假设对BT结点的访问就是打印数据 */
printf("%d ", BT->Data); /* 假设数据为整型 */
InorderTraversal( BT->Right );
}
}
前序遍历,根节点->左子树->右子树;
void PreorderTraversal( BinTree BT )
{
if( BT ) {
printf("%d ", BT->Data );
PreorderTraversal( BT->Left );
PreorderTraversal( BT->Right );
}
}
后序遍历,左子树->右子树->根节点;
void PostorderTraversal( BinTree BT )
{
if( BT ) {
PostorderTraversal( BT->Left );
PostorderTraversal( BT->Right );
printf("%d ", BT->Data);
}
}
层序遍历——类似BFS
创建队列,从根节点开始入队,子树从左至右入队;
结点出队后输出,并且左右子树顺序入队,即可完成层序遍历;
void LevelorderTraversal ( BinTree BT )
{
Queue Q;
BinTree T;
if ( !BT ) return; /* 若是空树则直接返回 */
Q = CreatQueue(); /* 创建空队列Q */
AddQ( Q, BT );
while ( !IsEmpty(Q) ) {
T = DeleteQ( Q );
printf("%d ", T->Data); /* 访问取出队列的结点 */
if ( T->Left ) AddQ( Q, T->Left );
if ( T->Right ) AddQ( Q, T->Right );
}
}
二叉搜索树
1.插入结点
将X插入BST中;
若结点为空,创建节点对象,X赋值给data;(终止条件)
若结点不为空,比较X和节点的data大小,X小则往左子树找,X大则往右子树找;
直到找到子树为空的结点,插入该空位即可;
BinTree Insert( BinTree BST, ElementType X )
{
if( !BST ){ /* 若原树为空,生成并返回一个结点的二叉搜索树 */
BST = (BinTree)malloc(sizeof(struct TNode));
BST->Data = X;
BST->Left = BST->Right = NULL;
}
else { /* 开始找要插入元素的位置 */
if( X < BST->Data )
BST->Left = Insert( BST->Left, X ); /*递归插入左子树*/
else if( X > BST->Data )
BST->Right = Insert( BST->Right, X ); /*递归插入右子树*/
/* else X已经存在,什么都不做 */
}
return BST;
}
2.结点删除函数
在BST中删除X;
通过比较找到X,若直到空结点都未找到X,说明不存在要删除的X;
若找到X,需分类讨论:
(1)要删除的结点有左右子树:
从左子树中找到最大值或者从右子树中找到最小值结点,填充被删除的结点位置,删除该最大,最小结点(最大最小结点必只有一个子树或没有子树)
(2)要删除的结点有一个和子树或没有子树
若只有右子树:右子树接替被删除的结点的位置;
若只有左子树:左子树接替被删除的结点的位置;
若没有子树:被删除的结点位置变为空结点;
BinTree Delete( BinTree BST, ElementType X )
{
Position Tmp;
if( !BST )
printf("要删除的元素未找到");
else {
if( X < BST->Data )
BST->Left = Delete( BST->Left, X ); /* 从左子树递归删除 */
else if( X > BST->Data )
BST->Right = Delete( BST->Right, X ); /* 从右子树递归删除 */
else { /* BST就是要删除的结点 */
/* 如果被删除结点有左右两个子结点 */
if( BST->Left && BST->Right ) {
/* 从右子树中找最小的元素填充删除结点 */
Tmp = FindMin( BST->Right );
BST->Data = Tmp->Data;
/* 从右子树中删除最小元素 */
BST->Right = Delete( BST->Right, BST->Data );
}
else { /* 被删除结点有一个或无子结点 */
Tmp = BST;
if( !BST->Left ) /* 只有右孩子或无子结点 */
BST = BST->Right;
else /* 只有左孩子 */
BST = BST->Left;
free( Tmp );
}
}
}
return BST;
}
平衡二叉树——AVL树
改善二叉搜索树,使得树不会一边高一边低;
1.树结点
增加树高的记录;
左子树+右子树+结点数据;
typedef struct AVLNode *Position;
typedef Position AVLTree; /* AVL树类型 */
struct AVLNode{
ElementType Data; /* 结点数据 */
AVLTree Left; /* 指向左子树 */
AVLTree Right; /* 指向右子树 */
int Height; /* 树高 */
};
防止树一边高一边低的方法
在插入结点时调整;
若插入新结点导致上面一节点的高度差变为2,需要根据新节点和老节点的位置进行相应调整;
(1)新节点在老节点的右子树的右子树,进行RR旋转,即左旋;
老节点的右子树变为原右子树的左子树;
老节点的右子树的左子树变为老节点;
更新老节点的树高和老节点的右子树的书高;
AVLTree rightright(AVLTree A) //右右 ——左旋
{
AVLTree B=A->right;
A->right=B->left;
B->left=A;
A->height=max(getheight(A->left),getheight(A->right));
B->height=max(getheight(B->right),A->height);
return B;
}
(2)新节点在老节点的左子树的左子树,进行LL旋转,即右旋;
老节点的左子树指向老节点左子树的右子树;
老节点的左子树的右子树指向老节点;
更新老节点的树高和老节点的左子树的树高;
AVLTree leftleft(AVLTree A){ //左左 ——右旋
AVLTree B=A->left;
A->left=B->right;
B->right=A;
//计算高度
A->height=max(getheight(A->left),getheight(A->right));
B->height=max(getheight(B->left),A->height);
//要写一个getheight函数——递归得到给定节点的height
return B;
}
(3)新节点在老节点的左子树的右子节点上,即LR旋转,(对L子树进行RR,再对自己进行LL);
AVLTree leftright(AVLTree A)
{
//先把左边左旋,再把自己右旋
A->left=rightright(A->left);
return leftleft(A);
}
先对老节点的左子树进行RR旋转,即左旋;
再对老节点进行LL旋转,即右旋;
AVLTree DoubleLeftRightRotation ( AVLTree A )
{ /* 注意:A必须有一个左子结点B,且B必须有一个右子结点C */
/* 将A、B与C做两次单旋,返回新的根结点C */
/* 将B与C做右单旋,C被返回 */
A->Left = SingleRightRotation(A->Left);
/* 将A与C做左单旋,C被返回 */
return SingleLeftRotation(A);
}
(4)新节点在老节点的右子树的左节点上,即RL旋转,(对R子树进行LL,再对自己进行RR)
先对老节点的右子树进行LL旋转,即右旋;
再对老节点机械能RR旋转,即左旋;
AVLTree rightleft(AVLTree A)
//先把右边的右旋,再把自己左旋
{
A->right=leftleft(A->right);
return rightright(A);
}
2.插入函数
与搜索二叉树相同,找到空结点后插入值为X的新结点;
在插入的过程中每次更新经过的结点的左右高度差,当高度差为2时,插入节点往左子树的左边走时使用LL旋转,往左子树的右边走时使用LR旋转;
同理添加RR旋转和RL旋转;
插入后需要更新根节点的高度;
AVLTree Insert( AVLTree T, ElementType X )
{ /* 将X插入AVL树T中,并且返回调整后的AVL树 */
if ( !T ) { /* 若插入空树,则新建包含一个结点的树 */
T = (AVLTree)malloc(sizeof(struct AVLNode));
T->Data = X;
T->Height = 0;
T->Left = T->Right = NULL;
} /* if (插入空树) 结束 */
else if ( X < T->Data ) {
/* 插入T的左子树 */
T->Left = Insert( T->Left, X);
/* 如果需要左旋 */
if ( GetHeight(T->Left)-GetHeight(T->Right) == 2 )
if ( X < T->Left->Data )
T = SingleLeftRotation(T); /* 左单旋 */
else
T = DoubleLeftRightRotation(T); /* 左-右双旋 */
} /* else if (插入左子树) 结束 */
else if ( X > T->Data ) {
/* 插入T的右子树 */
T->Right = Insert( T->Right, X );
/* 如果需要右旋 */
if ( GetHeight(T->Left)-GetHeight(T->Right) == -2 )
if ( X > T->Right->Data )
T = SingleRightRotation(T); /* 右单旋 */
else
T = DoubleRightLeftRotation(T); /* 右-左双旋 */
} /* else if (插入右子树) 结束 */
/* else X == T->Data,无须插入 */
/* 别忘了更新树高 */
T->Height = Max( GetHeight(T->Left), GetHeight(T->Right) ) + 1;
return T;
}
3.getheight函数
递归方式遍历结点,得到结点高度
int getheight(AVLTree A)
{
if(!A) return 0;
return max(getheight(A->left),getheight(A->right))+1;
}