C/C++数据结构之深入了解线性表:顺序表、单链表、循环链表和双向链表

news2024/12/23 14:40:06

线性表是一种基本的数据结构,它在计算机科学中起着至关重要的作用。线性表用于存储一系列具有相同数据类型的元素,这些元素之间存在顺序关系。在C/C++中,我们可以使用各种方式来实现线性表,其中包括顺序表、单链表、循环链表和双向链表。本篇博客将深入探讨这些数据结构的概念,并通过示例代码进行详细分析。

什么是线性表?

线性表是一种抽象数据类型,它表示具有相同数据类型的元素的有序集合。线性表中的元素之间存在明确的线性顺序,通常由一个首元素和一个尾元素界定。线性表的常见操作包括插入、删除、查找和遍历。在C/C++中,线性表可以通过数组或链表来实现。

顺序表(Array)

顺序表是一种使用数组来实现的线性表,其中元素在内存中是连续存储的。这使得顺序表具有O(1)时间复杂度的随机访问特性,但在插入和删除操作时可能需要移动大量元素。顺序表的大小通常是固定的,除非重新分配内存。

顺序表的定义

在C/C++中,顺序表可以定义如下:

#include <stdio.h>

#define MAX_SIZE 100  // 顺序表的最大容量

typedef struct {
    int data[MAX_SIZE];  // 数据存储数组
    int length;          // 当前长度
} SeqList;

顺序表的示例

以下是一个简单的C程序,用于初始化、插入和遍历顺序表:

#include <stdio.h>

#define MAX_SIZE 100

typedef struct {
    int data[MAX_SIZE];
    int length;
} SeqList;

// 初始化顺序表
void init(SeqList *list) {
    list->length = 0;
}

// 在指定位置插入元素
int insert(SeqList *list, int position, int element) {
    if (position < 0 || position > list->length || list->length >= MAX_SIZE) {
        return 0;  // 插入失败
    }

    for (int i = list->length; i > position; i--) {
        list->data[i] = list->data[i - 1];
    }

    list->data[position] = element;
    list->length++;
    return 1;  // 插入成功
}

// 遍历顺序表
void traverse(SeqList *list) {
    for (int i = 0; i < list->length; i++) {
        printf("%d ", list->data[i]);
    }
    printf("\n");
}

int main() {
    SeqList list;
    init(&list);
    
    insert(&list, 0, 1);
    insert(&list, 1, 2);
    insert(&list, 2, 3);

    traverse(&list);

    return 0;
}

这段代码演示了如何初始化顺序表、在指定位置插入元素以及遍历顺序表。输出将是 1 2 3

单链表(Singly Linked List)

单链表是一种通过节点之间的指针链接来实现的线性表。每个节点包含数据元素和一个指向下一个节点的指针。单链表不要求内存中的节点是连续的,因此插入和删除操作比顺序表更高效。

单链表的定义

在C/C++中,单链表可以定义如下:

#include <stdio.h>

typedef struct Node {
    int data;           // 数据元素
    struct Node *next;  // 指向下一个节点的指针
} ListNode;

单链表的示例

以下是一个简单的C程序,用于初始化、插入和遍历单链表:

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

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

// 初始化单链表
ListNode *init() {
    return NULL;
}

// 在链表末尾插入元素
ListNode *insert(ListNode *head, int element) {
    ListNode *newNode = (ListNode *)malloc(sizeof(ListNode));
    if (newNode == NULL) {
        perror("Memory allocation failed");
        exit(1);
    }

    newNode->data = element;
    newNode->next = NULL;

    if (head == NULL) {
        return newNode;
    }

    ListNode *current = head;
    while (current->next != NULL) {
        current = current->next;
    }
    current->next = newNode;
    return head;
}

// 遍历单链表
void traverse(ListNode *head) {
    ListNode *current = head;
    while (current != NULL) {
        printf("%d ", current->data);
        current = current->next;
    }
    printf("\n");
}

// 释放单链表的内存
void destroy(ListNode *head) {
    while (head != NULL) {
        ListNode *temp = head;
        head = head->next;
        free(temp);
    }
}

int main() {
    ListNode *head = init();
    
    head = insert(head, 1);
    head = insert(head, 2);
    head = insert(head, 3);

    traverse(head);

    destroy(head);

    return 0;
}

这段代码演示了如何初始化单链表、在链表末尾插入元素以及遍历单链表。输出将是 1 2 3

循环链表(Circular Linked List)

循环链表是一种单链表的变体,其中最后一个节点的指针指向第一个节点,形成一个循环。循环链表可以用于解决环形问题,如约瑟夫问题(Josephus Problem)。

循环链表的定义

循环链表的定义与单链表类似,唯一不同之处在于最后一个节点的指针指向第一个节点。

#include <stdio.h>

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

循环链表的示例

以下是一个简单的C程序,用于初始化、插入和遍历循环链表:

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

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

// 初始化循环链表
CircularListNode *init() {
    return NULL;
}

// 在链表末尾插入元素
CircularListNode *insert(CircularListNode *head, int element) {
    CircularListNode *newNode = (CircularListNode *)malloc(sizeof(CircularListNode));
    if (newNode == NULL) {
        perror("Memory allocation failed");
        exit(1);
    }

    newNode->data = element;
    newNode->next = NULL;

    if (head == NULL) {
        newNode->next = newNode; // 指向自己形成循环
        return newNode;
    }

    CircularListNode *current = head;
    while (current->next != head) {
        current = current->next;
    }
    current->next = newNode;
    newNode->next = head; // 最后一个节点指向头节点形成循环
    return head;
}

// 遍历循环链表
void traverse(CircularListNode *head) {
    if (head == NULL) {
        return;
    }

    CircularListNode *current = head;
    do {
        printf("%d ", current->data);
        current = current->next;
    } while (current != head);
    printf("\n");
}

// 释放循环链表的内存
void destroy(CircularListNode *head) {
    if (head == NULL) {
        return;
    }

    CircularListNode *current = head;
    CircularListNode *temp;
    do {
        temp = current;
        current = current->next;
        free(temp);
    } while (current != head);
}

int main() {
    CircularListNode *head = init();
    
    head = insert(head, 1);
    head = insert(head, 2);
    head = insert(head, 3);

    traverse(head);

    destroy(head);

    return 0;
}

这段代码演示了如何初始化循环链表、在链表末尾插入元素以及遍历循环链表。输出将是 1 2 3,并形成循环。

双向链表(Doubly Linked List)

双向链表是一种链表,每个节点包含两个指针,一个指向前一个节点,一个指向后一个节点。这种结构使得在双向链表中可以轻松地进行双向遍历,但相对于单链表,它需要更多的内存空间来存储额外的指针。

双向链表的定义

在C/C++中,双向链表可以定义如下:

#include <stdio.h>

typedef struct Node {
    int data;
    struct Node *prev;  // 指向前一个节点的指针
    struct Node *next;  // 指向后一个节点的指针
} DoublyListNode;

双向链表的示例

以下是一个简单的C程序,用于初始化、插入和遍历双向链表:

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

typedef struct Node {
    int data;
    struct Node *prev;
    struct Node *next;
} DoublyListNode;

// 初始化双向链表
DoublyListNode *init() {
    return NULL;
}

// 在链表末尾插入元素
DoublyListNode *insert(DoublyListNode *head, int element) {
    DoublyListNode *newNode = (DoublyListNode *)malloc(sizeof(DoublyListNode));
    if (newNode == NULL) {
        perror("Memory allocation failed");
        exit(1);
    }

    newNode->data = element;
    newNode->prev = NULL;
    newNode->next = NULL;

    if (head == NULL) {
        return newNode;
    }

    DoublyListNode *current = head;
    while (current->next != NULL) {
        current = current->next;
    }
    newNode->prev = current;
    current->next = newNode;
    return head;
}

// 遍历双向链表(正向)
void traverseForward(DoublyListNode *head) {
    DoublyListNode *current = head;
    while (current != NULL) {
        printf("%d ", current->data);
        current = current->next;
    }
    printf("\n");
}

// 遍历双向链表(反向)
void traverseBackward(DoublyListNode *head) {
    DoublyListNode *current = head;
    while (current->next != NULL) {
        current = current->next;
    }

    while (current != NULL) {
        printf("%d ", current->data);
        current = current->prev;
    }
    printf("\n");
}

// 释放双向链表的内存
void destroy(DoublyListNode *head) {
    DoublyListNode *current = head;
    DoublyListNode *temp;
    while (current != NULL) {
        temp = current;
        current = current->next;
        free(temp);
    }
}

int main() {
    DoublyListNode *head = init();
    
    head = insert(head, 1);
    head = insert(head, 2);
    head = insert(head, 3);

    traverseForward(head);
    traverseBackward(head);

    destroy(head);

    return 0;
}

这段代码演示了如何初始化双向链表、在链表末尾插入元素以及正向和反向遍历双向链表。输出将是 1 2 33 2 1


了解更多内容获取更多相关资源前往公众号:每日推荐系列


总结

线性表是计算机科学中常见的数据结构,用于存储一系列具有相同数据类型的元素。在C/C++中,我们可以使用不同的数据结构来实现线性表,包括顺序表、单链表、循环链表和双向链表。每种数据结构都有其独特的特点和适用场景,根据具体需求选择合适的数据结构是非常重要的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1144635.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于白鲸优化算法BWO优化的VMD-KELM光伏发电短期功率预测MATLAB代码(含详细算法介绍)

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; VMD适用于处理非线性和非平稳信号&#xff0c;例如振动信号、生物信号、地震信号、图像信号等。它在信号处理、振动分析、图像处理等领域有广泛的应用&#xff0c;特别是在提取信号中的隐含信息和去除噪声方面…

无头浏览器自动化:Puppeteer 帮你释放效能 | 开源日报 No.64

facebook/react Stars: 209.5k License: MIT React是一个用于构建用户界面的JavaScript库。它具有以下优势和特点&#xff1a; 声明式&#xff1a;React使得创建交互式UI变得轻松。您可以为应用程序中的每个状态设计简单视图&#xff0c;当数据发生更改时&#xff0c;React会…

Postman日常操作

一.Postman介绍 1.1第一个简单的demo 路特斯&#xff08;英国汽车品牌&#xff09;_百度百科 (baidu.com) 1.2 cookie 用postman测试需要登录权限的接口时&#xff0c;会被拦截&#xff0c;解决办法就是每次请求接口前&#xff0c;先执行登录&#xff0c;然后记住cookie或者to…

ChineseChess5 2023.10.28

中国象棋残局&#xff1a;黑双卒单车压境解棋

iphone备份后怎么转到新手机,iphone备份在哪里查看

iphone备份会备份哪些东西&#xff1f;iphone可根据需要备份设备数据、应用数据、苹果系统等。根据不同的备份数据&#xff0c;可备份的数据类型不同&#xff0c;有些工具可整机备份&#xff0c;有些工具可单项数据备份。本文会详细讲解苹果手机备份可以备份哪些东西。 一、ip…

婚礼的魅力

昨日有幸被邀请去当伴郎&#xff0c;虽然是替补&#xff0c;即别人鸽了&#xff0c;过去救急&#xff0c;但总归是去起作用。 婚礼的魅力&#xff0c;感受到了&#xff0c;满满的仪式感&#xff0c;紧凑的流程&#xff0c;还有不断的拍照&#xff0c;做视频&#xff0c;留下美好…

接口测试到底怎么做,5分钟时间看完这篇文章彻底搞清楚

01、通用的项目架构 02、什么是接口 接口&#xff1a;服务端程序对外提供的一种统一的访问方式&#xff0c;通常采用HTTP协议&#xff0c;通过不同的url&#xff0c;不同的请求类型&#xff08;GET、POST&#xff09;&#xff0c;不同的参数&#xff0c;来执行不同的业务逻辑。…

2.Vue — 模板语法、数据绑定、el与data的写法、数据代理

文章目录 一、模板语法1.1 插值语法1.2指令语法 二、数据绑定语法2.1 单向数据绑定2.2 双向数据绑定 三、el与data的两种写法3.1 el3.2 data 四、数据代理4.1 Object.defineProperty4.2 Vue数据代理4.2.1 展示数据代理4.2.2 Vue数据代理 一、模板语法 root容器里面的代码被称为…

Ajax学习笔记第二天

喜欢的东西太贵了&#xff0c;我一咬牙&#xff0c;狠下心决定不喜欢了&#xff01; 【一.GET请求】 【1.1 URL即信息】 我们知道php的相关运算都是在服务器端进行的&#xff0c;此时我们要考虑一个问题&#xff0c;如何将要计算的数字带给服务器&#xff1f;我们可以通过UR…

如何能在项目具体编码实现之前能尽可能早的发现问题并解决问题

在项目的具体编码实现之前尽可能早地发现并解决问题&#xff0c;可以大大节省时间和资源&#xff0c;提高项目的成功率。以下是一些策略和方法&#xff1a; 1. 明确需求和预期&#xff1a; 确保所有的项目需求都是清晰和明确的。需求模糊不清是项目失败的常见原因之一。与利益…

java - IDEA IDE - 设置字符串断点

文章目录 java - IDEA IDE - 设置字符串断点概述笔记END java - IDEA IDE - 设置字符串断点 概述 IDE环境为IDEA2022.3 在看一段序列化的代码, 想找出报错抛异常那个点, 理解一下代码实现. 因为序列化代码实现在第三方jar包中, 改不了(只读的). 根本数不清第几次才会开始报…

vue3基础流程

目录 1. 安装和创建项目 2. 项目结构 3. 主要文件解析 3.1 main.js 3.2 App.vue 4. 组件和Props 5. 事件处理 6. 生命周期钩子 7. Vue 3的Composition API 8. 总结和结论 响应式系统&#xff1a; 组件化&#xff1a; 易于学习&#xff1a; 灵活性&#xff1a; 社…

Ubuntu 23.10(Mantic Minotaur)正式发布,支持Linux 6.5和GNOME 45

导读Canonical 近日正式发布了 Ubuntu 23.10&#xff08;Mantic Minotaur&#xff09;操作系统&#xff0c;其中包含一些最新的 GNU/Linux 技术、改进的硬件支持以及许多其他变化。 Ubuntu 23.10 采用了最新的 Linux 6.5 内核系列&#xff0c;并为 Ubuntu 桌面和服务器增强了 z…

代码随想录Day31 贪心06 T738 单调递增的数字 T968监控二叉树

LeetCode T738 单调递增的数字 题目链接:738. 单调递增的数字 - 力扣&#xff08;LeetCode&#xff09; 题目思路: 我们以332举例,题目要我们获得的是小于等于332的最大递增数字,我们知道这个数字要递增只能取299了,332 -- 329 --299 我们从后向前遍历,只要前一位大于后一位,我…

嵌入式-数码管控制

一、数码管显示数字&#xff0c;P2_4, P2_3,P2_2,这三个组合起来代表1-8的二进制表示代表1-8个led。P0代表要显示的数字的16进制表示。 这个图就是表示led灯, 比如要显示数据6&#xff0c;那就是0111 1101&#xff0c;那么16进制就是0x7D,所以p00x7D 二、数码管&#xff0c;动…

C/C++数据结构之深入了解树与二叉树:概念、存储结构和遍历

树是一种常见的数据结构&#xff0c;它在计算机科学和数学中都有广泛的应用。树结构的最简单形式是二叉树&#xff0c;本文将深入探讨树和二叉树的概念、存储结构以及二叉树的遍历&#xff0c;并提供一些实际的代码示例来帮助理解这些概念。 树与二叉树的概念 树 (Tree) 树是…

[双指针] Leetcode 283.移动零和1089.复习零

[双指针] Leetcode 283.移动零和1089.复习零 移动零 283. 移动零 1.题意分析 (1) 给你一个数组&#xff0c;将数组中的所有0移动到数组的末尾 (2) 保证非0元素在数组中相对位置不变 (3) 在原数组中操作 2.解题思路 由于题目要求我们移动数组内容&#xff08;也就是交换两…

【JAVA学习笔记】49 - String类,StringBuffer类,StringBuilder类(重要)

String类 一、String入门 1) String对象用于保存字符串&#xff0c;也就是一组字符序列 2)字符串常量对象是用双引号括起的字符序列。例如: "你好"、 "12.97"、 "boy"等 3)字符串的字符使用Unicode字符编码&#xff0c;一个字符&#xff08;不…

城市群(Megalopolis)/城际(inter-city)OD相关研究即Open Access数据集调研

文章目录 1 城市群/城际OD定义2 理论模型与分析方法2.1 重力模型 Gravity Model2.2 干预机会模型 Intervening Opportunities Model2.3 辐射模型 Radiation Model 3 Issues related to OD flows3.1 OD Prediction3.2 OD Forecasting3.3 OD Construction3.4 OD Estimation 4 OD …

Kubernetes - Ingress HTTP 负载搭建部署解决方案(新版本v1.21+)

在看这一篇之前&#xff0c;如果不了解 Ingress 在 K8s 当中的职责&#xff0c;建议看之前的一篇针对旧版本 Ingress 的部署搭建&#xff0c;在开头会提到它的一些简介Kubernetes - Ingress HTTP 负载搭建部署解决方案_放羊的牧码的博客-CSDN博客 开始表演 1、kubeasz 一键安装…