复习——栈、队列、树、哈希表

栈、队列、树、哈希表

一、栈(Stack)

1.1 基本概念

定义 :限定仅在表尾 进行插入和删除操作的线性表

特性:

  • 先进后出(FILO)

  • 后进先出(LIFO)

核心概念:

  • 栈顶(Top):允许操作的一端

  • 栈底(Bottom):不允许操作的一端

  • 入栈(Push):向栈顶添加元素

  • 出栈(Pop):从栈顶移除元素

1.2 数据结构栈 vs 系统栈

对比项 数据结构栈 系统栈(调用栈)
存储位置 堆空间 内存固定区域(约8M)
内容 用户数据 函数调用、局部变量、参数
大小 可自定义 有限制
实现 数组/链表 硬件/OS支持

1.3 实现方式

顺序栈(数组实现)
复制代码
#define MAXSIZE 100
typedef struct {
    int data[MAXSIZE];
    int top;  // 栈顶指针
} SeqStack;
链栈(链表实现)
复制代码
typedef struct StackNode {
    int data;
    struct StackNode *next;
} StackNode;

typedef struct {
    StackNode *top;  // 栈顶指针
    int size;        // 栈大小
} LinkStack;

1.4 基本操作

复制代码
// 初始化
void InitStack(SeqStack *S) {
    S->top = -1;
}

// 入栈
int Push(SeqStack *S, int x) {
    if (S->top == MAXSIZE-1) return 0; // 栈满
    S->data[++S->top] = x;
    return 1;
}

// 出栈
int Pop(SeqStack *S, int *x) {
    if (S->top == -1) return 0; // 栈空
    *x = S->data[S->top--];
    return 1;
}

1.5 应用场景

  1. 函数调用栈:保存调用现场

  2. 表达式求值:中缀转后缀

  3. 括号匹配:检查嵌套正确性

  4. 递归实现:转化为非递归

  5. DFS算法:路径记录与回溯

二、队列(Queue)

2.1 基本概念

定义 :只允许在队尾 插入,在队头删除的线性表

特性:先进先出(FIFO)

核心概念:

  • 队头(Front):允许删除的一端

  • 队尾(Rear):允许插入的一端

2.2 实现方式

循环队列(顺序实现)
复制代码
#define MAXSIZE 100
typedef struct {
    int data[MAXSIZE];
    int front;  // 队头指针
    int rear;   // 队尾指针
} CircularQueue;

// 判空:front == rear
// 判满:(rear + 1) % MAXSIZE == front
链式队列
复制代码
typedef struct QueueNode {
    int data;
    struct QueueNode *next;
} QueueNode;

typedef struct {
    QueueNode *front;  // 队头指针
    QueueNode *rear;   // 队尾指针
    int size;
} LinkQueue;

2.3 基本操作

复制代码
// 入队(循环队列)
int EnQueue(CircularQueue *Q, int x) {
    if ((Q->rear + 1) % MAXSIZE == Q->front)
        return 0; // 队满
    Q->data[Q->rear] = x;
    Q->rear = (Q->rear + 1) % MAXSIZE;
    return 1;
}

// 出队
int DeQueue(CircularQueue *Q, int *x) {
    if (Q->front == Q->rear)
        return 0; // 队空
    *x = Q->data[Q->front];
    Q->front = (Q->front + 1) % MAXSIZE;
    return 1;
}

2.4 应用场景

  1. 任务调度:CPU进程调度

  2. 缓冲区:打印机任务队列

  3. BFS算法:图的广度优先搜索

  4. 消息队列:系统间通信

  5. 数据流处理:生产者-消费者模式

三、树(Tree)

3.1 基本概念

定义:n(n≥0)个节点的有限集合

  • n=0:空树

  • n>0:有且仅有一个根节点,其余分为m个互不相交的子树

重要术语:

  • 节点的度:子树的个数

  • 叶子节点:度为0的节点

  • 树的度:树中最大的节点度数

  • 节点的层次:根为第1层

  • 树的高度:最大层次

3.2 二叉树(Binary Tree)

定义:每个节点最多有两个子树的树结构

特殊二叉树
  1. 满二叉树:

    • 所有层都达到最大节点数

    • 深度为k,节点数=2^k - 1

  2. 完全二叉树:

    • 除最后一层外都是满的

    • 最后一层节点集中在左边

二叉树性质
  1. 第i层最多有 2^(i-1) 个节点

  2. 深度为k的二叉树最多有 2^k - 1 个节点

  3. 叶子节点数 = 度为2的节点数 + 1

  4. n个节点的完全二叉树深度 = ⌊log₂n⌋ + 1

3.3 遍历方式

深度优先遍历(DFS)
复制代码
// 先序遍历:根->左->右
void PreOrder(TreeNode *root) {
    if (root == NULL) return;
    visit(root);
    PreOrder(root->left);
    PreOrder(root->right);
}

// 中序遍历:左->根->右
void InOrder(TreeNode *root) {
    if (root == NULL) return;
    InOrder(root->left);
    visit(root);
    InOrder(root->right);
}

// 后序遍历:左->右->根
void PostOrder(TreeNode *root) {
    if (root == NULL) return;
    PostOrder(root->left);
    PostOrder(root->right);
    visit(root);
}
广度优先遍历(层序遍历)
复制代码
void LevelOrder(TreeNode *root) {
    if (root == NULL) return;
    
    TreeNode *queue[MAXSIZE];
    int front = 0, rear = 0;
    queue[rear++] = root;
    
    while (front < rear) {
        TreeNode *node = queue[front++];
        visit(node);
        
        if (node->left) queue[rear++] = node->left;
        if (node->right) queue[rear++] = node->right;
    }
}

3.4 存储结构

顺序存储(数组)

适用于完全二叉树

复制代码
// 下标从1开始
// 节点i的父节点:i/2
// 左孩子:2*i,右孩子:2*i+1
链式存储(二叉链表)
复制代码
typedef struct BiTNode {
    int data;
    struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;

3.5 树的应用

  1. 文件系统:目录结构

  2. 数据库索引:B树、B+树

  3. 决策树:机器学习

  4. 语法分析树:编译器

  5. 家谱图:层次关系

四、哈希表(Hash Table)

4.1 基本概念

定义:通过哈希函数将关键字映射到表中位置的数据结构

核心思想:存储位置 = f(key)

目标:实现O(1)的平均查找时间

4.2 哈希函数设计

方法 公式 特点
直接定址 h(key) = a×key + b 简单,无冲突
除留余数 h(key) = key % p 最常用
平方取中 取key²的中间几位 分布均匀
折叠法 分割相加 适合长关键字

4.3 冲突解决方法

开放定址法
复制代码
// 线性探测:hᵢ(key) = (h(key) + i) % m
// 二次探测:hᵢ(key) = (h(key) ± i²) % m
// 双重散列:hᵢ(key) = (h₁(key) + i×h₂(key)) % m
链地址法(最常用)
复制代码
typedef struct HashNode {
    int key;
    int value;
    struct HashNode *next;
} HashNode;

typedef struct {
    HashNode **table;
    int size;
    int count;
} HashMap;

4.4 性能指标

装载因子

α = 表中元素个数 / 哈希表长度

  • 通常保持 α < 0.75
平均查找长度(ASL)
方法 ASL成功(平均) ASL失败(平均)
线性探测 (1+1/(1-α)²)/2 (1+1/(1-α))/2
链地址法 1+α/2 α+e⁻α

4.5 基本操作实现

复制代码
// 链地址法实现
HashMap* CreateHashMap(int size) {
    HashMap *hm = (HashMap*)malloc(sizeof(HashMap));
    hm->table = (HashNode**)calloc(size, sizeof(HashNode*));
    hm->size = size;
    hm->count = 0;
    return hm;
}

int HashFunction(int key, int size) {
    return abs(key) % size;
}

void HashMapPut(HashMap *hm, int key, int value) {
    int index = HashFunction(key, hm->size);
    
    // 查找是否已存在
    HashNode *node = hm->table[index];
    while (node) {
        if (node->key == key) {
            node->value = value;  // 更新
            return;
        }
        node = node->next;
    }
    
    // 新建节点,头插法
    HashNode *newNode = (HashNode*)malloc(sizeof(HashNode));
    newNode->key = key;
    newNode->value = value;
    newNode->next = hm->table[index];
    hm->table[index] = newNode;
    hm->count++;
}

int HashMapGet(HashMap *hm, int key) {
    int index = HashFunction(key, hm->size);
    HashNode *node = hm->table[index];
    
    while (node) {
        if (node->key == key)
            return node->value;
        node = node->next;
    }
    
    return -1;  // 未找到
}

4.6 应用场景

  1. 快速查找:字典、缓存

  2. 数据去重:统计唯一值

  3. 数据库索引:加速查询

  4. 密码存储:存储哈希值

  5. 路由表:网络路由器

五、四种数据结构对比总结

特性 栈 队列 树 哈希表
访问方式 栈顶访问 队头访问 多种遍历 直接访问
插入位置 栈顶 队尾 指定位置 哈希位置
删除位置 栈顶 队头 指定位置 任意位置
时间复杂度 O(1) O(1) 遍历O(n) 平均O(1)
空间效率 高 高 中等 较低
主要应用 函数调用 任务调度 层次关系 快速查找

六、学习建议与常见问题

6.1 栈的注意事项

  • 栈溢出:递归深度过大或栈空间不足

  • 括号匹配:注意不同括号的优先级

  • 表达式求值:运算符优先级处理

6.2 队列的注意事项

  • 循环队列判空/满:注意判断条件

  • 假溢出:顺序队列的问题

  • 队列大小:合理设置初始大小

6.3 树的注意事项

  • 平衡性:树不平衡会影响性能

  • 遍历顺序:不同遍历的应用场景

  • 存储方式:根据需求选择合适结构

6.4 哈希表的注意事项

  • 哈希函数设计:影响性能的关键

  • 冲突处理:选择合适的解决方法

  • 装载因子:及时扩容避免性能下降

  • 内存管理:链地址法的节点释放


七、综合应用示例

7.1 计算器(栈应用)

复制代码
int calculate(char* s) {
    int stack[MAXSIZE], top = -1;
    int num = 0, res = 0;
    char sign = '+';
    
    for (int i = 0; s[i]; i++) {
        if (isdigit(s[i])) {
            num = num * 10 + (s[i] - '0');
        }
        
        if ((!isdigit(s[i]) && s[i] != ' ') || i == strlen(s)-1) {
            switch(sign) {
                case '+': stack[++top] = num; break;
                case '-': stack[++top] = -num; break;
                case '*': stack[top] *= num; break;
                case '/': stack[top] /= num; break;
            }
            sign = s[i];
            num = 0;
        }
    }
    
    while (top != -1) {
        res += stack[top--];
    }
    return res;
}

7.2 LRU缓存(哈希表+双向链表)

复制代码
// 实现O(1)的get和put操作
// 使用哈希表快速查找,双向链表维护访问顺序

这份笔记涵盖了栈、队列、树和哈希表的核心知识点。建议您:

  1. 理解每种数据结构的特点和适用场景

  2. 掌握基本操作的实现方法

  3. 多做练习题,特别是综合应用题目

  4. 在实际项目中尝试应用这些数据结构

相关推荐
写后端的胖头鱼5 小时前
一文详解B+树原理(附场景+示例)
数据结构·b树
阳光九叶草LXGZXJ6 小时前
达梦数据库-报错-15-列【XXX】长度超出定义
linux·运维·数据库·sql·学习
怕浪猫6 小时前
RAG 面试 6 连问,从原理到优化全部覆盖
python·算法·面试
奔跑的大白啊6 小时前
多容器共享目录权限踩坑记
linux·ubuntu·共享目录·linux权限·docker 容器化部署
无线通信科研笔记6 小时前
IEEE TVT 2026 论文精读与完整复现|相位误差如何重塑近场 RIS 的幅相响应
论文阅读·人工智能·python·算法·论文笔记
mmmmath_37 小时前
二叉树の递归遍历
算法
All for pursuit.7 小时前
【位运算-1】338.比特位计数
数据结构·c++·算法·leetcode
小师兄吃牛肉8 小时前
C语言中的nullptr有必要吗
c语言·c++·算法
mlidongfeng8 小时前
【学习】mKernel 分析
学习
Android系统攻城狮9 小时前
Linux Gstreamer深度解析之gst_audio_encoder_set_frame_max调用流程与实战(六十一)
android·linux·运维·音视频·gstreamer音视频·音视频进阶·gstreamer音视频进阶