数据结构:树、二叉树、最小堆、二叉搜索树

树的概念

树是一种重要的数据结构,广泛应用于计算机科学中。树的定义如下:

根节点:深度唯一,唯一没有父节点的节点

子结点:一个节点的后继节点

叶子节点:没有后继的子结点

深度:从根到节点的路径中边的数量,树的深度是所有叶子节点深度的最大值

路径:一个节点到另一个节点边构成的有序集合,任意两个节点的路径唯一

高度:节点到叶子节点中边的最大值

兄弟节点:拥有相同父节点的节点

除了根节点,每个节点有且仅有一个前驱节点(父节点),有 0 ~ n 个后继节点(子结点);

一棵树由n个节点和n-1条边构成。

节点定义

struct node
{
    int data;					// 数据域
    struct node *first_child;	// 第一个子结点
    struct node *next_sibling;	// 下一个兄弟节点
}

二叉树(binary tree)

概念:每个节点最多只有两个子节点的树。

二叉树分类

满二叉树:每层节点都是满的,节点数量为2^n(n为节点深度)。

完全二叉树:与满二叉树相似,但叶子节点聚集左边。

最大(小)堆:父节点大于(或小于)子节点。

二叉搜索树:左子树小于父节点,右子树大于父节点。

实现

  1. 顺序存储,基于树组,时间复杂度为O(1)

    • 满二叉树
    • 完全二叉树
      • 优先级队列

    节点的父、子节点位置计算公式:(n为当前节点索引)

    • 父节点:n / 2 - 1
    • 子结点:n * 2 + 1(左子树), n * 2 + 2(右子树)
  2. 链式存储,基于节点和指针

二叉树定义
struct node
{
    int data;			// 数据域
    struct node *left;	// 左子树
    struct node *right;	// 右子树
};

二叉树的遍历

  • 先序遍历:根节点->左子树->右子树
  • 中序遍历:左->根->右
  • 后续遍历:左->右->根
  • 层序遍历:从上往下,从左到右

 12923517425611203

最大堆:父节点大于子结点

最小堆:父节点小于子结点

构建一个最大堆:

​							5620			    4217		9		12		235	 11	  3

每次取走堆顶元素后,把最后一个元素补到空缺处,再进行堆排序,这样可以保证堆内没有空缺

堆定义

struct heap
{
    int *data;  // 使用数组实现最小堆,避免空间浪费
    int size;
    int capacity;
};

实现一个最小堆

/*
 * @Author: ZPY
 * @TODO: 实现一个最小堆
 */

#include <stdio.h>
#include <stdlib.h>

typedef struct heap
{
    int *data;  // 使用数组实现最小堆,避免空间浪费
    int size;
    int capacity;
} Heap;

void init(Heap *heap);
void insert(Heap *heap, int data);
void delete(Heap *heap);
void swap(int *, int *);
void display(Heap *heap);

int main(int argc, char const *argv[])
{
    Heap *heap = malloc(sizeof(Heap));

    // 初始化堆
    init(heap);

    // 插入元素 12,2,31,4,15,11,17,82,54
    insert(heap, 12);
    insert(heap, 2);
    insert(heap, 31);
    insert(heap, 4);
    insert(heap, 15);
    insert(heap, 11);
    insert(heap, 17);
    insert(heap, 82);
    insert(heap, 54);

    printf("堆中元素:");
    display(heap);

    // 删除堆顶元素
    delete(heap);
    printf("删除堆顶元素后,堆中元素:");
    display(heap);
    delete(heap);
    printf("删除堆顶元素后,堆中元素:");
    display(heap);

    free(heap);
    return 0;
}

void init(Heap *heap)
{
    heap->capacity = 10;
    heap->size = 0;
    heap->data = malloc(sizeof(int) * heap->capacity);
}

void insert(Heap *heap, int data)
{
    // 插入之前检查数组余量,满了就扩容为原来的 1.5 倍
    if (heap->size == heap->capacity - 1)
    {
        int incr = heap->capacity >> 1;
        heap->capacity += incr;
        heap->data = realloc(heap->data, sizeof(int) * heap->capacity);
    }
    heap->data[heap->size] = data;
    heap->size++;

    // 插入之后,从下往上调整
    for (int i = heap->size - 1; i > 0; i--)
    {
        int parent = (i - 1) >> 1;
        if (heap->data[parent] > heap->data[i])
        {
            swap(&heap->data[parent], &heap->data[i]);
        }
        else
        {
            break;
        }
    }
}

void delete(Heap *heap)
{
    // 如果堆为空,则返回
    if (heap == NULL)
    {
        return;
    }
    // 每次删除堆顶元素,将最后1个元素放到堆顶
    heap->data[0] = heap->data[heap->size - 1];
    heap->size--;
    if (heap->size == 0)
    {
        return;
    }
    // 删除之后,从上往下调整
    for (int i = 0; i < heap->size; i++)
    {
        int left = (i << 1) + 1;
        int right = (i << 1) + 2;
        // 父节点与最小的子节点交换
        if (right < heap->size && heap->data[right] < heap->data[left] && heap->data[right] < heap->data[i])
        {
            swap(&heap->data[right], &heap->data[i]);
        }
        else if (left < heap->size && heap->data[left] < heap->data[i])
        {
            swap(&heap->data[left], &heap->data[i]);
        }
        
    }

}

void display(Heap *heap)
{
    for (int i = 0; i < heap->size; i++)
    {
        printf("%d, ", heap->data[i]);
    }
    printf("\n");
}

void swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

输出结果

堆中元素:2, 4, 11, 12, 15, 31, 17, 82, 54, 
删除堆顶元素后,堆中元素:4, 12, 11, 54, 15, 31, 17, 82, 
删除堆顶元素后,堆中元素:11, 12, 17, 54, 15, 31, 82, 

二叉搜索树

为实现快速的搜索,左子树比父节点小,右子树比父节点大。

12923517425611203

构建一个二叉搜索树:

​						129		235	 11   17	423	_	_  _ _  20  _   56

遍历结果:

- 先序遍历:12953112317204256
- 中序遍历:35911121720234256      // 排序
- 后续遍历:35119201756422312
- 层序遍历:12923511174232056
完全二叉树的层序遍历:A,B,C,D,E,F,G,H,I,J,K

​						A

​				B				C

​			D		E		F		G

​		H	I	J	K 

先序:A,B,D,H,I,E,J,K,C,F,G

中序:H,D,I,B,J,E,K,A,F,C,G

后序:H,I,D,J,K,E,B,F,G,C,A

二叉搜索树的实现

/*
 * @Author: ZPY
 * @TODO: 二叉搜索树
 */

#include <stdio.h>
#include <stdlib.h>

typedef struct node
{
    int data;           // 数据域
    struct node *left;  // 左子树
    struct node *right; // 右子树
} Node;

typedef struct node Tree;

// 队列结构
typedef struct queue
{
    Tree **data;    // 队列存储节点指针
    int front;
    int rear;
    int capacity;
    int size;
} Queue;


Node *insert(Tree *t, int v); // 插入
int max(Tree *t);             // 最大值,递归/循环
int min(Tree *t);             // 最小值
void preorder(Tree *t);       // 先序遍历
void inorder(Tree *t);        // 中序遍历
void postorder(Tree *t);      // 后序遍历
void levelorder(Tree *t);     // 层序遍历,基于队列
void reverse(Tree *t);        // 翻转
void free_tree(Tree *t);      // 释放
Queue *create_queue();        // 创建队列
void enqueue(Queue *q, Tree *t);// 入队
Tree *dequeue(Queue *q);        // 出队
void destory_queue(Queue *q);   // 销毁队列


int main(int argc, char const *argv[])
{
    // 创建根节点
    Tree *t = malloc(sizeof(Tree));

    // 12,2,31,4,15,11,17,82,54
    // 插入节点
    t = insert(NULL, 12); // 将插入的第一个值作为根,否则会默认把0作为根
    t = insert(t, 2);
    t = insert(t, 31);
    t = insert(t, 4);
    t = insert(t, 15);
    t = insert(t, 11);
    t = insert(t, 17);
    t = insert(t, 82);
    t = insert(t, 54);

    printf("最大值:%d\n", max(t));
    printf("最小值:%d\n", min(t));

    printf("先序遍历:");
    preorder(t);

    printf("\n中序遍历:");
    inorder(t);

    printf("\n后序遍历:");
    postorder(t);

    printf("\n层序遍历:");
    levelorder(t);

    printf("\n=============翻转树================");
    reverse(t);
    printf("\n");

    printf("先序遍历:");
    preorder(t);

    printf("\n中序遍历:");
    inorder(t);

    printf("\n后序遍历:");
    postorder(t);

    printf("\n层序遍历:");
    levelorder(t);
    printf("\n");

    free_tree(t);
    return 0;
}

Node *insert(Tree *t, int v)
{
    if (t == NULL)
    {
        // 创建根节点
        t = malloc(sizeof(Tree));
        t->data = v;
        t->left = t->right = NULL;
    }
    else if (v < t->data)
    {
        t->left = insert(t->left, v);
    }
    else if (v > t->data)
    {
        t->right = insert(t->right, v);
    }

    return t;
}

int max(Tree *t)
{
    int max = t->data;

    while (t->right) // 循环实现
    {
        max = t->right->data;
        t = t->right;
    }

    return max;
}

int min(Tree *t)
{
    if (t->left == NULL)
    {
        return t->data;
    }

    return min(t->left); // 递归实现,最左边的一定是最小的
}

void preorder(Tree *t)
{
    if (t == NULL)
    {
        return;
    }

    // 根->左->右
    printf("%d, ", t->data); // 访问根
    preorder(t->left);       // 递归遍历左子树
    preorder(t->right);      // 递归遍历右子树
}

void inorder(Tree *t)
{
    if (t != NULL)
    {
        // 左->根->右
        inorder(t->left);        // 递归遍历左子树
        printf("%d, ", t->data); // 访问根
        inorder(t->right);       // 递归遍历右子树
    }
}

void postorder(Tree *t)
{
    if (t != NULL)
    {
        // 左->右->根
        postorder(t->left);      // 递归遍历左子树
        postorder(t->right);     // 递归遍历右子树
        printf("%d, ", t->data); // 访问根
    }
}

void levelorder(Tree *t)
{
    if (t == NULL)
    {
        return;
    }
    Queue *q = create_queue();
    enqueue(q, t);
    while (q->size)
    {
        t = dequeue(q);
        printf("%d, ", t->data);
        if (t->left)
        {
            enqueue(q, t->left);
        }
        if (t->right)
        {
            enqueue(q, t->right);
        }
    }
    destory_queue(q);
}

void reverse(Tree *t)
{
    if (t == NULL)
    {
        return;
    }

    // 交换左右子树
    Tree *temp = t->left;
    t->left = t->right;
    t->right = temp;

    // 递归遍历左右子树
    reverse(t->left);
    reverse(t->right);
}

void free_tree(Tree *t)
{
    if (t != NULL)
    {
        free_tree(t->left);
        free_tree(t->right);
        free(t);
    }
}

// 创建队列
Queue *create_queue()
{
    Queue *q = malloc(sizeof(Queue));
    if (q == NULL)
    {
        printf("创建队列失败\n");
        return NULL;
    }
    q->front = 0;
    q->rear = 0;
    q->size = 0;
    q->capacity = 10;
    q->data = malloc(sizeof(Tree *) * q->capacity);

    return q;   // 返回队列指针
}

// 入队
void enqueue(Queue *q, Tree *t)
{
    if (q->size == q->capacity)
    {
        printf("队列已满\n");
        return;
    }
    q->data[q->rear] = t;  // 队尾入队
    q->rear = (q->rear + 1) % q->capacity;  // 队尾移到下一个位置
    q->size++;
}

// 出队
Tree *dequeue(Queue *q)
{
    if (q->size == 0)
    {
        printf("队列为空\n");
        return NULL;
    }
    Tree *t = q->data[q->front];
    q->data[q->front] = NULL;    // 清除指针
    q->front = (q->front + 1) % q->capacity;    // 队首移到下一个位置
    q->size--;


    return t;
}

// 销毁队列
void destory_queue(Queue *q)
{
    for (int i = 0; i < q->capacity; i++)
    {
        q->data[i] = NULL;
    }
    free(q->data);
    free(q);
}

输出结果

最大值:82
最小值:2
先序遍历:12, 2, 4, 11, 31, 15, 17, 82, 54, 
中序遍历:2, 4, 11, 12, 15, 17, 31, 54, 82, 
后序遍历:11, 4, 2, 17, 15, 54, 82, 31, 12, 
层序遍历:12, 2, 31, 4, 15, 82, 11, 17, 54, 
=============翻转树================
先序遍历:12, 31, 82, 54, 15, 17, 2, 4, 11, 
中序遍历:82, 54, 31, 17, 15, 12, 11, 4, 2, 
后序遍历:54, 82, 17, 15, 31, 11, 4, 2, 12, 
层序遍历:12, 31, 2, 82, 15, 4, 54, 17, 11, 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值