第4章树与二叉树

博客围绕二叉树展开,介绍了其与链表的区别、节点属性,阐述广度优先和深度优先遍历方式。还讲解了二叉树的类型、遍历方法、广义表表示法及哈夫曼编码。最后给出多个二叉树相关的习题,如前序遍历、层序遍历等,并提及货仓选址问题。

属于二叉树

跟链表的不同:每个节点可以指向多个节点

所以直接改变next[n]就好

链表是树形结构特例

树的深度(高度): 层数 =5 [1 4 5 7 8]

节点深度:从根节点开始,4的深度:[1 4] =1

节点高度:从叶子节点开始,4的高度:[8 7 5 4]=3

8号节点高度为0

1号节点深度为0

节点的度:子节点数量:1号节点度为3

叶子节点度为0

度分为入度和出度:

​ 入度:几个节点指向我,1号节点入度为0 :父节点

​ 出度:我指向了几个节点 ,8号节点出度为0 :子节点

节点数量=边数+1

n个节点=n-1条边

树的节点代表几个,边代表关系

根节点代表全集

广度优先遍历和深度优先遍历

广度遍历是层序遍历,一层一层

深度遍历是,遍历子子子,直到没有子,回溯,再找有没有没有遍历的子节点,回溯-遍历

看两个节点是不是父子关系:看子节点在不在父节点范围内

广度优先:

1 入 2 3 入 1 出

4 入 2 出

5 6 7入 3 出

8 9 入

深度优先:

1 2 4 入 没了 弹出栈顶 4 2

1 有 3 5 入 弹5

6 入 8 入 弹8

9入 9 弹

6弹

7 入 7 弹

3 弹

1 弹

:1 2 4 4 2 3 5 5 6 8 8 9 9 6 7 7 3 1 -》范围内表示是子树

二叉树

二叉树每个节点度最多为2

二叉树度0为的节点比度为2的节点多一个

点数=边数+1

n0+n1+n2=n1+2n2+1

n0=n2+1

度为0的节点比度为1的节点多一个

完全二叉树:除了最后一层以外,上面的都是慢的

​ 左孩子:2*i

​ 右孩子:2*i+1

满二叉树:没有度为0的树

完美二叉树:每一层都是满的

练习递归技巧的最佳选择:

数学归纳法-》结构归纳法

左孩子有兄弟表示法节省空间《–多叉树都可以转为二叉树

左边表示孩子,右边表示兄弟

#include <cstdlib>
#include <ctime>
#include <cstdio>

//
// Created by cry on 2024/3/15.
//
typedef struct Node{
    int key;
    struct Node *lchild,*rchild;
}Node;
//初始化二叉树节点
Node *acm_1_5_shu_guangdubianli_getNewNode(int key){
    Node *p=(Node *)malloc(sizeof(Node));
    p->key=key;
    p->lchild=p->rchild=NULL;
    return p;
}
//了无牵挂式析构法
void acm_1_5_shu_guangdubianli_clear(Node *root){
    if (root ==NULL) return;
    acm_1_5_shu_guangdubianli_clear(root->lchild);
    acm_1_5_shu_guangdubianli_clear(root->rchild);
    free(root);
    return ;
}
Node *acm_1_5_shu_guangdubianli_insert(Node *root,int key){
    if(root ==NULL) return acm_1_5_shu_guangdubianli_getNewNode(key);
    if(rand() %2) root->lchild= acm_1_5_shu_guangdubianli_insert(root->lchild,key);
    else root->rchild= acm_1_5_shu_guangdubianli_insert(root->rchild,key);
    return root;
}

#define MAX_NODE 10
int head,tail;
Node *queue[MAX_NODE+5];
//广度优先
void dfs(Node *root){
    head=tail=0;
    queue[tail++]=root;
    while(head<tail){
        Node *node=queue[head];
        printf("\n node:%d\n",node->key);

        if(node->lchild) {
            queue[tail++] = node->lchild;
            printf("\t%d->%d(left)\n",node->key,node->lchild->key);
        }
        if(node->rchild) { queue[tail++] = node->rchild;
            printf("\t%d->%d(right)\n",node->key,node->rchild->key);}
        head++;
    }
    return ;
}
//深度优先遍历
int tot=0;
void bfs(Node *root){
    if(root == NULL) return;
    tot+=1;
    int start,end;
    start=tot;
    if(root->lchild) bfs(root->lchild);
    if(root->rchild) bfs(root->rchild);

    tot+=1;
    end=tot;
    printf("%d:[%d,%d]\n",root->key,start,end);

}
void acm_1_5_shu_guangdubianli_test(){
    srand(time(0));
    Node *root=NULL;
    for(int i=0;i<MAX_NODE;i++){
        root= acm_1_5_shu_guangdubianli_insert(root,rand()%100);
    }
    bfs(root);
    dfs(root);
//    return 0;
}

左右表示左子树

前序遍历:根左右 :1 2 4 5 3 6

中序遍历:左根右 :4 2 5 1 3 6

后序遍历:左右根:4 5 2 6 3 1

用于序列化

线索化:

右边空指针:前驱

左边空指针:后继

二叉树的遍历展现的像链表

//
// Created by cry on 2024/3/15.
//
//acm_1_5_shu_bainliheXiansuohua_
#include <cstdlib>
#include <ctime>
#include <cstdio>
using namespace std;
typedef struct Node{
    int key,ltag,rtag; //ltag表示左边边标记,ltag=1表示左边是线索  0:正常边
    struct Node *lchild,*rchild;
}Node;
Node *acm_1_5_shu_bainliheXiansuohua_getNewNode(int key){
    Node *p=(Node *) malloc(sizeof(Node));
    p->key=key;
    p->lchild=p->rchild=NULL;
    p->ltag=p->rtag=0;
    return p;
}
Node *acm_1_5_shu_bainliheXiansuohua_insert(Node *root,int key){
    if(root ==NULL) return acm_1_5_shu_bainliheXiansuohua_getNewNode(key);
    if(rand() %2) root->lchild= acm_1_5_shu_bainliheXiansuohua_insert(root->lchild,key);
    else root->rchild= acm_1_5_shu_bainliheXiansuohua_insert(root->rchild,key);
    return root;
}
void acm_1_5_shu_bainliheXiansuohua_clear(Node *root){
    if(root==NULL) return;
    if(root->ltag==0)acm_1_5_shu_bainliheXiansuohua_clear(root->lchild);
    if(root->rtag==0)acm_1_5_shu_bainliheXiansuohua_clear(root->rchild);
    acm_1_5_shu_bainliheXiansuohua_clear(root);
    return ;
}
//前序遍历
void acm_1_5_shu_bainliheXiansuohua_pre_order(Node *root){

    if(root==NULL) return;
    printf("%d ",root->key);
    if(root->ltag==0)acm_1_5_shu_bainliheXiansuohua_pre_order(root->lchild);
    if(root->rtag==0)acm_1_5_shu_bainliheXiansuohua_pre_order(root->rchild);
    return;
}
//中序遍历
void acm_1_5_shu_bainliheXiansuohua_in_order(Node *root){
    if(root==NULL) return;
    if(root->ltag==0)acm_1_5_shu_bainliheXiansuohua_in_order(root->lchild);
    printf("%d ",root->key);
    if(root->rtag==0)acm_1_5_shu_bainliheXiansuohua_in_order(root->rchild);
}
Node *pre_node=NULL,*inorder_root=NULL;
void __acm_1_5_shu_bainliheXiansuohuabuild_in_order_thread(Node *root){
    if(root==NULL) return;
    if(root->ltag==0)__acm_1_5_shu_bainliheXiansuohuabuild_in_order_thread(root->lchild);
    if(inorder_root==NULL){inorder_root=root;}
    if(root->lchild==NULL) {
        root->lchild = pre_node;
        root->ltag = 1;
    }
    if(pre_node && pre_node->rchild==NULL){
        pre_node->rchild=root;
        pre_node->rtag=1;
    }
    pre_node=root;

    if(root->rtag==0)__acm_1_5_shu_bainliheXiansuohuabuild_in_order_thread(root->rchild);
}
void acm_1_5_shu_bainliheXiansuohuabuild_in_order_thread(Node *root){
    __acm_1_5_shu_bainliheXiansuohuabuild_in_order_thread(root);
    pre_node->rchild=NULL;
    pre_node->rtag=1;
    return ;
}
//后序遍历
void acm_1_5_shu_bainliheXiansuohua_post_order(Node *root){
    if(root==NULL) return;
    if(root->ltag==0)acm_1_5_shu_bainliheXiansuohua_post_order(root->lchild);
    if(root->rtag==0)acm_1_5_shu_bainliheXiansuohua_post_order(root->rchild);
    printf("%d ",root->key);
}
Node *acm_1_5_shu_bainliheXiansuohua_getNext(Node *root){
    if(root->rtag==1) return root->rchild;
    root=root->rchild;
    while(root->ltag==0 && root->lchild){
        root=root->lchild;
    }
    return root; //右子树最左边-后继
}
void acm_1_5_shu_bainliheXiansuohua_test(){
    srand(time(0));
    Node *root=NULL;
#define MAX_NODE 10
    for (int i = 0; i < MAX_NODE; i++) {
        root=acm_1_5_shu_bainliheXiansuohua_insert(root,rand()%100);
    }
    pre_node=NULL,inorder_root=NULL;
    acm_1_5_shu_bainliheXiansuohuabuild_in_order_thread(root);
    printf("\n");
    acm_1_5_shu_bainliheXiansuohua_pre_order(root);
    printf("\n");
    acm_1_5_shu_bainliheXiansuohua_in_order(root);
    printf("\n");
    acm_1_5_shu_bainliheXiansuohua_post_order(root);
    printf("\n");

    Node *node=inorder_root;
    while(node) {
        printf("%d ",node->key);
        node = acm_1_5_shu_bainliheXiansuohua_getNext(node);
    }

    acm_1_5_shu_bainliheXiansuohua_clear(root);
    return;
}

广义表表示法

将二叉树表示为字符串

空树:()

一个节点:A / A()

两个节点:A(B,) / A(B)

只有右子树:A(,B)

只有左子树,可没有逗号:A(B)

三个结点:A(B,C)

A(B(,D),C(E))

//
// Created by cry on 2024/3/15.
//
//acm_1_5_shu_serialize_deserialize_
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#define KEY(n) (n?n->key:-1) //-1表示没有值
//二叉树转广义表
typedef struct Node{
    int key;
    struct Node *lchild,*rchild;
}Node;
Node *acm_1_5_shu_serialize_deserialize_getNewNode(int key){
    Node *p=(Node *) malloc(sizeof(Node));
    p->key=key;
    p->lchild=p->rchild=NULL;
    return p;
}
void acm_1_5_shu_serialize_deserialize_clear(Node *root){
    if(root==NULL)return ;
    acm_1_5_shu_serialize_deserialize_clear(root->lchild);
    acm_1_5_shu_serialize_deserialize_clear(root->rchild);
    acm_1_5_shu_serialize_deserialize_clear(root);
}
Node *acm_1_5_shu_serialize_deserialize_insert(Node *root,int key){
    if(root==NULL){return acm_1_5_shu_serialize_deserialize_getNewNode(key);}
    if(rand()%2)root->lchild= acm_1_5_shu_serialize_deserialize_insert(root->lchild,key);
    else root->rchild= acm_1_5_shu_serialize_deserialize_insert(root->rchild,key);
    return root;
}
Node *acm_1_5_shu_serialize_deserializ_getRandomBinaryTree(int n){
    Node *root=NULL;
    for (int i = 0; i < n; i++) {
        root=acm_1_5_shu_serialize_deserialize_insert(root,rand()%100);
    }
    return root;
}


//开始变成广义表-序列化
char buff[1000];
int len=0;
void __acm_1_5_shu_serialize_deserialize_serialize(Node *root){
    if (root==NULL) return;
    //根(左,右)
    len+=snprintf(buff+len,100,"%d",root->key);

    if(root->lchild==NULL && root->rchild==NULL) return;
    len+= snprintf(buff+len,100,"(");
    __acm_1_5_shu_serialize_deserialize_serialize(root->lchild);
    if(root->rchild){
        len+=  snprintf(buff+len,100,",");
        __acm_1_5_shu_serialize_deserialize_serialize(root->rchild);
    }
    len+=  snprintf(buff+len,100,")");
    return ;
}
void acm_1_5_shu_serialize_deserialize_serialize(Node *root){
    memset(buff,0,sizeof(buff));
    len=0;
    __acm_1_5_shu_serialize_deserialize_serialize(root);
    return ;
}
void acm_1_5_shu_serialize_deserializ_print(Node *root){
    printf("%d(%d,%d)\n",KEY(root),KEY(root->lchild),KEY(root->rchild));
    return;
}
void acm_1_5_shu_serialize_deserializ_output(Node *root){
    if(root==NULL) return;
    acm_1_5_shu_serialize_deserializ_print(root);
    acm_1_5_shu_serialize_deserializ_output(root->lchild);
    acm_1_5_shu_serialize_deserializ_output(root->rchild);
    return;
}
void acm_1_5_shu_serialize_deserialize_test(){
    srand(time(0));
#define MAX_NODE 10
    Node *root= acm_1_5_shu_serialize_deserializ_getRandomBinaryTree(MAX_NODE);
    //拥有了10个结点的二叉树

    //二叉树变成广义表-序列化

    acm_1_5_shu_serialize_deserialize_serialize(root);
    printf("Buff[] : %s\n",buff);

    //对比,输出二叉树前序遍历和中序遍历
    acm_1_5_shu_serialize_deserializ_output(root);

}

广义表转二叉树

A -> 直接节点A

A(,B) ->

  1. 遇到关键字就生成新的节点
  2. 遇到左括号就把新生成的节点压入栈
  3. 遇到 逗号 , **标记(flag)**当前处理右子树了
  4. 遇到右括号就将栈顶元素出栈
  5. 每次生成一个新的节点的时候根据标记设置栈顶元素的左右子树
//
// Created by cry on 2024/3/15.
//
//acm_1_5_shu_serialize_deserialize_
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#define KEY(n) (n?n->key:-1) //-1表示没有值
#define MAX_NODE 10
//二叉树转广义表
typedef struct Node{
    int key;
    struct Node *lchild,*rchild;
}Node;
Node *acm_1_5_shu_serialize_deserialize_getNewNode(int key){
    Node *p=(Node *) malloc(sizeof(Node));
    p->key=key;
    p->lchild=p->rchild=NULL;
    return p;
}
void acm_1_5_shu_serialize_deserialize_clear(Node *root){
    if(root==NULL)return ;
    acm_1_5_shu_serialize_deserialize_clear(root->lchild);
    acm_1_5_shu_serialize_deserialize_clear(root->rchild);
    acm_1_5_shu_serialize_deserialize_clear(root);
}
Node *acm_1_5_shu_serialize_deserialize_insert(Node *root,int key){
    if(root==NULL){return acm_1_5_shu_serialize_deserialize_getNewNode(key);}
    if(rand()%2)root->lchild= acm_1_5_shu_serialize_deserialize_insert(root->lchild,key);
    else root->rchild= acm_1_5_shu_serialize_deserialize_insert(root->rchild,key);
    return root;
}
Node *acm_1_5_shu_serialize_deserializ_getRandomBinaryTree(int n){
    Node *root=NULL;
    for (int i = 0; i < n; i++) {
        root=acm_1_5_shu_serialize_deserialize_insert(root,rand()%100);
    }
    return root;
}


//TODO 开始变成广义表-序列化
char buff[1000];
int len=0;
void __acm_1_5_shu_serialize_deserialize_serialize(Node *root){
    if (root==NULL) return;
    //根(左,右)
    len+=snprintf(buff+len,100,"%d",root->key);

    if(root->lchild==NULL && root->rchild==NULL) return;
    len+= snprintf(buff+len,100,"(");
    __acm_1_5_shu_serialize_deserialize_serialize(root->lchild);
    if(root->rchild){
        len+=  snprintf(buff+len,100,",");
        __acm_1_5_shu_serialize_deserialize_serialize(root->rchild);
    }
    len+=  snprintf(buff+len,100,")");
    return ;
}
void acm_1_5_shu_serialize_deserialize_serialize(Node *root){
    memset(buff,0,sizeof(buff));
    len=0;
    __acm_1_5_shu_serialize_deserialize_serialize(root);
    return ;
}

//广义表转二叉树
Node *acm_1_5_shu_serialize_deserialize_deserialize(char *buff,int n){
//手撸栈麻烦,用数组
    Node **s=(Node **) malloc(sizeof(Node *)*MAX_NODE );
    int top=-1,flag=0,scode=0; //状态码scode,用状态机
    Node *p=NULL,*root=NULL;
    for(int i=0;buff[i];i++){
        switch(scode){ //每一种状态码下应该干什么
            case 0:{
                if(buff[i]>='0' && buff[i] <='9'){
                    scode=1;
                }
                else if(buff[i]=='('){
                    scode=2;
                }
                else if(buff[i]==','){
                    scode=3;
                }else scode=4;
                i-=1;
            }
                break;
            case 1:{
                //新建一个节点
                int key=0;
                while(buff[i] <='9' && buff[i]>='0'){
                    key=key*10+(buff[i]-'0');//字符串转为数字
                    i+=1;
                }
                p=acm_1_5_shu_serialize_deserialize_getNewNode(key);
                //设置为栈顶元素的左子树或右子树
                if(top >=0){
                    if(flag==0){
                        s[top]->lchild=p;
                    }
                    else if(flag==1){
                        s[top]->rchild=p;
                    }
                }
                //收尾工作
                i-=1;
                scode=0;
            }
                break;
            case 2:{
                //压栈
                s[++top]=p;
                flag=0;
                scode=0;
            }
                break;
            case 3:{
                flag=1;
                scode=0;
            }
                break;
            case 4:{
                root=s[top];
                top-=1; //弹栈
                scode=0;
            }
                break;
        }
    }
    return root;
}
void acm_1_5_shu_serialize_deserializ_print(Node *root){
    printf("%d(%d,%d)\n",KEY(root),KEY(root->lchild),KEY(root->rchild));
    return;
}
void acm_1_5_shu_serialize_deserializ_output(Node *root){
    if(root==NULL) return;
    acm_1_5_shu_serialize_deserializ_print(root);
    acm_1_5_shu_serialize_deserializ_output(root->lchild);
    acm_1_5_shu_serialize_deserializ_output(root->rchild);
    return;
}
void acm_1_5_shu_serialize_deserialize_test(){
    srand(time(0));

    Node *root= acm_1_5_shu_serialize_deserializ_getRandomBinaryTree(MAX_NODE);
    //拥有了10个结点的二叉树

    //二叉树变成广义表-序列化

    acm_1_5_shu_serialize_deserialize_serialize(root);
    printf("Buff[] : %s\n",buff);

    //对比,输出二叉树前序遍历和中序遍历
    acm_1_5_shu_serialize_deserializ_output(root);
    printf("\n");
    Node *new_root=acm_1_5_shu_serialize_deserialize_deserialize(buff,len);
    acm_1_5_shu_serialize_deserializ_output(new_root);
    return ;
}

哈夫曼编码

编码方式导致网速明显感到差别

zoom和腾讯会议早期对比

明明只传26个字母5字节 非要用ascii8字节,慢3秒

变长编码:每个字母长度不一样

定长编码:每个字母长度一样

例如:传输 a b c d四种字符,共100个字符

定长:只用两个比特位:2*100=200/100=2s

变长:a:1 b:011 c:010 d:00 出现概率越高字符越小:50*1+20*3+10*3+20*2=180

所以怎么衡量两套编码?

平均编码长度!

0.5*1+0.2*3+0.1*3+0.2*2=1.8

哈夫曼编码–最优的变长编码

6666666

证明

第h层有2^h个节点总数

第h层覆盖了2^(h-Li)个节点

不超过h层节点数量上限

和信息熵有关

//
// Created by cry on 2024/3/16.
//
//acm_1_5_shu_hafuman_


#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cstdlib>
#include <cassert>
#include <cstring>
#define MAX_CHAR_NUM 128

using namespace std;
typedef struct Node{
    char ch;
    int freq;
    struct Node *lchild,*rchild;
}Node;
Node *acm_1_5_shu_hafuman_getNewNode(int freq,char ch){
    Node *p=(Node *) malloc(sizeof(Node));
    p->lchild=p->rchild=NULL;
    p->ch=ch;
    p->freq=freq;
    return p;
}
void acm_1_5_shu_hafuman_clear(Node *root){
    if(root==NULL) return;
    acm_1_5_shu_hafuman_clear(root->lchild);
    acm_1_5_shu_hafuman_clear(root->rchild);
    acm_1_5_shu_hafuman_clear(root);
}
void acm_1_5_shu_hafuman_swap_node(Node **node_arr,int i,int j){
    Node *temp=node_arr[i];
    node_arr[i]=node_arr[j];
    node_arr[j]=temp;
    return ;
}
int acm_1_5_shu_hafuman_find_min_node(Node **node_arr,int n){
    int ind=0;
    for(int j=0;j<=n;j++){
        if(node_arr[ind]->freq > node_arr[j]->freq) ind=j;
    }
    return ind;
}
Node *acm_1_5_shu_hafuman_buildHaffmanTree(Node **node_arr,int n){
    //TODO find_two_min_node 找到两个最小值节点
    for(int i=1;i<n;i++){
        //进行n-1次合并
        //找两个最小值的节点,结束后,最小值在最后
//        Node *temp=node_arr[0]; //找最小值
        int ind1=acm_1_5_shu_hafuman_find_min_node(node_arr,n-i);

//节点交换
        acm_1_5_shu_hafuman_swap_node(node_arr,ind1,n-i);
        int ind2=acm_1_5_shu_hafuman_find_min_node(node_arr,n-i-1);
        acm_1_5_shu_hafuman_swap_node(node_arr,ind2,n-i-1);
        //TODO merge_to_node合并成一个节点并回到数组最后一位
        int freq=node_arr[n-i]->freq+node_arr[n-i-1]->freq;
        Node *node=acm_1_5_shu_hafuman_getNewNode(freq,0);
        node->lchild=node_arr[n-i-1]; //左子树为次小值节点
        node->rchild=node_arr[n-i]; //右子树为最小值节点
        node_arr[n-i-1]=node;
    }
    return node_arr[0]; //结束后根节点在第一位
}
void acm_1_5_shu_hafuman_output(Node **node_arr,int n){
    for(int i=0;i<n;i++){
        cout << node_arr[i]->ch << " " << node_arr[i]->freq << endl;
//        printf("%c %d\n",node_arr[i]->ch,node_arr[i]->freq);
    }
    return ;
}
char *char_code[128]={0}; //数组

void acm_1_5_shu_hafuman_extractHaffmanCode(Node *root,char buff[],int k){ //k为路径长度
    buff[k]=0;
    //当前节点是不是叶子节点,如果是,直接返回
    if(root->lchild==NULL && root->rchild==NULL){
        char_code[root->ch]=strdup(buff);
        printf("%c:%s\n",root->ch,buff);
        return;
    }
    buff[k]='0';
    acm_1_5_shu_hafuman_extractHaffmanCode(root->lchild,buff,k+1);
    buff[k]='1';
    acm_1_5_shu_hafuman_extractHaffmanCode(root->rchild,buff,k+1);
    return;
}
void acm_1_5_shu_hafuman_test(){
    string filename = "input2.txt";
    ifstream file(filename);
    string line;
    assert( file.is_open() );   // 确定文件打开了;
    getline(file, line);
    int n,freq;
    char s;
    n= stoi(line);
//    scanf("%d",&n);
    Node **node_arr=(Node **) malloc(sizeof(Node*)*n); //二维数组
    printf("开始\n");
    for(int i=0;i<n;i++){
        getline(file, line); // 读取每行数据
        stringstream ss(line);
        ss >> s >> freq;  // 从文件中读取字符和频率
//        cout << s << " " << freq << endl;
//        scanf("%s%d",s,&freq);
        node_arr[i]= acm_1_5_shu_hafuman_getNewNode(freq,s);
    }
    acm_1_5_shu_hafuman_output(node_arr,n);
    printf("\n");
    Node *root=acm_1_5_shu_hafuman_buildHaffmanTree(node_arr,n);
//提取相关编码信息
    char buff[1000];
    acm_1_5_shu_hafuman_extractHaffmanCode(root,buff,0);
    for(int i=0;i<MAX_CHAR_NUM;i++){
        if(char_code[i]==NULL) continue;
        //char_code[i]不为空,输出字符
        printf("%c:%s\n",i,char_code[i]); //按照ABCD顺序输出(字典序)
    }
    acm_1_5_shu_hafuman_clear(root);
}

习题环节

二叉树的前序遍历 : 跟左右

N叉树:根 1 2 3棵树

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> children;

    Node() {}

    Node(int _val) {
        val = _val;
    }

    Node(int _val, vector<Node*> _children) {
        val = _val;
        children = _children;
    }
};
*/
// N叉树前序遍历
class Solution {
public:
    vector<int> preorder(Node* root) {
        if(root ==NULL) return vector<int>();
        //不为空
        vector<int> ans; //遍历结果
        ans.push_back(root->val);
        for(auto x:root->children) //一次扫描root的子节点
        {
            //x每个子树的前序遍历结果 给temp
            vector<int> temp=preorder(x); //先每个子树
            for(auto y:temp){
                //然后temp遍历
                ans.push_back(y);
            }
        }
        return ans;
    }
};
效率低

没有什么是一层封装解决不了的,如果有,就两层

class Solution {
public:
//传入引用是为了减少数据拷贝次数
    void __preorder(Node *root,vector<int> &ans){
        if(root==NULL) return;
        ans.push_back(root->val);
        for(auto x:root->children){
            __preorder(x,ans);
        }
        return ;
    }
    vector<int> preorder(Node* root) {
        vector<int> ans; //root前序遍历结果
        __preorder(root,ans);
        return ans;
    }
};

105leet:前中序回复二叉树

左子树的前中序 合成大的左子树

右子树的前中序 合成大的右子树

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        //边界条件为size==0
        int n=preorder.size();
        if(n==0) return NULL;
        int pos=0; //中序遍历中根节点的位置
        while(inorder[pos]!=preorder[0]){
            pos+=1;
        }
        //找到了根节点位置
        //前序1-pos为左子树
        //中序0到pos-1为左子树
        TreeNode *root=new TreeNode(preorder[0]);
        vector<int> preArr,inArr; //存储左右子树的前中序遍历结果
        for(int i=1;i<=pos;i++){
            preArr.push_back(preorder[i]); //左子树前序遍历结果
        }
        for(int i=0;i<pos;i++){
            inArr.push_back(inorder[i]); //左子树中序遍历结果
        }
        root->left=buildTree(preArr,inArr); //然后继续左子树往下分
        preArr.clear();
        inArr.clear();
        for(int i=pos+1;i<n;i++){
            preArr.push_back(preorder[i]); //右边子树前序遍历结果
        }
        for(int i=pos+1;i<n;i++){
            inArr.push_back(inorder[i]); //左子树中序遍历结果
        }
        //回复右子树
        root->right=buildTree(preArr,inArr); //然后继续左子树往下分
        return root; 
    }
    
};
class Solution(object):
    def buildTree(self, preorder, inorder):
        """
        :type preorder: List[int]
        :type inorder: List[int]
        :rtype: TreeNode
        """
        if not preorder or not inorder:
            return None

        root_val = preorder[0]
        root_index = inorder.index(root_val) #在中序遍历中找到根节点
		# while(inorder[pos]!=preorder[0]):pos+=1;
        root = TreeNode(root_val)
        # 左子树是1:pos 0:pos
        root.left = self.buildTree(preorder[1:root_index+1], inorder[:root_index])
        # 右子树是:pos+1:n  pos+1:n
        root.right = self.buildTree(preorder[root_index+1:], inorder[root_index+1:])

        return root

leetcode102:二叉树层序遍历

用队列

广搜 或者 深搜都可以

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def levelOrder(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        if not root:
            return []
        q = [root]
        ans = []
        while q:
            t = []
            for _ in range(len(q)):
                node = q.pop(0)
                t.append(node.val)
                if node.left:
                    q.append(node.left)
                if node.right:
                    q.append(node.right)
            ans.append(t) #每次压入一层
        return ans
        

队列:用广搜完成层序遍历

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        if(root==NULL) return vector<vector<int>>();
        TreeNode *node;
        queue<TreeNode *>  q; //队列,类型为TreeNode
        vector<vector<int>> ans;
        q.push(root); 
        while(!q.empty()){
            int cnt=q.size();
            vector<int> temp;
            for(int i=0;i<cnt;i++){
                node=q.front();
                temp.push_back(node->val);
                if(node->left) q.push(node->left);
                if(node->right) q.push(node->right);
                q.pop();
            }
            ans.push_back(temp);
        }
    return ans;
        
    }
};

栈:用深搜完成层序遍历

只需要在深搜过程中记录层数就行

class Solution{
    public:
    void dfs(TreeNode *node,int k,vector<vector<int>> &ans){
        if(node ==NULL) return;
        if(k==ans.size()) ans.push_back(vector<int>());
        ans[k].push_back(node->val);
        dfs(node->left,k+1,ans);
        dfs(node->right,k+1,ans);
        return ;
    }
    vector<vector<int>> levelOrder(TreeNode *root){
        vector<vector<int>> ans;
        dfs(root,0,ans);
        return ans;
    }
}

leetcode 226:翻转二叉树

翻转根节点-翻转左子树-翻转右子树

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root==NULL)return NULL;
        TreeNode *left=invertTree(root->left);
        TreeNode *right=invertTree(root->right);
        root->left=right;
        root->right=left;
        return root;
    }
};
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root==NULL)return NULL;
        swap(root->left,root->right);
        invertTree(root->left);
        invertTree(root->right);
        return root;
    }
};

leetcode 107 二叉树层序遍历2

二叉树层序遍历,后直接reverse

class Solution {
public:
    void dfs(TreeNode *root,int k,vector<vector<int>> &ans){
        if(root==NULL) return;
        if(k==ans.size()) ans.push_back(vector<int>());
        ans[k].push_back(root->val);
        dfs(root->left,k+1,ans);
        dfs(root->right,k+1,ans);
        return;
    }
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        vector<vector<int>> ans;
        dfs(root,0,ans);
        //交换
        reverse(ans.begin(),ans.end()); 
        //等价于:
        for(int i=0,j=ans.size()-1;i<j;i++,j--){
            swap(ans[i],ans[j]);//冒泡排序
        }
        return ans;
    }
};

leetcode103 二叉树的锯齿形层序遍历

每一层的节点值按照从左到右的顺序进行排列,但是每一层都是从前一层的右节点开始排列的

class Solution {
public:
    void dfs(TreeNode *root,int k,vector<vector<int>> &ans){
        if(root==NULL) return ;
        if(k==ans.size()) ans.push_back(vector<int>());
        ans[k].push_back(root->val);
        dfs(root->left,k+1,ans);
        dfs(root->right,k+1,ans);
        return ;
    }
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> ans;
        dfs(root,0,ans);
        for(int i=1;i<ans.size();i+=2){ //偶数行都翻转
            // reverse(ans[i].begin(),ans[i].end());
            for(int k=0,g=ans[i].size()-1;k<g;k++,g--){
                swap(ans[i][k],ans[i][g]);
            }
        }

        return ans;
    }
};

leetcode26 :树的子结构

class Solution{
    public:
    bool match_one(TreeNode *A,TreeNode *B){
        if(A==NULL) return B==NULL;
        if(B==NULL) return true;
        if(A->val != B->val) return false;
        return match_one(A->left,B->left) && match_one(A->right,B->right);
    }
    bool isSubstructure(TreeNode *A,TreeNode *B){
        if(A==NULL) return B==NULL;
        if(B==NULL) return false;
        if(A->val == B->val && match_one(A,B)) return true;
        if(isSubstructure(A->left,B)) return true;
        if(isSubstructure(A->right,B)) return true;
        return false;
    }
}
class Solution{
    public:
    bool isSubstructure(TreeNode *A,TreeNode *B){
        return (A!=nullptr && B!==nullptr) && (recur(A,B) || isSubstructure(A->left,B) || isSubstrcture(A->right,B));
    }
    private:
   	bool recur(TreeNode *A,TreeNode *B){
        if(B==nullptr) return true;
        if(A==nullptr || A->val != B->val) return false;
        return recur(A->left,B->left) && recur(A->right,B->right);
}
}

HZOJ 287 合并果子:哈夫曼编码

二叉树的叶子节点*每个叶子节点深度 最后相加

ai * 路径长度,相加

#include <iostream>
#include <set>
using namespace std;
void acm_1_4_shu_hzoj287_test(){
    int n;
    cin>> n;
    set<int> s;

    s.insert(10); //{10}
    cout << *s.begin() <<endl;

    s.insert(8); //{10,8,9}
    cout << *s.begin() <<endl;

    s.insert(9); //{10,8,9}
    cout << *s.begin() <<endl;
}
// 输出:10 8 8 
//set<int> 的begin()每次输出最小值
//s.size() 输出集合有多少个元素
//set不允许有重复元素:用set<pair<int,int>> s避免重复,加一个下标
//
// Created by cry on 2024/3/24.
//
#include <iostream>
#include <set>
using namespace std;
void acm_1_4_shu_hzoj287_test(){
    int n;
    cin>> n;
    set<pair<int,int>> s;

    s.insert(pair<int,int>(10,1));
    cout << s.begin()->first << "," << s.size()<<endl;

    s.insert(pair<int,int>(8,2));
    cout << s.begin()->first << ","<< s.size()<<endl;

    s.insert(pair<int,int>(9,3));
    cout << s.begin()->first<< ","<< s.size() <<endl;
    s.insert(pair<int,int>(9,4));
    cout << s.begin()->first<< ","<< s.size() <<endl;
}
//10,1
//8,2
//8,3
//8,4
#include <iostream>
#include <set>
using namespace std;
typedef pair<int,int> PII
void hzoj287(){
    int n,a;
    set<PII> s;
    cin >> n;
    //输入
    for(int i=0;i<n;i++){
        cin >> a;
        s.insert(PII(a,i));
    }
    int ans=0;
    //开始哈夫曼编码,每次找最小的,然后相加
    for(int i=1;i<n;i++){
        int min1=s.begin()->first;
        s.erase(s.begin());
        int min2=s.begin()->first;
        s.erase(s.begin());
        ans+=min1+min2;
        s.insert(PII(min1+min2,n+i));
    }
    cout << ans << endl;
}

hzoj245:货仓选址

选一个点建立仓库,让所有商店到仓库的距离总和最小

样例输入
5
1 3 5 6 10
样例输出
12
//
// Created by cry on 2024/3/24.
//
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
void acm_1_4_shu_hzoj245(){
    int n,a;
    cin >> n;
    vector<int> arr;
    //输入
    for(int i=0;i<n;i++){
        cin >> a;
        arr.push_back(a);
    }
    //输出每个商店到arr中间中位数的距离,并相加
    sort(arr.begin(),arr.end()); //排序
    int p=arr[n/2],ans=0;
    for(int i=0;i<n;i++){
        ans+=abs(arr[i]-p);
    }
    cout << ans <<endl;
}

cout << s.begin()->first<< ","<< s.size() <<endl;
}
//10,1
//8,2
//8,3
//8,4
#include <iostream>
#include <set>
using namespace std;
typedef pair<int,int> PII
void hzoj287(){
    int n,a;
    set<PII> s;
    cin >> n;
    //输入
    for(int i=0;i<n;i++){
        cin >> a;
        s.insert(PII(a,i));
    }
    int ans=0;
    //开始哈夫曼编码,每次找最小的,然后相加
    for(int i=1;i<n;i++){
        int min1=s.begin()->first;
        s.erase(s.begin());
        int min2=s.begin()->first;
        s.erase(s.begin());
        ans+=min1+min2;
        s.insert(PII(min1+min2,n+i));
    }
    cout << ans << endl;
}

hzoj245:货仓选址

选一个点建立仓库,让所有商店到仓库的距离总和最小

样例输入
5
1 3 5 6 10
样例输出
12
//
// Created by cry on 2024/3/24.
//
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
void acm_1_4_shu_hzoj245(){
    int n,a;
    cin >> n;
    vector<int> arr;
    //输入
    for(int i=0;i<n;i++){
        cin >> a;
        arr.push_back(a);
    }
    //输出每个商店到arr中间中位数的距离,并相加
    sort(arr.begin(),arr.end()); //排序
    int p=arr[n/2],ans=0;
    for(int i=0;i<n;i++){
        ans+=abs(arr[i]-p);
    }
    cout << ans <<endl;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

厨 神

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值