每日算法之3
1.之二叉树前中唯一构建二叉树[重点]
//问题描述:使用二叉树的前序和中序遍历序列唯一重建一棵二叉树
//算法分析:根据二叉树前序序列的特点,第一个节点为根节点;;然后根据中序序列的特点,一旦找到根节点的位置,其前面所有节点都是左子树的序列,其后面的序列都是右子树的序列;;;通过前序和中序就可使用递归唯一构建出一棵二叉树;;
typedef struct BTNode{
char data;
struct BTNode *lchild,*rchild;
}BTNode;
BTNode* createBTree_usePreInorder_useRecursion(
char* preorder_start,char* preorder_end,
char* inorder_start,char* inorder_end //表示输入两个序列的开头及结尾参数的指针
)
{
//使用前序遍历确定根节点
BTNode* root;
char rootData;
root=new BTNode;
rootData=preorder_start[0];//表示将序列的第一个节点赋值给根节点的数据值
root->lchild=root->rchild=NULL;//初始化根节点
root->data=rootData;
//当递归到前序只有一个时,即叶子节点,到达递归边界直接将节点返回
if(preorder_start==preorder_end){
if(inorder_start==inorder_end && *preorder_start==*inorder_start)
return root; //为了保证递归到边界时两个序列相互对应相等
else
throw "error";
}
//使用中序遍历序列确定根节点的位置
char* root_pos_inorder;//表示根节点在中序遍历中的位置
root_pos_inorder=inorder_start;
while(root_pos_inorder<=inorder_end && *root_pos_inorder!=rootData)
++root_pos_inorder;//找到根节点在中序遍历中的位置
if(root_pos_inorder==inorder_end && *root_pos_inorder!=rootData){
throw "error";
}//当找不到根节点的位置时应该抛出错误!!
//确定左子树序列,和右子树序列
int leftLength=root_pos_inorder-inorder_start;
int rightLength=inorder_end-root_pos_inorder;
if(leftLength>0){
//构建左子树
root->lchild=createBTree_usePreInorder_useRecursion(
preorder_start+1,preorder_start+leftLength,
inorder_start,root_pos_inorder-1
);
}
if(rightLength>0){
//构建右子树
root->rchild=createBTree_usePreInorder_useRecursion(
preorder_start+leftLength+1,preorder_end,
root_pos_inorder+1,inorder_end
);
}
return root;
}
BTNode* createBTree_usePreInorder(char* preorder,char* inorder,int length){
//保证算法的健壮性,排除空指针,和空数据的测试用例
if(preorder==NULL || inorder==NULL || length<=0)
return NULL;
//排除空指针
return createBTree_usePreInorder_useRecursion(
preorder,preorder+length-1,
inorder,inorder+length-1
);
//递归函数的大问题是输入二叉树的前序和后序序列的开头和结尾指针,开始递归构建二叉树
}
//可以看出使用递归构建二叉树时间复杂度为创建节点的个数O(n),空间复杂度为树的深度O(n),最坏的情况
void preorder(BTNode *B){
if(B!=NULL){
cout<<B->data<<" ";
if(B->lchild!=NULL)
preorder(B->lchild);
if(B->rchild!=NULL)
preorder(B->rchild);
}
}
void inorder(BTNode *B){
if(B!=NULL){
if(B->lchild!=NULL)
inorder(B->lchild);
cout<<B->data<<" ";
if(B->rchild!=NULL)
inorder(B->rchild);
}
}
void postorder(BTNode *B){
if(B!=NULL){
if(B->lchild!=NULL)
postorder(B->lchild);
if(B->rchild!=NULL)
postorder(B->rchild);
cout<<B->data<<" ";
}
}
int main(){
char pre[]="ABDGHCEF";
char in[]="GDHBAECF";
BTNode *root;
try{
root=createBTree_usePreInorder(pre,in,8);
}catch(const char* e){
cout<<e;
}
preorder(root);cout<<endl;
inorder(root);cout<<endl;
postorder(root);cout<<endl;
}
2.之二叉树的八大递归遍历及非递归实现,使用尾递归?
//二叉树的递归遍历
typedef struct BTNode{
char data;
struct BTNode *lchild,*rchild;
}BTNode;
//深度优先:depthFirst
//1.先序遍历递归实现
void preorder_useRecursion(BTNode *B){
if(B!=NULL){
visit(B);
preorder_useRecursion(B->lchild);
preorder_useRecursion(B->rchild);
}
}
//先序遍历非递归实现:先将根节点入栈,出栈访问;有右孩入栈,有左孩入栈,出栈访问,循环操作
//直到栈空
void preorder_notuseRecursion(BTNode *B){
BTNode* stack[100];
int top=0;
BTNode *pop;//表示出栈的节点
if(B!=NULL){
stack[++top]=B;
while(top!=0){
pop=stack[top--];
visit(pop);
if(pop->rchild!=NULL)
stack[++top]=pop->rchild;
if(pop->lchild!=NULL)
stack[++top]=pop->lchild;
}
}
}
//2.中序遍历递归实现
void inorder_useRecursion(BTNode* B){
if(B!=NULL){
inorder_useRecursion(B->lchild);
visit(B);
inorder_useRecursion(B->rchild);
}
}
//中序遍历的非递归实现
void inorder_notuseRecursion(BTNode* B){
BTNode* stack[100];
int top=0;
BTNode* curNode;//表示当前操作的节点
curNode=B;
while(curNode!=NULL || top!=0){
while(curNode!=NULL){
stack[++top]=curNode;
curNode=curNode->lchild;
}
if(top!=0){
curNode=stack[top--];
visit(curNode);
curNode=curNode->rchild;
}
}
}
//3.后序遍历的递归实现
void postorder_useRecursion(BTNode *B){
if(B!=NULL){
postorder_useRecursion(B->lchild);
postorder_useRecursion(B->rchild);
visit(B);
}
}
//后序遍历的非递归实现:对于任意节点p,先将其入栈,如果没有左右孩子,则可以出栈访问,
//或者其左右孩子都被访问过了,也可以进行访问;;除此之外,必须先将右孩子,左孩子一次入栈;从而保证
//左右孩子全被访问过,才访问根节点;;
void postorder_notuseRecursion(BTNode *B){
BTNode* stack[100];
int top=0;
bool is_access;//定义一个是否能够访问的bool变量
BTNode *curNode,*preNode;//表示当前操作的节点和上次访问的节点
curNode=preNode=NULL;//初始化
if(B!=NULL){
stack[++top]=B;
while(top!=0){
curNode=stack[top];
is_access=(curNode->rchild==NULL&&curNode->lchild==NULL)||
(preNode!=NULL&&(preNode==curNode->rchild||
preNode==curNode->lchild));
if(is_access){
visit(curNode);
--top;
preNode=curNode;
}else{
if(curNode->rchild!=NULL)
stack[++top]=curNode->rchild;
if(curNode->lchild!=NULL)
stack[++top]=curNode->lchild;
}
}
}
}
//层次遍历:借助循环队列实现;;breadthFirst广度优先
//算法分析:借助循环队列,将根节点入队列,出队列,访问,如果有左孩,入队,如果有右孩,入队;
//出队访问;如此循环,直到队列为空
void level(BTNode* B){
const int MAX=100;
BTNode* queue[MAX];
int rear,front;
rear=front=0;
BTNode* curNode;//表示当前操作的节点
if(B!=NULL){
curNode=B;
rear=(rear+1)%MAX;
queue[rear]=curNode;
while(rear!=front){
front=(front+1)%MAX;
//千万注意不能省略这一步,否则入队和出队,front和rear根本就没改变!!!
curNode=queue[front];
cout<<curNode->data<<" ";//表示访问节点操作
if(curNode->lchild!=NULL){
rear=(rear+1)%MAX;
queue[rear]=curNode->lchild;
}
if(curNode->rchild!=NULL){
rear=(rear+1)%MAX;
queue[rear]=curNode->rchild;
}
}
}
}
3.之寻找二叉树的下一个节点
//问题描述:输入一棵二叉树和一个其中任意的节点,返回此节点在中序遍历序列的下一个节点,该树的节点指针
//除了含有左右孩子指针外,还含有指向其父节点的指针
typedef struct BTNode{
int data;
struct BTNode *lchild,*rchild,*parent;
}BTNode;
//算法分析:对于任意一个节点p,如果存在右子树,则下一个节点就是右子树最左节点;如果无右子树,为其
//父节点的左节点,则下一个节点即为它的父节点;如果无右子树,为其父节点的左节点,则下一个节点
//为:朝着它的父节点进行遍历,如果存在某个节点为其父节点的左节点,则这个父节点即为所得,否则
//不存在下一个节点
BTNode* findNextNode_inorder(BTNode *p){//只需要传入任意一个节点即可
if(p==NULL)
return NULL;
BTNode* pNext=NULL;//定义p所找的下一个节点指针
if(p->rchild!=NULL){
BTNode* pRight=p->rchild;//它的右子树的节点指针
while(pRight->lchild!=NULL)
pRight=pRight->lchild;
pNext=pRight;
}
else if(p->parent!=NULL){
BTNode* curNode=p;
BTNode* curParentNode=curNode->parent;
while(curParentNode->lchild!=curNode && curParentNode!=NULL){
curNode=curParentNode;
curParentNode=curNode->parent;
}
pNext=curParentNode;
}
return pNext;
}