更新中…
目录
1二叉树的遍历
typedef struct node
{
int data;
struct node *lchild,*rchild;
}BTNode,BTREE;
1.1递归算法
1.1.1前序遍历的递归算法
void Pre_Order(BTREE T)
{
if(T!=NULL)
{
cout<<T->data<<" ";
Pre_Order(T->lchild);
Pre_Order(T->rchild);
}
}
1.1.2中序遍历的递归算法
void IN_Order(BTREE T)
{
if(T!=NULL)
{
In_Order(T->lchild);
cout<<T->data<<" ";
In_Order(T->rchild);
}
}
1.1.3后序遍历的递归算法
void Post_Order(BTREE T)
{
if(T!=NULL)
{
Pre_Order(T->lchild);
Pre_Order(T->rchild);
cout<<T->data<<" ";
}
}
1.1.4双序遍历
void BianLi(BTREE T)//双序遍历
{
if(T)
{
cout<<T->data;
BianLi(T->lchild );
cout<<T->data;
Bianli(T->rchild);
}
}
1.2非递归算法
1.2.1前序遍历的非递归算法
public void Pre_Order2(BTREE T){ //前序遍历非递归算法
BTREE p;
Stack<BTREE> stack = new Stack<BTREE>();
stack.push(T); //先将根节点压进栈中
while(T!=null&&!stack.empty()){
p = stack.pop(); //弹出栈顶的节点赋给p
System.out.println(p.data); //用输出来代替对节点的处理
if(p.rchild!=null){
stack.push(p.rchild); //如果弹出节点的右孩子不为空则压入栈
} //注意,这里的重点是一定要先将右孩子压入栈,再将左孩子压入
if(p.lchild!=null){
stack.push(p.lchild); //如果弹出节点的左孩子不为空则压入栈
}
}
1.2.2中序遍历的非递归算法
#define M 100
void In_Order2(BTREE T)
{
BTREE Stack[M],p=T;
int top=-1;
if(T!=NULL)
{
do
{
while(p!=NULL)
{
Stack[++top]=p;//将p所指节点地址入栈
p=p->lchild;//将p移动到左节点
}
p=Stack[top--];//退栈
Visit(p);//访问p所指节点
p=p->rchild;
}while(p!=NULL && top!=-1);
}
}
1.2.3层次遍历的非递归算法
基本思想:依次将每一层的节点进出队列。
void levelOrder(BTNode*& BT) {
SqQueue* q; //定义队列
initQueue(q); //初始化队列
if (BT != NULL) {
enQueue(q, BT); //根节点指针进队列
}
while (!emptyQueue(q)) { //队不为空循环
deQueue(q, BT); //节点出队
printf("%c", BT->data); //输出节点存储的值
if (BT->lchild != NULL) { //有左孩子时将该节点进队列
enQueue(q, BT->lchild);
}
if (BT->rchild != NULL) { //有右孩子时将该节点进队列
enQueue(q, BT->rchild);
} //一层一层的把节点存入队列
} //当没有孩子节点时就不再循环
}
2.二叉排序树
定义:一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的结点。
2.1二叉排序树的查找
步骤:若根结点的关键字值等于查找的关键字,成功。
否则,若小于根结点的关键字值,递归查左子树。
若大于根结点的关键字值,递归查右子树。
若子树为空,查找不成功。
BTREE SEARCH(BTREE T,int item)
{
BTREE p=T;
if(p==NULL)
{
return 0;
}
while(p!=NULL)
{
if(p->data==item)
{
return p;
}
else if(p->data<item)
//如果要查找的数据比该节点的数据大,说明要到其右子树查找
{
p=p->rchild;
}
else
{
p=p->lchild;
}
}
return 0;
}
2.2二叉排序树的插入
步骤:首先执行查找算法,找出被插结点的父亲结点。
判断被插结点是其父亲结点的左、右儿子。将被插结点作为叶子结点插入。
若二叉树为空。则首先单独生成根结点。
注意:新插入的结点总是叶子结点。
struct BiTree {
int data;
BiTree *lchild;
BiTree *rchild;
}*BTREE;
//在二叉排序树中插入查找关键字key
BTREE InsertBST(BTREE T,int key)
{
if (T == NULL)
{
T=(BTREE)malloc(sizeof(BiTree));
T->lchild = T->rchild = NULL;
T->data = key;
return T;
}
if (key < T->data)
T->lchild = InsertBST(T->lchild, key);
else
t->rchild = InsertBST(T->rchild, key);
return T;
}
//n个数据在数组d中,tree为二叉排序树根
BTREE CreateBiTree(BTREE T, int d[], int n)
{
for (int i = 0; i < n; i++)
T= InsertBST(T, d[i]);
}
2.3二叉排序树的删除
> > 思路: 在二叉排序树删去一个结点,分三种情况讨论:
> > 1.若*p结点为叶子结点,即PL(左子树)和PR(右子树)均为空树。则可以直接删除此子结点。
> >
> > 2.若*p结点只有左子树PL或右子树PR,此时只要令PL或PR直接成为其双亲结点*f的左子树(当*p是左子树)或右子树(当*p是右子树)即可,作此修改也不破坏二叉排序树的特性。
>
> 3.若*p结点的左子树和右子树均不空。在删去*p之后,为保持其它元素之间的相对位置不变,可按中序遍历保持有序进行调整,可以有两种做法:其一是令*p的左子树为*f的左/右(依*p是*f的左子树还是右子树而定)子树,*s为*p左子树的最右下的结点,而*p的右子树为*s的右子树;其二是令*p的直接前驱(或直接后继)替代*p,然后再从二叉排序树中删去它的直接前驱(或直接后继)-即让*f的左子树(如果有的话)成为*p左子树的最左下结点(如果有的话),再让*f成为*p的左右结点的父结点。
bool DeleteBST(BTREE &TParent,BTREE &T, int key)
/*若二叉排序树T中存在关键字等于key的数据元素时
则删除该数据元素,并返回TRUE;否则返回FALSE*/
{
if(!T)//不存在关键字等于key的数据元素
return false;
else
{
if(key == T->data.key)//找到关键字等于key的数据元素
return Delete(TParent, T);
else if(key < T->data.key)
return DeleteBST(T, T->lchild,key);
else
return DeleteBST(T, T->rchild,key);
}
return true;
}
bool Delete(BTREE& fp , BTREE&p)//从二叉排序树中删除结点p,并重接它的左或右子树
{
if(!p->rchild)//右子树空则只需重接它的左子树
{
fp->lchild = p->lchild;
delete(p);
}
else if(!p->lchild)//左子树空只需重接它的右子树
{
fp->rchild = p->rchild;
delete(p);
}
else//左右子树均不空
{
q=p;
fp->lchild = p->lchild;
s=p->lchild;//转左
while(s->rchild)//然后向右到尽头
{
q=s;
s=s->rchild;
} //此时q是s的父结点
s->rchild=p->rchild; //将s的左子树作为q的右子树
delete(p);
}
return true;
}
3. 二叉树中序遍历的下一个结点
题目描述:给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
分析:(1) 有右子树的,那么下个结点就是 右子树最左边的点;
(2) 没有右子树的,是父节点左孩子,那么 父节点就是下一个节点 ;
(3) 没有右子树的,是父节点的右孩子,找他的父节点的父节点的父节点…直到遍历到的结点是其父节点的左孩子位置, 返回该父结点。如果没有,那么他就是尾节点。
struct TNode
{
int data;
struct TNode *lchild;
struct TNode *rchild;
struct TNode *father;
TNode(int x):data(x),lchild(NULL),lchild(NULL),father(NULL){
}
}*BTREE;
BTREE GetNext(BTREE T)
{
if(T==NULL)
{
return NULL;
}
if(T->rchild)
{
//如果有右子树,下一个节点就是他右子树的最左子节点
T=T->rchild;
while(T->lchild!=NULL)
{
T=T->lchild;
}
return T;
}
else
{
//没有右子树,下一个节点是父节点或向上遍历,以遍历到的节点为左孩子的父节点
while(T->father)
{
if(T->father->lchild==T)
//若为父节点的左孩子,则父节点就是下一个节点
{
return T->father;
}
else
//往上查找直到当前节点为父节点的左节点
{
T=T->father;
}
}
return NULL;
}
}
4.二叉树的镜像
题目描述:
二叉树的镜像定义:源二叉树
8
/ \
6 10
/ \ / \
5 7 9 11
镜像二叉树
8
/ \
10 6
/ \ / \
11 9 7 5
void Is_Mirror( BTREE T )
{
if(T==NULL)
{
return;
}
if(T->lchild==NULL &&T->rchild==NULL)
{
return;
}
Swap(T->rchild,T->lchild);//交换左右节点
if(T->lchild)
{
Is_Mirror(T->lchild);//如果左子树非空,则镜像化左子树
}
if(T->rchild)
{
Is_Mirror(T->rchild);
}
}
5.是否为对称二叉树
/*将这棵树所有节点的左右子树交换,新树和原树对应位置
的结构相同且点权相等。即交换二叉树为原来的镜像*/
//判断某二叉树是否为对称二叉树
bool Is_Symmetric(BTREE T)
{
return Is_Mirror(T,T);
}
bool Is_Mirror(BTREE T1,BTREE T2)
{
//两节点为空时为true,当只有一节点为空时false,若两节点非空,则看左右子树是否对称
if(T1==NULL && T2==NULL)
{
return true;
}
else if(T1==NULL || T2==NULL)
{
return false;
}
else
{
return (T1->data==T2->data)&&(Is_Mirror(T->rchild,T->lchild))&&(T->lchild,T->rchild);
}
}
6.判断是否为平衡二叉树
描述:平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
思路:通过depth发放递归获得左右子树的深度,若是平衡二叉树,则返回真实深度,否则返回-1
bool isBalanced(BTREE T)
{
if(Depth(T)==-1)
{
return false;
}
else
{
return true;
}
}
int Depth(BTREE T)
{
if(T==NULL)
{
return 0;
}
int left=Depth(T->lchild);//返回左子树的深度
if(left==-1)
{
return -1;
}
int right=Depth(T->rchild);
if(right==-1)
{
return -1;
}
int result=abs(right-left);//计算左右子树的深度之差
if(result>1)
{
return false;
}
else
{
return 1+max(right,left);
}
}
7.求二叉树的最大深度
int MaxDepth(BTREE T)
{
if(T==NULL)
{
return 0;//递归出口
}
int left_depth=MaxDepth(T->lchild);
int right_depth=MaxDepth(T->rchild);
return 1+max(left_depth,right_depth);
}
8.树中两个节点的最低公共祖先
分为三种情况:
1.若该是为二叉搜索树,说明左子树的节点都比父节点小,右子树节点都比父节点大,则只需从根节点开始与输入的两个节点比较。
2.该树为二叉树,但有指向父节点的指针,分别找到从两个结点开始到根结点的路径,即两个链表;然后找到两个链表的第一个公共结点,就是最低的公共父结点;
3.普通二叉树,用DFS算法,分别找到到达两个结点的路径;
然后 找到两条路径的最后一个公共结点 ,就是最低的公共父结点。
8.1若为二叉搜索树
BTREE lowestAncestor(BTREE T,BTREE q,BTREE p)
{
if(!T)
return NULL;
//从根结点开始递归
if ( T -> data > max ( p -> data , q -> data )) //如果p,q在当前结点左子树,则对左子树遍历
return lowestAncestor ( T -> left , p , q );
else if ( T -> data < min ( p -> data , q -> data )) //如果p,q在当前结点右子树,则对右子树遍历
return lowestAncestor ( T -> right , p , q );
else //如果当前结点在p,q之间,则为最低公共父结点(因为从上往下在遍历,故遇到的第一个满足此条件的就是)
return T ;
}
8.2若存在指向父节点的指针
BTREE lowestAncestor(BTREE T,BTREE q,BTREE p)
{
if(!T)
return NULL;
if(q->father && p->father)
{
q=q->father;
p=p->father;
if(q==p)
{
return p;
}
}
}
8.3普通二叉树
BTREE lowestAncestor(BTREE T,BTREE q,BTREE p)
{
if(T==NULL || p==T ||q==T)
{
return T;
}
BTREE left = lowestAncestor ( T -> left , p , q );
//找左子树中是否存在p或q,存在时返回p或q或者p,q的祖先结点,不存在时返回nullptr
BTREE right = lowestAncestor ( T -> right , p , q );
//找右子树中是否存在p或q
if ( left && right )
//left和right分别有p或q,则其父结点T为p,q的最低公共祖先
return T ;
else
return left ? left : right ;
//left同时有p,q,则left为公共祖先,(若left为空,则right为公共祖先)
}
9.将有序数组转换为二叉搜索树
题目描述:
数组:[-10,-3,0,5,9],
答案(不唯一):
0
/
-3 9
/ /
-10 5
思路:分治法,类似于快速排序,转化成一个height-balanced的二叉查找树,所以数组中点就是二叉查找树的根结点。
写递归思路:先写递归的主体,再写起点输入,再写递归的出口return语句(尤其注意递归的子函数的出口)
BTREE SortedArrayToTree(vector<int> &nums)
{
return BuildTree(nums,0,nums.size()-1);
}
BTREE BuildTree(vector<int> &nums,int start,int end)
{
if(start>end)
{
//递归函数出口
return NULL;
}
int mid=(start+end+1)/2;//若不是整数,取较大的那个数作为根
BTREE T=(BTREE)malloc(sizeof(TNode));
T->data=nums[mid];//构建根节点
T->lchild=BuildTree(nums,start,mid-1);
T->rchild=BuildTree(nums,mid+1,end);
return T;
}
10.验证是否为二叉搜索树
思路:二叉搜索树经过中序遍历后,结果为升序排列
bool IsValidBST(BTREE T)
{
if(T==NULL)
return true;
stack<BTREE> s;//用栈来存储树的信息
BTREE p=T;
BTREE pre=NULL;//pre节点用来与p节点比较,判断是否升序
while(!s.empty() || p)//如果栈为空或者树为空
{
if(p)
{
s.push(p);
p=p->lchild;//将左子树的值入栈
}
else
{
p = s . top ();//访问栈顶元素
if(pre != NULL && p->data <= pre->data)
return false; //看是否为升序
pre = p; //保存已经访问的结点
s . pop ();
p = p -> right ;//将右子树的值入栈
}
}
return true ;
}
}
11.树的子结构
题目描述:输入两棵二叉树A和B,判断B是不是A的子结构。
解题思路:分为两步:第一步在树A中查找是否存在和B的根节点一样的节点R,第二步判断A中以R为根节点的子树是不是包含和B中一样的结构,根据树的特性,本题可采用递归求解
bool HasSubtree(BTREE root1,BTREE root2)
{
bool result=false;
if(root1!=NULL && root2!=NULL)
{
if(root1->data==root2->data)
{
result=DoesTree1HaveTree2(root1,root2);
}
if(!result)
{
result=HasSubtree(root1->lchild,root2);
}
if(!result)
{
result=HasSubtree(root1->rchild,root2);
}
}
return result;
}
bool DoesTree1HaveTree2(BTREE root1,BTREE root2)
{
if(root1==NULL && root2==NULL)
{
return true;
}
if(root1->data!=root2->data)
return false;
return DoesTree1HaveTree2(root1->lchild,root2->lchild)&&DoesTree1HaveTree2(root1->rchild,root2->rchild);
}
12判断是否为二叉搜索树的后序遍历序列
思路:以数组[5,7,6,9,11,10,8]为例,后序遍历结果最后一个数8就是根节点的值,前三个数5,7,6比8小是值为8的左子树节点,而9,11,10比8大,是值为8的节点的右子树节点。这实际上是一个递归的过程,分析5,6,7,最后一个数字6是左子树根节点的值,而5是值为6的节点的左子节点,7是它的右子节点,同样,9,11,10中9是值为10的节点的左子节点,11是值为10的节点的右子节点。
bool VerifyArraysOFBST(int Arrays[],int length)
{
if(Arrays==NULL || length<=0)
return false;
int root=Arrays[length-1];//root是根节点
//搜索左子树的节点小于根节点
for(int i=0;i<length;i++)
{
if(Arrays[i]>root)
{
break;
}
}
//搜索右子树节点大于根节点
for(int j=i;j<length;j++)
{
if(Arrays[j]<root)
{
return false;
}
}
//判断左子树是不是二叉搜索树
bool left=true;
if(i>0)
{
left=VerifyArraysOFBST(Arrays,length);
}
bool right=true;
if(i<length-1)
{
right=VerifyArraysOFBST(Arrays+i,length-i-1);
}
return (left&&right);
}
13判断平衡二叉树
题目描述:输入一颗二叉树的根节点,判断该数是不是平衡二叉树某二叉树的任意节点的左右子树的深度相差不超过1,则是一颗平衡二叉树。
解题思路:求出每个节点的左右子树的深度,若每个节点的左右子树的深度相差不超过1则它是一棵平衡二叉树。
int TreeDepth(BTREE T)
{
if(T==NULL)
{
return 0;
}
int left=TreeDepth(T->lchild);
int right=TreeDepth(T->rchild);
return (left>right)?(left+1):(right+1);
}
bool IsBalanced(BTREE T)
{
if(T==NULL)
{
return true;
}
int left=TreeDepth(T->lchild);
int right=TreeDepth(T->rchild);
int result=left-right;
if(result>1 || result<-1)
{
return false;
}
return IsBalanced(T->lchild)&&IsBalanced(T->rchild);
}