算法笔记(树专题)

树专题

1.DFS模板

//伪代码
void DFS(一个结点){
    访问该结点;
    for(遍历该结点的相邻未访问过的结点){
        选此结点;
        DFS(这个邻接结点);
        去掉刚刚选的结点;   //****(勿忘)
    }
}

2.BFS模板

//BFS使用队列
void BFS(int s){
    queue<int> q;
    q.push(s);  //起始点入队
    while(!q.empty()){
        取出队首元素top;
        访问队首元素top;
        将队首元素出队;
        将top的下一层结点中未曾入队的结点全部入队,并设置为已入队;
    }
}
for(结点未被访问){
    BSF(该结点);
}

3.二叉树的动态实现(指针)

struct node{
    int data;
    int layer;//层次遍历需要
    node* lchild;
    node* rchild;
};
//由于在二叉树建树前根节点不存在,因此其地址一般设为NULL ********
node* root = NULL;
//生成一个新结点
node* newNode(int v){
    node* Node = new node;
    Node->data = v;
    Node->lchild = Node->rchild = NULL;
    return Node;
}
//insert函数将在二叉树中插入一个数据域为x的新结点
void insert(node* &root, int x){ //(注意root要使用引用)****
    if(root == NULL){
        //root = newNode(x);
        root = new node;
        root->data = x;
        root->lchild = root->rchild = NULL;
        return;
    }
    if(由二叉树性质,x应该插在左子树){
        insert(root->lchild, x);
    } else{
        insert(root->rchild, x);
    }
}
//层序遍历
void LayerOrder(node* root){
    queue<node*> q;  //******(注意)
    q.push(root);
    while(!q.empty()){
        node* now = q.front();
        q.pop();
        printf("%d ", now->data);
        if(now->lchild != NULL) {
            now->lchild->layer = now->layer + 1;
            q.push(now->lchild);
        }
        if(now->rchild != NULL) {
            now->rchild->layer = now->layer + 1;
            q.push(now->rchild);
        }
    }
}

4.二叉树的静态实现

struct Node{
    int data;
    int lchild;
    int rchild;
} node[maxn];
//先序遍历
void preorder(int root){
    if(root == -1) return;  //到达空树,递归边界,一般空树设为-1
    printf("%d\n", node[root].data);
    preorder(node[root].lchild);
    preorder(node[root].rchild);
}
//中序遍历
void inorder(int root){
    if(root == -1) return;
    inorder(node[root].lchild);
    printf("%d\n", node[root].data);
    inorder(node[root].rchild);
}
//后序遍历
void postorder(int root){
    if(root == -1) return;
    postorder(node[root].lchild);
    postorder(node[root].rchild);
    printf("%d\n", node[root].data);
}
struct Node{
    int data;
    int lchild;
    int rchild;
} node[maxn];
//先序遍历
void preorder(int root){
    if(root == -1) return;  //到达空树,递归边界,一般空树设为-1
    printf("%d\n", node[root].data);
    preorder(node[root].lchild);
    preorder(node[root].rchild);
}
//中序遍历
void inorder(int root){
    if(root == -1) return;
    inorder(node[root].lchild);
    printf("%d\n", node[root].data);
    inorder(node[root].rchild);
}
//后序遍历
void postorder(int root){
    if(root == -1) return;
    postorder(node[root].lchild);
    postorder(node[root].rchild);
    printf("%d\n", node[root].data);
}

5.树的遍历、树的静态写法

? 树:即子结点个数不确定且子结点没有先后次序的树。推荐使用静态写法

struct Node{
    int data;  //数据域
    int layer; //用于树的层次遍历
    vector<int> child;   //*****指针域,存放所有子结点的下标
} node[maxn];  //结点数组,maxn为结点上限个数
//树的先根遍历 或 DFS
void PreOrder(int root){
    printf("%d ", node[root].data);   //访问当前结点
    for(int i=0; i<node[root].child.size(); i++){
        PreOrder(node[root].child[i]);   //递归访问结点root的所有子结点
    }
}
//树的层次遍历 或 BFS
void BFS(int root){
    queue<int> Q;
    Q.push(root); //将根结点入队
    node[root].layer = 0; //记根结点的层号为0
    while(!Q.empty()){
        int front = Q.front();
        Q.pop();  //取出队首元素
        printf("%d ", node[front].data); //操作,如打印当前结点的数据域
        for(int i=0; i<node[front].child.size(); i++){
            int child = node[front].child[i];  //当前结点的第i个子结点的编号
            node[child].layer = node[front].layer + 1; //子结点层号为当前结点层号+1
            Q.push(child);   //将当前结点的所有子结点入队
        }
    }
}

6.知道二叉树的先序和中序遍历,建立该二叉树

struct Node{
    int data;
    Node *lchild, rchild;
};
int pre[MAXN], in[MAXN], post[MAXN];  //先序、中序及后序
//当前二叉树的先序序列区间为[preL, preR],中序序列区间为[inL, inR]
//create函数返回构建出的二叉树的根节点地址
Node* create(int preL, int preR, int inL, int inR){
    if(preL > preR)  return NULL;
    Node* root = new Node;
    root->data = pre[preL];
    int k;
    for(k = inL; k<=inR; k++){
        if(in[k] == pre[preL]){  //在中序序列中找到in[k] == pre[L]的结点
            break;
        }
    }
    int numLeft = k - inL;  //左子树的结点个数
    //返回左子树的根结点地址,赋值给root的左指针
    root->lchild = create(preL+1, preL+numLeft, inL, k-1);   //****注意参数怎么表示
    //返回右子树的根结点地址,赋值给root的右指针
    root->rchild = create(preL+numLeft+1, preR, k+1, inR);
    return root;
}

7.二叉树查找(BST)

//****search函数查找二叉查找树中数据域为x的结点
void search(node* root, int x){
    if(root == NULL){ //空树,查找失败
        printf("search failed\n");
        return;
    }
    if(x == root->data){
        printf("%d\n", root->data);
    }else if(x < root->data){//如果x比根节点的数据域小,说明x在左子树
        search(root->lchild, x);  //往左子树搜索
    }else{
        search(root->rchild, x);
    }
}
//****insert函数将在二叉树中插入一个数据域为x的新结点(注意参数root要加引用 &)
void insert(node* &root, int x){
    if(root == NULL){ //空树,查找失败,也即插入位置
        root = newNode(x);   //新建结点,权值为x
        return;
    }
    if(x == root->data){  //查找成功,说明结点已存在,直接返回
        printf("%d\n", root->data);
    }else if(x < root->data){//如果x比根节点的数据域小,说明x需要插在左子树
        insert(root->lchild, x);  //往左子树搜索
    }else{
        insert(root->rchild, x);
    }
}
//****二叉查找树的建立
node* create(int data[], int n){
    node* root = NULL;  //新建根节点root
    for(int i=0; i<n; i++){
        insert(root, data[i]);  //将data[0]~data[n-1]插入二叉查找树
    }
    return root;  //返回根节点
}
//寻找以root为根结点的树中最大权值结点
node* findMax(node* root){
    while(root->rchild != NULL){
        root = root->rchild;   //不断往右,直到没有右孩子
    }
    return root;
}
node* findMin(node* root){
    while(root->data !=NULL){
        root = root->lchild;
    }
    return root;
}
//删除以root为根结点的树中权值为x的结点(****)
void deletNode(node* &root, int x){
    if(root == NULL) return;  //不存在权值为x的结点
    if(root->data == x){//找到欲删除的结点
        if(root->lchild == NULL && root->rchild == NULL){ //叶子结点直接删除
            root = NUUL; //把root地址设为NULL,父结点就引用不到它了
        }else if(root->lchild != NULL){ //左子树不空时
            node* pre = findMax(root->lchild);  //找root前驱
            root->data = pre->data;  //用前驱覆盖root
            deleteNode(root->lchild, pre->data);  //在左子树中删除结点pre
        }else{//右子树不为空时
            node* next = findMin(root->rchild);  //找root后继
            root->data = next->data;  //用后继覆盖root
            deleteNode(root->rchild, next->data);  //在右子树中删除结点next
        }else if(root->data > x){
            deleteNode(root->lchild, x); //在左子树中删除x
        }else{
            deleteNode(root->rchild, x);  //在右子树中删除x
        }
    }
}

8.平衡二叉树 AVL

struct node{
    int v, height; //v为结点权值,height为当前子树高度
    node *lchild;
    node *rchild;
} *root;
//生成一个新结点
node* newNode(int v){
    node* Node = new node;
    Node->v = v;
    Node->height = 1;  //AVL需要
    Node->lchild = Node->rchild = NULL;
    return Node;
}
//获取以root为根结点的子树的当前height
int getHeight(node* root){
    if(root == NULL) return 0;
    return root->height;
}
/更新结点root的height
void updateHeight(node* root){
    //max(左孩子结点的height, 右孩子结点的height)+1
    root->height = max(getHeight(root->lchild), getHeight(root->rchild)) + 1;
}
//计算结点的平衡因子
int getBalanceFactor(node* root){
    //左子树高度减右子树高度
    return getHeight(root->lchild) - getHeight(root->rchild);
}
//左旋
void L(node* &root){
    node* temp = root->rchild;
    root->rchild= temp->lchild;
    temp->lchild= root;
    updateHeight(root);
    updateHeight(temp);
    root = temp;
}
//右旋
void R(node* &root){
    node* temp = root->lchild;
    root->lchild = temp->rchild;
    temp->rchild = root;
    updateHeight(root);
    updateHeight(temp);
    root = temp;
}
void insert(node* &root, int v){
    if(root == NULL){  //到达空结点
        root = newNode(v);
        return;
    }
    if(v < root->v){  //v比根结点权值小
        insert(root->lchild, v);  //往左子树插入
        updateHeight(root);  //更新树高
        if(getBalanceFactor(root) == 2){
            if(getBalanceFactor(root->lchild) == 1){  //LL型
                R(root);
            } else if(getBalanceFactor(root->lchild) == -1){  //LR型
                L(root->lchild);
                R(root);
            }
        }
    } else{  //v比根结点权值大
        insert(root->rchild, v);  //往右子树插入
        updateHeight(root);  //更新树高
        if(getBalanceFactor(root) == -2){
            if(getBalanceFactor(root->rchild) == -1){  //LL型
                L(root);
            } else if(getBalanceFactor(root->rchild) == 1){  //RL型
                R(root->rchild);
                L(root);
            }
        }
    }
}
//AVL树的建立
node* Create(int data[], int n){
    node* root = NULL;
    for(int i=0; i<n; i++){
        insert(root, data[i]);
    }
    return root;
}

原文地址:https://www.cnblogs.com/bahcelor/p/8616204.html

时间: 2024-11-04 16:29:59

算法笔记(树专题)的相关文章

算法笔记--树的直径模板

思路: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c之间的距离就是树的直径. 模板: const int N=1e6+5; int head[N]; int dis[N]; bool vis[N]; int cnt=0,b,mxn=0; struct edge { int to,w,next; }edge[N]; void add_edge(int u,

算法笔记2-优先队列(堆)(上)

一.什么是优先队列? 看一情景:我们去KTV唱歌,点歌的时候,可以发现所点的歌就是一个队列. 这时候,一个MM突然不玩手机了想唱歌,于是她来点歌,并且想尽早轮到她. 于是她可以选择"插歌"这个功能插到前排队列里. 这种具备可以插入优先权元素的队列,就叫优先队列.但是,这个定义不是严谨的. 优先队列的基本模型是这样的-- 具备两个功能: insert插入: deleteMin 删除最小者. 它的工作就是-- 它很有用哦,具体可以用在操作系统,外部排序和贪婪算法中等. 二.怎么实现优先队列

算法笔记--lca倍增算法

算法笔记 模板: vector<int>g[N]; vector<int>edge[N]; int anc[20][N]; int deep[N]; int h[N]; void dfs(int o,int u,int w) { if(u!=o)deep[u]=deep[o]+1,h[u]=h[o]+w; for(int j=0;j<g[u].size();j++) { if(g[u][j]!=o) { anc[0][g[u][j]]=u; for(int i=1;i<

树套树专题——bzoj 3110: [Zjoi2013] K大数查询 &amp; 3236 [Ahoi2013] 作业 题解

[原题1] 3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 978  Solved: 476 Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M 接下来M行,每行形如1 a b c或2 a b c Outpu

算法笔记-DTW动态时间规整

算法笔记-DTW动态时间规整 简介 简单的例子 定义 讨论 约束条件 步模式 标准化 点与点的距离函数 具体应用场景 分类 点到点匹配 算法笔记-DTW动态时间规整 动态时间规整/规划(Dynamic Time Warping, DTW)是一个比较老的算法,大概在1970年左右被提出来,最早用于处理语音方面识别分类的问题. 1.简介 简单来说,给定两个离散的序列(实际上不一定要与时间有关),DTW能够衡量这两个序列的相似程度,或者说两个序列的距离.同时DTW能够对两个序列的延展或者压缩能够有一定

小算法笔记

素数: 除 1 外只能被 1 和自身整除的数. 方法一: #include <stdio.h> #define N 1000 int num = 0; int prime(int n) { int i; if(n % 2 == 0) return (n == 2); if(n % 3 == 0) return (n == 3); if(n % 5 == 0) return (n == 5); for(i = 7; i*i <= n; ++i) if(n % i == 0) return

算法笔记之堆排序

一.对堆排序的相关了解 1.堆排序的运行时间是 O(nlogn) : 2.定义: 堆heap是一棵具有以下属性的二叉树-- (1)它是一棵完全二叉树: (2)每个结点大于或等于它的任意一个孩子. 备注:完全二叉树的定义--除了最后一层没填满以及最后一层的叶子都是偏左放置的,其他层都是满的二叉树! 3.二叉堆有两种:最大堆和最小堆.在堆排序中我们使用的是最大堆,最小堆常常在构造优先队列时使用. 4.一条路径的深度指的是这条路径的边数,一个结点的深度是指从根结点到该结点的路径的长度. 二.对堆进行排

算法之字符串专题

一.单串匹配问题 poj2406(求字符串的周期)利用next[ ]性质,ans=next[len]%(len-next[len])==0?next[len]/(len-next[len]):1; poj2752(求所有相同的前后缀)利用next[ ]性质,pos=next[pos](不断向前找) next[0]=-1: next[i]=max(相同前后缀) poj3461(kmp模板题) 二.多串匹配问题 poj3080.poj3450(求多串最长公共字串且字典序最小)二分,枚举首串(按字典序

算法笔记_023:拓扑排序(Java)

目录 1 问题描述 2 解决方案 2.1 基于减治法实现 2.2 基于深度优先查找实现 1 问题描述 给定一个有向图,求取此图的拓扑排序序列. 那么,何为拓扑排序? 定义:将有向图中的顶点以线性方式进行排序.即对于任何连接自顶点u到顶点v的有向边uv,在最后的排序结果中,顶点u总是在顶点v的前面. 2 解决方案 2.1 基于减治法实现 实现原理:不断地做这样一件事,在余下的有向图中求取一个源(source)(PS:定义入度为0的顶点为有向图的源),它是一个没有输入边的顶点,然后把它和所有从它出发