C PRIMER PLUS——第13节:高级数据表示

目录

[1.链表(Linked List)](#1.链表(Linked List))

[2.抽象数据类型(Abstract Data Type, ADT)](#2.抽象数据类型(Abstract Data Type, ADT))

[3.队列 ADT(Queue Abstract Data Type)](#3.队列 ADT(Queue Abstract Data Type))

4.链表与数组的对比

[5.二叉查找树(Binary Search Tree, BST)](#5.二叉查找树(Binary Search Tree, BST))

6.总结


1.链表(Linked List)

①含义

链表是一种动态数据结构,由一系列节点(Node)组成,每个节点包含数据域和指向下一个节点的指针。节点在内存中不必连续存储,通过指针连接形成逻辑上的序列。

②作用

  • 动态内存分配,无需预先指定大小
  • 高效的插入和删除操作(O (1))
  • 适合数据量不确定或频繁变动的场景

③表达方式

复制代码
struct Node {
    int data;           // 数据域
    struct Node* next;  // 指向下一个节点的指针
};

④注意事项

  • 内存管理:手动分配和释放内存(malloc/free
  • 指针操作:避免悬空指针和内存泄漏
  • 遍历链表时需检查空指针(NULL

⑤例题

实现链表的基本操作:插入、删除、遍历

复制代码
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node* next;
};

// 在链表头部插入节点
void insertAtHead(struct Node** head_ref, int new_data) {
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
    new_node->data = new_data;
    new_node->next = (*head_ref);
    (*head_ref) = new_node;
}

// 遍历链表并打印元素
void printList(struct Node* node) {
    while (node != NULL) {
        printf("%d ", node->data);
        node = node->next;
    }
}

int main() {
    struct Node* head = NULL;
    insertAtHead(&head, 3);
    insertAtHead(&head, 2);
    insertAtHead(&head, 1);
    printList(head);  // 输出: 1 2 3
    return 0;
}

2.抽象数据类型(Abstract Data Type, ADT)

①建立抽象

将数据结构的逻辑特性与其实现细节分离,定义数据类型的行为(操作)而不涉及具体实现。

②建立接口

声明一组操作(函数原型),定义外部可见的行为规范。

复制代码
// 接口示例:栈ADT
void push(int item);      // 入栈
int pop();                // 出栈
int isEmpty();            // 判断栈空

③使用接口

通过调用接口函数操作数据,无需关心内部实现。

复制代码
push(10);
int top = pop();

④实现接口

具体实现接口中声明的操作。

复制代码
// 栈的数组实现
#define MAX_SIZE 100
int stack[MAX_SIZE];
int top = -1;

void push(int item) {
    stack[++top] = item;
}

int pop() {
    return stack[top--];
}

⑤注意事项

  • 接口与实现分离,便于维护和修改
  • 避免直接访问内部数据,确保数据封装性

3.队列 ADT(Queue Abstract Data Type)

①定义队列抽象数据类型

队列是遵循 FIFO(先进先出)原则的线性数据结构,支持入队(enqueue)和出队(dequeue)操作。

②定义接口

复制代码
void enqueue(int item);   // 入队
int dequeue();            // 出队
int isEmpty();            // 判断队空

③实现接口数据表示

使用链表实现队列:

复制代码
struct Node {
    int data;
    struct Node* next;
};

struct Queue {
    struct Node* front;
    struct Node* rear;
};

void enqueue(struct Queue* q, int item) {
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
    new_node->data = item;
    new_node->next = NULL;
    
    if (q->rear == NULL) {
        q->front = q->rear = new_node;
        return;
    }
    
    q->rear->next = new_node;
    q->rear = new_node;
}

④测试队列

验证队列操作的正确性:

复制代码
struct Queue q;
q.front = q.rear = NULL;
enqueue(&q, 1);
enqueue(&q, 2);
printf("%d ", dequeue(&q));  // 输出: 1

⑤注意事项

  • 循环队列可避免数组实现的假溢出问题
  • 链表实现需处理空队列的特殊情况

4.链表与数组的对比

**例题:**将链表逆置:

复制代码
struct Node* reverseList(struct Node* head) {
    struct Node* prev = NULL;
    struct Node* curr = head;
    struct Node* next = NULL;
    
    while (curr != NULL) {
        next = curr->next;
        curr->next = prev;
        prev = curr;
        curr = next;
    }
    
    return prev;
}

(1)二叉树 ADT

每个节点最多有两个子节点(左子树和右子树)的树形结构。

(2)二叉查找树接口

复制代码
struct TreeNode* insert(struct TreeNode* root, int key);  // 插入节点
struct TreeNode* search(struct TreeNode* root, int key);  // 查找节点
void inorderTraversal(struct TreeNode* root);             // 中序遍历

(3)二叉树的实现

复制代码
struct TreeNode {
    int key;
    struct TreeNode* left;
    struct TreeNode* right;
};

struct TreeNode* insert(struct TreeNode* root, int key) {
    if (root == NULL) {
        struct TreeNode* new_node = (struct TreeNode*)malloc(sizeof(struct TreeNode));
        new_node->key = key;
        new_node->left = new_node->right = NULL;
        return new_node;
    }
    
    if (key < root->key)
        root->left = insert(root->left, key);
    else if (key > root->key)
        root->right = insert(root->right, key);
    
    return root;
}

(4)使用二叉树

复制代码
struct TreeNode* root = NULL;
root = insert(root, 50);
insert(root, 30);
insert(root, 70);
inorderTraversal(root);  // 输出: 30 50 70(升序)

(5)树的思想

利用分治策略高效解决问题(如排序、搜索),时间复杂度平均为 O (log n)。

(6)注意事项

  • 保持树的平衡性可避免退化为链表(如 AVL 树、红黑树)
  • 删除操作需处理三种情况:无子节点、一个子节点、两个子节点

6.总结

相关推荐
Morpheon几秒前
使用 R 处理图像
开发语言·计算机视觉·r语言
xuanzdhc1 小时前
C++重点知识详解(命名空间,缺省参数,函数重载)
开发语言·c++
软件开发技术深度爱好者1 小时前
python中学物理实验模拟:凸透镜成像和凹透镜成像
开发语言·python
C羊驼1 小时前
C语言:排序算法
c语言·算法·排序算法
小猫咪怎么会有坏心思呢1 小时前
华为OD机试-云短信平台优惠活动-完全背包(JAVA 2024E卷)
java·开发语言·华为od
鱼鱼说测试1 小时前
jmeter工具简单认识
开发语言·python
Hello.Reader2 小时前
Lua 事务双写、RedisGears 异步双写、零停机索引迁移与容量预估
开发语言·lua
虾球xz2 小时前
CppCon 2017 学习:Howling at the Moon: Lua for C++ Programmers
开发语言·c++·学习·lua
monicaaaaan2 小时前
旋转图像C++
开发语言·c++
无影无踪的青蛙2 小时前
[C++] STL数据结构小结
开发语言·数据结构·c++