zoukankan      html  css  js  c++  java
  • 算法笔记-树-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

  • 相关阅读:
    深入JavaScript之获取cookie以及删除cookie
    js 首次进入弹窗
    jquery 点击加载更多
    express 设置允许跨域访问
    微信小程序之全局储存
    jquery 在页面上根据ID定位(jQuery锚点跳转及相关操作)
    我也想聊聊 OAuth 2.0 —— 基本概念
    一行代码,发送邮件
    【Git使用】强制推送代码到多个远程仓库
    一秒钟生成自己的iOS客户端
  • 原文地址:https://www.cnblogs.com/houzm/p/12323919.html
Copyright © 2011-2022 走看看