排序规则:定义一个vector数组,将数组中的第一个值作为根节点,此后将后面的值与根节点的值比较,如果小于根节点的值,则放在根节点的左边,如果大于根节点的值,放在根的右边。以此类推,根节点的左子树(右子树)又是一棵二叉树。此外,在二叉树排序中不允许出现两个相同的值。当二叉树完成之后,可以用中序遍历的方式将它从小到达排序。
定义结构体BstNode和BSTree分别定义了它们的属性和根节点,这里会增加一个双亲指针,它可以让我们的子节点指向它的父节点,但是这样也会降低它的存储密度。即原来12个字节中可以存储4个字节的有效数据,现在需要16个字节的空间来存放4个字节的有效数据。
typedef int ElemType;
typedef struct BstNode{
BstNode* left;//左节点
BstNode* parent;//双亲
BstNode* right;//右节点
ElemType data;//数据域
}BtNode;
typedef struct{
BstNode* root;//根节点
int cursize;//节点个数
};
插入元素
思想1:首先判断我们要插入的这颗二叉树是否为空树,如果为空树,我们就需要为其购买一个节点,然后将val值赋值给它。这时这个val值就是它的根节点。定义两个指针,用来指向树的根节点和双亲。**注意指向根节点的指针一定要在确认不为空树的情况下定义。**当根节点的数据域不等于val值时,让双亲指针指向根节点。然后将val值判断,如果val大于根节点指向的数据,就放在右子树,否则放在左子树。这是一个循环,目的是让指针指向空,也就是val的藏身之所。找到val要存放的位置后,让它和它的双亲指针比较大小来做最后的定夺。
bool Insert_BSTree(BSTree& myt,ElemType val)
//这里要对数组进行插入,不会改变这个元素,所以对它进行引用
{
if(myt.root==NULL)
{
myt.root=buynode();
myt.root->data=val;
myt.cursize+=1;
return true;
}
BstNode* p=myt.root;
BstNode* pa=NULL;
while(p!=NULL&&p->data!=val)//这是一个循环,目的是找到val的双亲
{
pa=p;
p=val<p->data?p->left:p->right;
}
if(p!=NULL&&p->data==val)//找到了val值,这在二叉树中是不允许的,直接return false
{
return false;
}
p=buynode();
p->data=val;
p->parent=pa;
if(p->data<pa->data)
{
pa->left=p;
}
else
{
pa->right=p;
}
myt.cursize+=1;
return true;
}
思想2:刚进入函数时先不用判空,定义p和pa,其中p指向根节点。然后让p根据val值往后跑,找到它的双亲节点。如果找到val值,直接return。否则购买一个节点让p指向pa,这里需要做一个判断,如果pa为空,说明这是一个空树,此时我们让刚申请好的节点作为根节点,如果这不是一个空树,进行判断。
bool Insert_BSTree(BSTree& myt,ElemType val)
{
BstNode* p=myt.root;
BstNode* pa=NULL;
while(p!=NULL&&p->data!=val)
{
pa=p;
p=val<p->data?p->left:p->right;
}
if(p!=NULL&&p->data==val)
{
return false;
}
p=buynode();
p->data=val;
p->parent=pa;
if(pa==NULL)
{
myt.root=p;
}
else{
if(p->data<pa->data)
{
pa->left=p;
}
else{
pa->right=p;
}
}
myt.cursize+=1;
return true;
}
二叉树的删除
思想:首先如果我们要删除的是一个空树,那就直接return。如果不是一个空树,我们首先要去寻找这个要删除的值。这里分三种情况:
删除的是叶子节点
删除的是单支节点
删除的是双支节点
如果我们要删除的是一个双分支节点,那么我们首先要找到这个节点的下一个节点(中序遍历),找到这个节点后,将这个节点覆盖掉我们要删除的节点
如果是单分支节点或者叶子节点,首先要找到它的双亲节点和它的一个子节点,我们首先判断它的子节点是否为空,如果不为空,那么这是一个单分支节点,让子节点指向父节点,否则这是一个叶子节点,判断父节点的左孩子是否等于要删除的节点,如果是,让父节点的左边指向空,否则父节点的右边指向空,然后将这个节点释放掉。
bool ReMove_Value(BSTree& myt,ElemType val)
{
if(myt.root==NULL) return false;
BstNode*p=FindValue(myt,val)//查找要删除的val值
if(p==NULL) return false//没有找到,直接return
if(p->left!=NULL&&p->right!=NULL)//如果命中,说明这是一个双分支节点
{
BstNode* tmp=Next(p->right)//找到它的下一个节点
p->data=tmp->data;//覆盖
p=tmp;
}
BstNode* pa=p->parent;//定义父指针
BstNode* child=p->left!=NULL?p->left:p->right;//找到它唯一的子节点
if(child!=NULL) child->parent=pa;//如果命中,说明这是一个单分支节点,让child直接指向pa
if(pa==NULL)//如果p的双亲节点为空
{
myt.root=p;//那么这个p指针就是当前的根节点
}
if(pa->left==p)
{
pa->left=NULL;
}
else{
pa->right=NULL;
}
FreeNode(p);
myt.cursize-=1;
return true;
}
二叉树的双向链表
非递归中序遍历
思想:利用中序遍历的思想,如果ptr不为空,栈不满且ptr->left不为空,压栈。直到ptr为空,说明找到了中序的头节点,(如果head此时为空)让head和nt指针同时指向头节点。否则将栈中元素出栈,此时nt指向的是出栈元素的左孩子,让nt的右孩子指向出栈元素,出栈元素的左孩子指向nt。此时nt和出栈元素相互指向,将ptr赋值给nt,遍历它的右孩子。
BtNode* NiceInOrder(BtNode* ptr)
{
if(ptr==NULL) return NULL;
stack<BtNode*> st;
BtNode* head=NULL;
BtNode* nt=NULL;
while(ptr!=NULL||!st.empty())
{
while(ptr!=NULL)
{
st.push(ptr);
ptr=ptr->left();
}
ptr=st.top();st.pop();
if(head==NULL)
{
head=ptr;
nt=ptr;
}
else{
nt->right=ptr;
ptr->left=nt;
nt=ptr;
}
ptr=ptr->right;
}
return head;
}
递归中序遍历
void InOrder_DbList(BtNode* ptr,BtNode* pre,BtNode* head)
{
if(ptr!=NULL)
{
InOrder_DbList(ptr->left,pre,head);
if(pre!=NULL)
{
pre->right=ptr;
ptr->left=pre;
}
else{
head=ptr;
}
pre=ptr;
InOrder_DbList(ptr->right,pre,head);
}
}
BtNode* InOrder_Lt(BtNode* ptr)
{
BtNode* pre=NULL;
BtNode* head=NULL;
InOrder_DbList(ptr,pre,head);
return head;
}
非递归逆向中序遍历
BtNode* HNiceInorder(BTNode* ptr)
{
if(ptr==NULL) return NULL;
stzck<BtNode*> st;
BtNode* head=NULL;
BtNode* nt=NULL;
while(ptr!=NULL||!st.empty())
{
while(ptr!=NULL)
{
st.push(ptr);
ptr=ptr->left;
}
ptr=st.top();st.pop();
if(head==NULL)
{
head=ptr;
}
else{
nt=ptr->right;
head->left=ptr;
ptr->right=head;
head=ptr;
}
ptr=nt;
}
}