PAT 备考——树相关算法

版权声明:Dirichlet_zju https://blog.csdn.net/Dirichlet_zju/article/details/84715654

目录

一、树与二叉树

1.树的存储

2.树的基本操作

2.1.节点查找

2.2.节点插入与删除(后面二叉搜索树与平衡二叉树会分别讲) 

二、树的遍历

1.二叉树的遍历

1.1.前序遍历

1.2.中序遍历

1.3.后序遍历

1.4.层序遍历

1.5.已知遍历顺序建树

2.一般树的遍历 

三、二叉搜索树

1.定义

2.操作集

2.1.树的结构与建树

2.2.查找

2.3.插入

2.4.删除

四、平衡二叉树

1.定义

2.基本操作集

2.1.基本函数

 2.2.查找

2.3.插入

五、堆

六、哈夫曼树

七、并查集


一、树与二叉树

1.树的存储

一般来说,树采用链表来定义:

typedef struct Node *PtrToNode;
typedef struct PtrToNode Tree;
typedef struct PtrToNode Position;
typedef int ElementType;
struct Node{
    ElementType Data;
    Position Left, Right;
};

当然,在静态树中,也可以利用数组来表示:

const int MAXN = 1000;

Typedef int ElementType;
struct Node{
    ElementType Data;
    int left, right;//Tree中的下标
}Tree[MAXN];

2.树的基本操作

这里以链表指针表示的动态二叉树为例。

2.1.节点查找

Position Find(Tree T, ElementType X){
    if(T==NULL) return NULL;//没找到
    if(T->data==X) return T;

    return Find(T->left; X);
    return Find(T->right; X);
}

2.2.节点插入与删除(后面二叉搜索树与平衡二叉树会分别讲) 

二、树的遍历

1.二叉树的遍历

1.1.前序遍历

1)递归解法:

/*树的前序遍历
 *使用:递归
 *思路:先输出头结点,再遍历左子树和右子树
 */
void PreOrderTraversal(Tree T){
    if(T){
        printf("%d\n", T->data);
        PreOrderTraversal(T->left);
        PreOrderTraversal(T->right);
    }
}

2)非递归解法:注意是或的关系,且进入循环前不push

/*树的前序遍历
 *使用:栈stack,#include<stack>
 *思路:先输出头结点,再遍历左子树和右子树
 */
void PreOrderTraversal(Tree T){
    stack<Node> S;
    Tree Tmp=T;
    while(Tmp || !S.empty()){//树非空或堆栈非空
        while(Tmp){     //树非空,打印并寻找左子树
            printf("%d\n", Tmp->data);
            S.push(Tmp);
            Tmp=Tmp->left;
        }
        if(!S.empty()){ //堆栈非空,弹出并寻找右子树
            Tmp=S.top();
            S.pop();
            Tmp=Tmp->right;
        }
    }
}

1.2.中序遍历

1)递归解法

/*树的中序遍历
 *使用:递归
 *思路:先遍历左子树,再输出头结点和右子树
 */
void InOrderTraversal(Tree T){
    if(T){
        InOrderTraversal(T->left);
        printf("%d\n", T->data);
        InOrderTraversal(T->right);
    }
}

2)非递归解法

/*树的前序遍历
 *使用:栈stack,#include<stack>
 *思路:先遍历左子树,再输出头结点和右子树
 */
void InOrderTraversal(Tree T){
    stack<Node> S;
    Tree Tmp=T;
    while(Tmp || !S.empty()){//树非空或堆栈非空
        while(Tmp){     //树非空,打印并寻找左子树
            S.push(Tmp);
            Tmp=Tmp->left;
        }
        if(!S.empty()){ //堆栈非空,弹出并寻找右子树
            printf("%d\n", Tmp->data);
            Tmp=S.top();
            S.pop();
            Tmp=Tmp->right;
        }
    }
}

1.3.后序遍历

1)递归解法

/*树的后序遍历
 *使用:递归
 *思路:先遍历左子树和右子树,再输出头结点
 */
void PastOrderTraversal(Tree T){
    if(T){
        PastOrderTraversal(T->left);
        printf("%d\n", T->data);
        PastOrderTraversal(T->right);
    }
}

 2)非递归解法:需要在节点中加入bool类型的IsFirstTraversal标志位

void PastOrderTraversal(Tree T){//后续非递归
    Tree Tmp = T;
    stack<Tree> S;
    while(Tmp || !S.empty()){
        while(Tmp){
            S.push(Tmp);
            Tmp=Tmp->left;
        }
        if(!S.empty()){
            Tmp=S.top();
            S.pop();
            if(Tmp->IsFirstTraversal){
                Tmp->IsFirstTraversal=false;
                S.push(Tmp);
                Tmp=Tmp->right;
            }
            else{
                cout<<Tmp->data;
            }
        }
    }
}

1.4.层序遍历

层序遍历一般用非递归实现:

/*树的层序遍历
 *使用:队列
 *思路:头结点加入队列;之后头结点弹出队列并把其儿子入队;
 * 循环直至队列为空*/
void LevelOrderTraversal(Tree T){
    queue<Tree> Q;

    if(T==NULL) return;//边界条件

    Q.push(T);
    while(!Q.empty()){
        T=Q.front();
        Q.pop();
        printf("%d ", T->data);
        if(T->left) Q.push(T->left);
        if(T->right) Q.push(T->right);
    }
}

1.5.已知遍历顺序建树

例:已知后序遍历和中序遍历,建树并输出层序遍历:

#include <iostream>
#include <queue>
//注意点:1.边界条件;
//        2.后序遍历开始位置和结束位置的确定不能依靠从中序遍历计算到的inRoot
using namespace std;

const int maxn = 35;

typedef struct Node *Tree;
struct Node{
    int data;
    Tree left=NULL, right=NULL;
};

int n, post[maxn], in[maxn];
Tree T;

/*后序遍历最后一位是根节点rootKey,
 *在中序遍历找到rootKey所在位置root后,
 *中序left到root-1是左子树,root+1到right是右子树
 *后序left到root-1是左子树,root到right-1是右子树
 *递归,找到边界条件:
 */
Tree GenerateTree(int postL, int postR, int inL, int inR){
    if(postR<postL) return NULL;//边界条件
    int inRoot;//中序遍历根节点的位置
    //寻找根节点inRoot
    for(inRoot=0; inRoot<inR+1; inRoot++){
        if(in[inRoot]==post[postR]) break;
    }
    int numleft = inRoot-inL;//左子树长度,用于确定后序遍历左右子树范围
    Tree tr = (Tree)malloc(sizeof(struct Node));//新建树节点,也可以用Tree tr = new Tree;不加括号
    tr->data=post[postR];//根节点data赋值
    tr->left=GenerateTree(postL, postL+numleft-1, inL, inRoot-1);//递归返回左子树
    tr->right=GenerateTree(postL+numleft, postR-1, inRoot+1, inR);//递归返回右子树
    return tr;
}
int pointer = 0;//空格控制器
void LevelOrderTraversal(){
    queue<Tree> q;
    q.push(T);
    Tree tmp=T;
    while(!q.empty()){
        tmp=q.front();
        q.pop();
        cout<<tmp->data;
        pointer++;
        if(pointer!=n) cout<<" ";//空格控制
        if(tmp->left) q.push(tmp->left);
        if(tmp->right) q.push(tmp->right);
    }
}

int main()
{
    cin>>n;
    for(int i=0; i<n; i++) cin>>post[i];//输入后序遍历
    for(int i=0; i<n; i++) cin>>in[i];//输入中序遍历
    T = GenerateTree(0, n-1, 0, n-1);
    LevelOrderTraversal();
    return 0;
}

2.一般树的遍历 

本节针对节点个数不限且子节点没有先后次序的树。方便起见,对于一般树考试时希望采用静态写法

const int maxn = 10000;
typedef int dataType;
struct Node{
    dataType data;
    vector<int> child;
}Tree[maxn];

 由于子节点个数不定,因此一般树的遍历只考虑先序遍历和层序遍历。

2.1.先序遍历(递归、不考虑子节点排序)

void PreOrderTraversal(int u){
    printf("%d\n", T[u].data);
    for(int i=0; i<T[u].child.size(); i++){
        PreOrderTraversal(T[u].child[i]);
    }
}

2.2.层序遍历

void LevelOrderTraversal(int u){//非递归
    queue<int> Q;
    Q.push(u);
    while(!Q.empty()){
        u=Q.front();
        Q.pop();
        cout<<u<<endl;
        for(int i=0; i<Tree[u].child.size(); i++){
            Q.push(Tree[u].child[i]);
        }
    }
}

三、二叉搜索树

1.定义

二叉搜索树(Binary Search Tree, BST)又称作二叉查找树、排序二叉树等,是二叉树的一种特殊形式。BST的每一个节点的左子节点小于(或小于等于)该节点,右节点大于该节点,形成一种从左到右从小到大的存储形式。

性质:二叉搜索树的中序遍历有序

2.操作集

2.1.树的结构与建树

typedef struct Node *BST;
typedef BST Position;
struct Node{
    int data;
    Position left, right;
};
//建树
BST Create(int data[], int n){//数组以及数组中数据个数
    BST T = NULL;//不能新建,最好为空
    for(int i=0; i<n; i++){
        Insert(data[i], T);
    }
    return T;
}

2.2.查找

1)查找任意元素 

//查找
Position Search(int X, BST T){
    if(T==NULL) return NULL;
    if(X==T->data) return T;
    else if(X<T->data) return Search(X, T->left);
    else return Search(X, T->right);
}

2)查找最值

Position FindMin(BST T){//查找最小值
    Position tmp = T;
    if(tmp==NULL) return NULL;
    while(tmp->left != NULL){
        tmp=tmp->left;
    }
    return tmp;
}
Position FindMax(BST T){//查找最大值
    Position tmp = T;
    if(tmp==NULL) return NULL;
    while(tmp->right != NULL){
        tmp=tmp->right;
    }
    return tmp;
}

2.3.插入

//插入
void Insert(int X, BST T){
    if(T==NULL){
        BST tmp = new BST;
        tmp->data=X;
        tmp->left=tmp->right=NULL;
        T=tmp;
    }
    if(X==T->data) return;//节点已存在
    else if(X<T->data) Insert(X, T->left);
    else Insert(X, T->right);
}

2.4.删除

void Delete(int X, BST T){
    Position P = Search(X, T);//查找被删除元素位置
    if(P->left==NULL && P->right==NULL){//没有子节点
        free(P);//有待商榷?
    }
    //有两个子节点,找右子节点最小值替换
    else if(P->right!=NULL && P->left!=NULL){
        Position minRight = FindMin(P->right);
        P->data=minRight->data;//替换data
        Delete(minRight->data, P->right);
    }
    else if(P->left!=NULL){//左不为空,右为空
        Position tmp = P->left;
        P->data=tmp->data;
        P->left=tmp->left;
        P->right=tmp->right;
        free(tmp);
    }
    else if(P->right!=NULL){//右不为空
        Position tmp = P->right;
        P->data=tmp->data;
        P->left=tmp->left;
        P->right=tmp->right;
        free(tmp);
    }
}

四、平衡二叉树

1.定义

所谓二叉平衡树,是在保持二叉搜索树性质的,且左右子树高度差的绝对值不超过1的二叉树。

性质:查询时间复杂度O(logn)

结构:仅增加了height即该节点的高度,叶节点为1,其上每一层父节点的高度是其左右子树最大值+1

typedef struct Node* Position;
typedef Position Tree;
struct Node{
    int data, height;
    Position left, right;
};

2.基本操作集

2.1.基本函数

Position NewNode(int v){//新建data是v的点
    Position T = (Position)malloc(sizeof(struct Node));
    T->data=v;
    T->height=1;
    T->left=T->right=NULL;
    return T;
}
int GetHeight(Position P){
    if(P==NULL) return 0;//空节点高度为0
    else return P->height;
}

int GetBalanceFactor(Position P){
    return GetHeight(P->left) - GetHeight(P->right);
}

void UpdateHeight(Position P){
    P->height = max(GetHeight(P->left), GetHeight(P->right)) + 1;//左右儿子最大值+1
}

 2.2.查找

//同二叉搜索树
Tree Search(int X, Tree T){
    if(T==NULL) return NULL;
    if(X==T->data) return T;
    if(X<T->data) return T->left;
    else(X<T->data) return T->right;
}

2.3.插入

//插入
void Insert(int X, Tree T){
    if(T==NULL){
        Position P = NewNode(X);
        return;
    }
    if(X<T->data){
        Insert(X, T->left);
        UpdateHeight(T);//从低往上重置树各节点高度
        if(GetBalanceFactor(T)==2){//判断本节点平衡因子是否为2,左大右小为正
            if(GetBalanceFactor(T->left)==1){//LL型
                T = RightRotate(T);
            }
            else if(GetBalanceFactor(T->right)==-1){//LR型
                T->left = LeftRotate(T->left);
                T = RightRotate(T);
            }
        }
    }
    else{
        Insert(X, T->right);
        UpdateHeight(T);
        if(GetBalanceFactor(T)==-2){
            if(GetBalanceFactor(T->right==-1)){//RR型
                LeftRotate(T);
            }
            else if(GetBalanceFactor(T->right==1)){//RL型
                RightRoTate(T->right);
                LeftRotate(T);
            }
        }
    }
}

其中左旋右旋分别是:

//左旋
Position LeftRotate(Tree T){
    Tree tmp = T->right;//左旋就找右儿子
    T->right=tmp->left;//第一步
    tmp->left=T;//第二步
    UpdateHeight(T);//先更新考下的节点的高度
    UpdateHeight(tmp);//在更新考上节点的高度
    return tmp;//第三步
}
//右旋
Position RightRotate(Tree T){
    Tree tmp = T->left;
    T->left=tmp->right;
    tmp->right=T;
    UpdateHeight(T);
    UpdateHeight(tmp);
    return tmp;
}

五、堆

六、哈夫曼树

七、并查集

猜你喜欢

转载自blog.csdn.net/Dirichlet_zju/article/details/84715654
PAT