算法笔记 [树章节笔记]

9.1 树与二叉树

1.二叉树的存储结构

struct node
{
    typename data;
    node* lchild;   // 指向左子树根节点的指针
    node* rchild;   // 指向右子树根节点的指针
}

二叉树在建树之前根节点不存在,地址一般为 NULLnode *root = NULL;

新建结点: (往二叉树中插入结点)

// 生成一个新结点,v为结点权值
node* newNode(int v)
{
    node* Node = new node;  // 申请一个 node 型变量的地址空间
    Node->data = v;         // 结点权值为v
    Node->lchild = Node->rchild = NULL;  // 初始状态下没有左右孩子
    return Node;            // 返回新建节点的地址
}

2.二叉树结点的查找与修改

void search(node* root, int x, int newdata)
{
    if(root == NULL)
        return;      // 空树
    if(root->data == x)  // 找到数据域中为x的结点,修改
        root->data = newdata;
    search(root->lchild, x, newdata);  // 往左子树中搜索x
    search(root->rchild, x, newdata);  // 往右子树中搜索x
}

3.二叉树结点插入

// 在二叉树中插入一个数据域为x的新结点
void insert(node* &root, int x)
{
    if(root == NULL) // 空树,查找失败,即插入的位置 (递归边界)
    {
        root = newNode(x);
        return;
    }
    if(由二叉树的性质,x应该插在左子树)
        insert(root->lchild, x); // 往左子树搜索
    else
        insert(root->rchild, x);  // 往右子树搜索
}

4.二叉树的创建

// 二叉树的建立
void Create(int data[], int n)
{
    node* root = NULL;  // 新建空根节点root
    for(int i=0; i<n; i++)
    {
        insert(root, data[i]); // 将data[0] - data[i-1] 插入到树中
    }
    return root;
}

9.2 二叉树的遍历

先序遍历

void preorder(node* root)
{
    if(root == NULL)  // 到达空树,递归边界
        return;
    // 访问根节点
    printf("%d\n", root->data);
    // 访问左子树
    preorder(root->lchild);
    // 访问右子树
    preorder(root->rchild);
}

中序遍历

void inorder(node* root)
{
    if(root == NULL)
        return;
    inorder(root->lchild);
    printf("%d\n", root->data);
    inorder(root->rchild);
}

后序遍历

void postorder(node* root)
{
    if(root == NULL)
        return;
    postorder(root->lchild);
    postorder(root->rchild);
    printf("%d\n", root->data);
}

层序遍历

void LayerOrder(node *root)
{
    queue<node*> q;
    q.push(root);  // 根结点地址入队
    while(!q.empty())
    {
        node* now = q.front();
        q.pop();
        printf("%d\n", now->data);  // 访问队首元素
        if(now->lchild != NULL) q.push(now->lchild); // 左子树非空
        if(now->rchild != NULL) q.push(now->rchild);
    }
}

在结构体变量中增加一个记录层次的变量:

struct node
{
    int data;  // 数据域
    int layer; // 层次
    node* lchild; // 左指针域
    node* rchild; // 右指针域
}
void LayerOrder(node* root)
{
    queue<node*> q;
    root->layer = 1;
    q.push(root);
    while(!q.empty())
    {
        node* now = q.front();
        q.pop();
        printf("%d\n", 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);
        }

    }
}

给定一棵二叉树的先序遍历序列和中序遍历序列,重建这棵二叉树

二叉树的静态实现

// 定义二叉树
struct node
{
    typename data; // 数据域
    int lchild;  // 指向左子树根节点的指针
    int rchild;  // 指向右子树根节点的指针
}Node[maxn];     // 节点数组,maxn为结点上限个数

// 结点的动态生成
int index = 0;
int newNode(int v)
{
    Node[index].data = v;
    Node[index].lchild = -1;
    Node[index].rchild = -1;
    return index++;
}

// 查找,root为根结点在数组中的下标
void search(int root, int x, int newdata)
{
    if(root == -1)
        return; // 空树
    if(Node[root].data == x)
        Node[root].data = newdata;
    search(Node[root].lchild, x, newdata);
    search(Node[root].rchild, x, newdata);
}

// 插入,root为根结点在数组中的下标
void insert(int &root, int x)
{
    if(root == -1) // 空树,查找失败,插入位置(递归边界)
    {
        root = newNode(x);
        return;
    }
    if(由二叉树的性质x应该插在左子树)
        insert(Node[root].lchild, x);
    else
        insert(Node[root].rchild, x);
}

// 二叉树的建立
int Create(int data[], int n)
{
    int root = -1; // 新建根结点
    for(int i=0; i<n; i++)
    {
        insert(root, data[i]); // 将每一个节点插入二叉树
    }
    return root;  // 返回根结点下标
}

遍历

// 先序遍历
void preorder(int root)
{
    if(root == -1)
        return;  // 到达空树,递归边界
    // 访问根节点 root
    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);
}

// 层序遍历
void LayerOrder(int root)
{
    queue<int> q;
    q.push(root);
    while(!q.empty())
    {
        int now = q.front();
        q.pop();
        printf("%d\n", Node[now].data);
        if(Node[now].lchild != -1) q.push(Node[now].lchild);
        if(Node[now].rchild != -1) q.push(Node[now].rchild);
    }
}

9.3 树的遍历

struct node
{
    typename data; // 数据域
    int child[maxn]; // 指针域,存放所有子结点的下标
}Node[maxn];

使用 vector 重新定义为

struct node
{
    typename data;
    vector child;
}Node[maxn];

新建一个节点

int index = 0;
int newNode(int v)
{
    Node[index].data = v;
    Node[index].child.clear();
    return index++;  // 返回节点下标,令index自增
}

先根遍历

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的所有子结点
    }
}

层序遍历

void LayerOrder(int root)
{
    queue<int> q;
    q.push(root);
    while(!q.empty())
    {
        int now = q.front();
        q.pop();
        printf("%d\n", Node[now].data);
        for(int i=0; i<Node[now].child.size(); i++)
        {
            q.push(Node[now].child[i]);
        }
    }
}

如果题目中涉及到层数,在结构体中增加一个变量存储层数信息。

struct node
{
    int layer; // 记录层号
    int data;
    vector<int> child;
}
// 层序遍历可以更新为
void LayerOrder(int root)
{
    queue<int> q;
    q.push(root);
    Node[root].layer = 0; // 根节点的层号为0
    while(!q.empty())
    {
        int now = q.front();
        q.pop();
        printf("%d\n", Node[now].data);
        for(int i=0; i<Node[now].child.size(); i++)
        {
            int child = Node[now].child[i];
            Node[child].layer = Node[now].layer + 1;
            q.push(child);
        }
    }
}

9.4 二叉查找树

// 查找二叉查找树中数据域为x的结点
void search(node* root, int x)
{
    if(root == NULL)  // 空树查找失败
        return;
    if(root->data == x)
        printf("%d\n", root->data);
    else if(x < root->data)
        search(root->lchild);   // 左子树搜索x
    else
        search(root->rchild);   // 右子树搜索x
}
// 插入一个数据域为x的新结点
void insert(node* &root, int x)
{
    if(root == NULL) // 空树,查找失败即插入的位置
    {
        roor = newNode(x);
        return;
    }
    if(x == root->data) // 已经存在,无需插入
        return;
    else if(x < root->data)
        insert(root->lchild, x);
    else
        insert(root->rchild, x);

二叉搜索树建立

node *Create(int data[], int n)
{
    node *root = NULL;
    for(int i=0; i<n; i++)
    {
        insert(root, data[i]);
    }
    return root; // 返回根结点
}

9.6 并查集

使用一个数组实现

int father[N];

初始化,每个元素都是一个独立的集合

for(int i=1; i<=N; i++)
{
    father[i] = i;
}

查找

// 递推实现
int findFather(int x)
{
    while(x != father[x]) // 不是根结点继续循环
        x = father[x];    // 或者自己的父亲节点
    return x;
}

// 递归实现
int findFather(int x)
{
    if(x == father[x]) return x; // 如果找到根结点,返回根结点编号x
    else return findFather(father[x]); // 否则递归判断x的父亲节点是否是根结点
}

合并

只对两个不同的集合进行合并,保证结果是一棵树。

void Union(int a, int b)
{
    int faA = findFather(a);
    int fbB = findFather(b);
    if(FaA != fbB)
    {
        father[faA] = fbB;
    }
}

路径压缩

// 递推
int findFather(int x)
{
    int a = x; // 保存当前节点
    while(x != father[x])
        x = father[x];

    // x存放的是根结点
    while(a != father[a])
    {
        int z = a;
        a = father[a]; // 指向上一个
        father[z] = x; // 最下方节点指向根父节点
    }
    return x; // 返回根结点
}

// 递归
int findFather(int v)
{
    if(v == father[v]) return v; // 找到根结点
    else
    {
        int F = findFather(father[v]); // 递归查找父节点的根结点
        father[v] = F; // 根结点F赋值给father[v]
        return F;  // 返回根结点 F
    }
}

// 更简单写法
int findFather(int x)
{
    if(x != father[x])  father[x] = findFather(father[x]);
    return father[x];
}

9.7 堆

从最后一个元素,从下往上,从右往左进行调整。没有下一级可以直接被跳过。

// 堆的表示
const int maxn = 100;
// heap为堆,n为元素个数
int heap[maxn], n=10;
// 对heap数组在[low, high]范围向下调整
// 其中low为欲调整结点的数组下标,high一般为堆的最后一个元素的数组下标
void downAdjust(int low, int high)
{
    int i=low, j=2*i; // i为欲调整结点,j为其左孩子
    while(j<high) // 存在孩子节点
    {
        // 右孩子存在且右孩子的值大于左孩子
        if(j+1<=high && heap[j+1]>heap[j])
            j = j+1; // j存储右孩子下标(更大的一个)
        // 孩子中最大的权值比欲调整结点i大
        if(heap[j]>heap[i])
        {
            swap(heap[j], heap[i]);  // 交换最大权值的孩子与欲调整结点i
            i = j;   // i为欲调整的节点
            j = i*2;  // j还是i的左孩子
        }
        else
            break; // 孩子的权值均比当前域调整的权值小,调整结束
    }
}
// 建堆
void createHeap()
{
    for(int i=n/2; i>=1; i--)
    {
        downAdjust(i, n);
    }
}
// 删除堆顶元素
void deleteTop()
{
    heap[1] = heap[n--]  // 用最后一个元素覆盖堆顶元素,元素个数减1
    downAdjust(1, n-1);  // 向下调整堆顶元素
}
// 对heap数组在[low, high]范围进行向上调整
// 其中low一般设置为1,high表示欲调整结点的数组下标
void upAdjust(int low, int high)
{
    int i=high, j= i/2; // i为欲调整节点,j为其父亲节点
    while(j>=low)
    {
        // 父亲权值小于与调整结点i的权值
        if(heap[j]<heap[i])
        {
            swap(heap[j], heap[i]);
            i = j;  // i为欲调整结点,j为i父亲
            j = i/2;
        }
        else
            break;
    }
}
// 添加元素
void insert(int x)
{
    heap[++n] = x;  // 元素个数加1,
    upAdjust(1, n); // 向上调整新加入的结点n
}
// 堆排序
void heapSort()
{
    createHeap();
    for(int i=n; i>1; i--)  // 倒着枚举,直到堆中只有一个元素
    {
        swap(heap[1], heap[i]); // 交换 heap[i] 与堆顶
        downAdjust(1, i-1);  // 调整堆顶
    }
}
发布了166 篇原创文章 · 获赞 27 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/HdUIprince/article/details/105464771