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

相关推荐
奔跑吧邓邓子14 分钟前
【C语言实战(71)】C语言进阶:树与图的奇妙数据之旅
c语言···开发实战
earthzhang202116 分钟前
【1039】判断数正负
开发语言·数据结构·c++·算法·青少年编程
蕓晨19 分钟前
auto 自动类型推导以及注意事项
开发语言·c++·算法
一袋米扛几楼9822 分钟前
【软件安全】C语言特性 (C Language Characteristics)
java·c语言·安全
mjhcsp1 小时前
C++ 递推与递归:两种算法思想的深度解析与实战
开发语言·c++·算法
m0_748248021 小时前
《详解 C++ Date 类的设计与实现:从运算符重载到功能测试》
java·开发语言·c++·算法
神仙别闹1 小时前
基于C语言 HTTP 服务器客户端的实验
服务器·c语言·http
我命由我123452 小时前
Java 并发编程 - Delay(Delayed 概述、Delayed 实现、Delayed 使用、Delay 缓存实现、Delayed 延迟获取数据实现)
java·开发语言·后端·缓存·java-ee·intellij-idea·intellij idea
HLJ洛神千羽2 小时前
C++程序设计实验(黑龙江大学)
开发语言·c++·软件工程
kyle~2 小时前
算法数学---差分数组(Difference Array)
java·开发语言·算法