算法笔记-树-DFS-BFS相关问题解决方案整理

如何建树?

二叉树-建树-方式一

dfs使用root左右指针建立树节点关系,返回根节点root

二叉树-建树-方式二

dfs使用二维数组,int nds[n][2],如:nds[i][0]表示i节点的左子节点,nds[i][1]表示i节点的右子节点

树-建树

dfs使用邻接表保存树节点关系,vector nds[n],如:nds[i][j]表示节点i的子节点j

二叉查找树-建树

二叉查找树-建树-方式一:前序序列

如前序序列:8 6 5 7 10 8 11
8是根节点
左子树:从6开始往后找小于8的都为8的左子树节点
右子树:从最后一位11开始往前找大于8的都为8的右子树节点
继续递归过程,直到完成建树

node * create(int preL,int preR){
    if(preL>preR)return NULL;
    node * root = new node(pre[preL]);
    int k=preL+1;
    while(k<preR&&abs(pre[k])<=abs(pre[preL]))k++;//找第一个大于根节点的值 
    root->f=create(preL+1,k-1);
    root->r=create(k,preR);
    return root;
}

二叉查找树-建树-方式二:依次插入

非递归

node * root;
void insert(int n) {
    if(root==NULL) {
        root=new node(n,1);
        return;
    }
    node * p=root;
    while(p!=NULL) {
        if(p->data<n) {
            if(p->right==NULL){
                p->right=new node(n,p->h+1);
                return;
            }
            else p=p->right;
        } else {
            if(p->left==NULL){
                p->left=new node(n,p->h+1);
                return;
            }
            else p=p->left;
        }
    }
}

递归

void insert(node* &root, int data, int dep) {
    if(root == NULL) {      //到达空结点时,即为需要插入的位置
        root = new node(data,dep);
        root->data = data;
        root->left = root->right = NULL;    //此句不能漏
        return;
    }
    if(data <= root->data) insert(root->left, data, root->h+1);     //插在左子树
    else insert(root->right, data, root->h+1);     //插在右子树
}

完全二叉查找树建树

完全二叉查找树,任意节点序列建树
注:二叉查找树(不一定要完全二叉树)任意节点序列,升序排序,即为二叉查找树的中序序列

  1. 方式一:模拟递归打印中序序列的过程,将中序序列依次插入到数组建树
  2. 方式二:找root在中序序列中的位置k并将root保存到数组,递归处理startk-1左子树和k+1end右子树,完成建树
void inOrder(int root) { //root保存在1位置,index初始化为0
	if(root>n) return;
	inOrder(root*2);
	CBT[root]=number[index++];
	inOrder(root*2+1);
}

方式二

void getLevel(int start, int end, int index) {
	if(start>end)return;
	int n=end-start+1;
	int l=log(n+1)/log(2);//最后一层的层数
	int leave=n-(pow(2,l)-1); //最后一层叶子结点数
	//pow(2, l - 1) - 1是除了root结点所在层和最后?层外,左?树的结点个数,pow(2, l - 1) 是l+1
	//层最多拥有的属于根结点左?树的结点个数,min(pow(2, l - 1), leave)是最后?个结点真正拥有的
	//属于根结点左?树上的结点个数
	int root = start+(pow(2,l-1)-1)+min((int)pow(2,l-1),leave);
	level[index]=in[root];
	getLevel(start,root-1,2*index+1);
	getLevel(root+1,end,2*index+2);

}

序列转换

前序后序转中序

后序倒数第一个节点为根root,root在前序中为第一个节点
后序倒数第二个节点为root的右子树的根节点rightTreeRoot,rightTreeRoot在前序中将序列分为左边部分(root和root的左子树),右边部分(root的右子树包含rightTreeRoot)
若rightTreeRoot前只有一个节点,那么说明该rightTreeRoot的父节点root只有一个子节点,即rightTreeRoot既可以为root的左子节点也可以为root的右子节点

node * create(int preL,int preR,int postL,int postR) {
    if(preL>preR)return NULL;
    node * root = new node;
    root->data=post[postR];
    root->left=NULL;
    root->right=NULL;
    if(preL==preR)return root;
    int k=preL+1;
    while(k<=preR&&pre[k]!=post[postR-1])k++;
    if(k-preL>1) {
        root->left=create(preL+1,k-1,postL,postL+(k-preL-1)-1);
        root->right=create(k,preR,postL+(k-preL-1),postR-1);
    } else {
        unique = false;
        root->right=create(k,preR,postL+(k-preL-1),postR-1);
    }
    return root;
}

前序中序转后序

思路

前序第一个节点为根root,root在中序中位置为k,将序列分为左边部分(root的左子树),右边部分(root的右子树)
inLk-1为下次左递归的后序序列,其对应的后序序列为preL+1preL+(k-inL);
k+1inR为下次递归的后序序列,其对应的后序序列为postL+(k-inL)+1preR;

void postFirst(int inL,int inR, int preL,int preR) {
    if(inL>inR||flag) return;
    int k=inL;
    while(k<inR&&in[k]!=pre[preL])k++;
    postFirst(inL, k-1, preL+1, preL+(k-inL));
    postFirst(k+1, inR, preL+(k-inL)+1, preR);
    if(flag==false) {
        printf("%d", pre[preL]);
        flag=true; 
    }
}

题目

PAT A1138 Postorder Traversal

后序中序转前序

思路

后序第一个节点为根root,root在中序中位置为k,将序列分为左边部分(root的左子树),右边部分(root的右子树)
inLk-1为下次左递归的后序序列,其对应的后序序列为postLpostL+(k-inL)-1;
k+1inR为下次递归的后序序列,其对应的后序序列为postL+(k-inL)postR-1;

node * create(int postL,int postR,int inL,int inR) {
    if(postL>postR)return NULL;
    node * root=new node;
    root->data=post[postR];
    // 找到当前根节点在中序遍历中的位置
    int i;
    for(i=inL; i<=inR; i++) {
        if(post[postR]==in[i])
            break;
    }
    int numLeft=i-inL;
    root->left=create(postL,postL+numLeft-1,inL,inL+numLeft-1);//inL+numLeft=i
    root->right=create(postL+numLeft,postR-1,inL+numLeft+1,inR);//inL+numLeft=i
    return root;
}

题目

PAT A1127 ZigZagging on a Tree (30分)

二叉查找树前序转后序

二叉查找树前序序列

  • pre[0]为root
  • 从pre[1]开始第一个大于root的值(位置为k,pre[1]pre[k-1]即为root左子树)
  • 从pre[N-1]开始第一个小于root的值(位置为k,pre[k+1]pre[N-1]即为root右子树)
void getPost(int root, int tail) {
    if(root>tail)return;
    int i=root+1;
    int j=tail;
    if(!isMirror) {
        while(i<=tail&&pre[i]<pre[root])i++;
        while(j>root&&pre[j]>=pre[root])j--;
    } else {
        while(i<=tail&&pre[i]>=pre[root])i++;
        while(j>root&&pre[j]<pre[root])j--;
    }
    if(i-j!=1)return;
    getPost(root+1,j);//左子树
    getPost(i,tail); //右子树
    post.push_back(pre[root]);
}

如何进行层级遍历序列?

方式一

建树,借助队列层级遍历

方式二

建树时,记录index到结点(如:节点i的左子节点index=2i+1,节点i的右子节点index=2i+2),之后节点index升序排序,依次打印即为层序序列

如何记录层级?

DFS中如何记录层级?如何记录每层叶子节点数?

记录层级-方式一-辅助数组

dfs函数参数中记录当前处理节点的层级,使用int max_h记录最大层数方便后续层级遍历,可使用int leaf[n]记录对应层级叶子节点数

void dfs(int index,int h) {
	max_h=max(h,max_h);//记录最大层数
	if(nds[index].size()==0) { //叶子节点
		leaf[h]++;
		return;
	}
	for(int i=0; i<nds[index].size(); i++) {
		dfs(nds[index][i],h+1);
	}
}

记录层级-方式二-结构体中定义层级变量

struct node {
	int data;
	node * left;
	node * right;
	int depth;
};
void dfs(node * root) {
	queue<node*> q;
	root->depth=0;
	q.push(root);
	while(!q.empty()) {
		node * now = q.front();
		q.pop();
//		printf("%d-%d ",now->data,now->depth);
		result[now->depth].push_back(now);
		if(now->left!=NULL) {
			now->left->depth=now->depth+1;
			q.push(now->left);
		}
		if(now->right!=NULL) {
			now->right->depth=now->depth+1;
			q.push(now->right);
		}
	}
}

BFS中如何记录层级?如何记录每层叶子节点数?

使用int h[n]记录节点层级,如:h[i]即为节点i的层级,使用int max_h记录最大层数方便后续层级遍历,可使用int leaf[n]记录对应层级叶子节点数

void bfs(){
	queue<int> q;
	q.push(1);
	while(!q.empty()){
		int now = q.front();
		q.pop();
		max_h=max(max_h,h[now]);
		if(nds[now].size()==0){
			leaf[h[now]]++;
		} else{
			for(int i=0;i<nds[now].size();i++){
				h[nds[now][i]]=h[now]+1;
				q.push(nds[now][i]);
			}
		}
	} 
}

如何判断树是否为完全二叉树

方式一

数组存储树(节点index=i,左右子节点index分别为2i+1,2i+2),判断数组中间是否有空闲位置

方式二

数组存储树(节点index=i,左右子节点index分别为2i+1,2i+2),最大index==结点数n-1即为完全二叉树

方式二

BFS借助队列广度优先遍历,若遇到NULL则为非完全二叉树,因为完全二叉树在借助队列BFS遍历时,中间不可能出现NULL

posted @ 2020-02-17 21:57  JamieHou  阅读(328)  评论(0编辑  收藏  举报