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.总结

相关推荐
Elohim8158 分钟前
进程IO之 进程
linux·c语言
怀旧,15 分钟前
【Python】2. 基础语法(2)
开发语言·python
敲代码的瓦龙18 分钟前
C++?继承!!!
c语言·开发语言·c++·windows·后端·算法
沐知全栈开发27 分钟前
Django 视图 - FBV 与 CBV
开发语言
wirepuller_king38 分钟前
Python安装、pycharm配置和添加库下载
开发语言·python·pycharm
雪风飞舞1 小时前
Python解压多种格式压缩包
开发语言·python
我也爱吃馄饨1 小时前
GitHub push失败解决办法-fatal: unable to access ‘https://github.com/xxx
git·github·visual studio
沙滩小岛小木屋1 小时前
多个vue2工程共享node_modules
开发语言·前端·javascript
鱼嘻1 小时前
四足机器人环境监测系统相关问题
linux·c语言·开发语言·网络·机器人