链表是线性表的一种,是一种物理存储单元上非连续的存储结构,链表中的数据元素之间是通过指针链接实现的。

  链表由一系列节点组成,节点可以在运行时动态的生成。

  链表中国的每个节点分为两部分:一部分是存储数据的数据域,另一部分是存储下一个节点的地址的指针域。

  如果要在链表中查找某个位置的元素,需要从第一个元素开始,循着指针链一个节点一个节点的找,不像顺序表那样可以直接通过下标获取对应的元素,因此,链表不适合查询操作频繁的场景。

  如果要在链表中添加或删除某个元素,只需要通过指针操作,将要操作的节点链入指针链或从指针链中移除即可,不必像顺序表那样需要移动之后的所有节点,因此,链表更适合增删操作频繁的场景。

  使用链表不需要像顺序表那样,处处考虑要不要给表扩容,链表中的元素都是在运行时动态生成的,因此可以充分利用计算机的内存空间;但是,由于链表中的每个元素都需要包括数据域和指针域两块区域,因此空间开销也是比较大的。

  下面是用 C语言 描述的链表的代码:

  链表数据结构的头文件LinkedList.h中的代码:

/**
* 线性表(链式存储)
* 注意:线性表的第一个节点不存储任何数据,只起到表头的作用
*/
#include <stdio.h>
#include <stdlib.h> // 类型定义
typedef int Status; // 方法的返回值
typedef int LinkedElemType; // LinkedList数据结构中节点中存储的数据的类型 // LinkedList中的节点的结构体
typedef struct LinkedNode {
LinkedElemType value;
struct LinkedNode* nextNode;
} LinkedNode; // LinkedList数据结构
typedef struct LinkedList {
LinkedNode* data;
int length;
} LinkedList; // 1.创建带头结点的空链表
void initLinkedList(LinkedList* L) {
L->data = (LinkedNode*)malloc(sizeof(LinkedNode));
if(L->data != NULL) {
L->data->nextNode = NULL;
L->length = ;
printf("创建链表成功!\n");
}
} // 2.销毁链表
void destroyLinkedList(LinkedList* L) {
LinkedNode* node = NULL;
if(L->data == NULL) {
printf("链表不存在!\n");
exit();
}
while(L->data != NULL) {
node = L->data;
L->data = L->data->nextNode;
free(node);
}
printf("销毁链表成功!\n");
} // 3.清空链表(使链表只剩下表头)
void clearLinkedList(LinkedList* L) {
LinkedNode* node = NULL;
if(L->data == NULL) {
printf("链表不存在!\n");
exit();
}
while(L->data->nextNode != NULL) {
node = L->data->nextNode;
L->data->nextNode = node->nextNode;
free(node);
}
L->length = ;
printf("清空链表成功!\n");
} // 4.返回链表的长度
int getLinkedListSize(LinkedList* L) {
return L->length;
} // 5.判断链表中是否存储着数据
Status isLinkedListEmpty(LinkedList* L) {
return L->data->nextNode == NULL;
} // 6.返回链表中第i个数据元素的值
LinkedElemType getLinkedElemAtPos(LinkedList* L, int i) {
LinkedNode* node = NULL;
int index = -;
if(L->data == NULL) {
printf("链表不存在!\n");
exit();
}
if(i < || i > L->length) {
printf("下标无效!\n");
exit();
}
node = L->data;
for(index = ; index <= i; index++) {
node = node->nextNode;
}
return node->value;
} // 7.在链表L中检索值为e的数据元素(第一个元素)
LinkedNode* getLinkedElem(LinkedList* L, LinkedElemType e) {
LinkedNode* node = L->data;
if(L->data == NULL) {
printf("链表不存在!\n");
return NULL;
}
while(node->nextNode != NULL) {
node = node->nextNode;
if(e == node->value) {
return node;
}
}
return NULL;
} // 8.在链表L中第i个数据元素之前插入数据元素e
void insertLinkedElemBefore(LinkedList* L, int i, LinkedElemType e) {
LinkedNode* priorNode = L->data;
LinkedNode* nextNode = NULL;
LinkedNode* newNode = NULL;
int index = -;
if(L->data == NULL) {
printf("链表不存在!\n");
exit();
}
if(i < || i >= L->length) {
printf("下标无效!\n");
exit();
}
newNode = (LinkedNode*)malloc(sizeof(LinkedNode));
newNode->value = e;
for(index = ; index < i; index++) {
priorNode = priorNode->nextNode;
nextNode = priorNode->nextNode;
}
priorNode->nextNode = newNode;
newNode->nextNode = nextNode;
L->length++;
printf("在第%d个位置插入%d成功!\n", i, e);
} // 9.在表尾添加元素e
void insertElemAtEnd(LinkedList* L, LinkedElemType e) {
LinkedNode* currNode = NULL;
LinkedNode* newNode = NULL;
if(L->data == NULL) {
printf("链表不存在!");
exit();
}
currNode = L->data;
while(currNode->nextNode != NULL) {
currNode = currNode->nextNode;
}
newNode = (LinkedNode*)malloc(sizeof(LinkedNode));
newNode->value = e;
newNode->nextNode = NULL;
currNode->nextNode = newNode;
L->length++;
printf("成功在表尾添加元素%d\n", e);
} // 10.删除链表中第i个位置上的元素
void deleteLinkedElemAtPos(LinkedList* L, int i) {
LinkedNode* priorNode = L->data;
LinkedNode* nextNode = NULL;
LinkedNode* oldNode = NULL;
int index = -;
if(L->data == NULL) {
printf("链表不存在!\n");
exit();
}
if(i < || i > L->length) {
printf("下标无效!\n");
exit();
}
for(index = ; index < i; index++) {
priorNode = priorNode->nextNode;
nextNode = priorNode->nextNode;
}
oldNode = nextNode;
priorNode->nextNode = nextNode->nextNode;
L->length--;
printf("成功删除第%d个位置上的元素%d!\n", i, oldNode->value);
free(oldNode);
} // 11.返回给定元素的前驱节点
LinkedNode* getPriorLinkedElem(LinkedList* L, LinkedNode* e) {
LinkedNode* priorNode = NULL;
LinkedNode* currNode = NULL;
if(L->data == NULL) {
printf("链表不存在!");
return NULL;
}
if(e == L->data->nextNode) {
return L->data->nextNode;
}
priorNode = L->data;
currNode = priorNode->nextNode;
while(currNode->nextNode != NULL) {
if(currNode == e) {
return priorNode;
}
priorNode = currNode;
currNode = priorNode->nextNode;
}
return NULL;
} // 12.返回给定元素的后继节点
LinkedNode* getNextLinkedElem(LinkedList* L, LinkedNode* e) {
LinkedNode* currNode = NULL;
if(L->data == NULL) {
printf("链表不存在!\n");
return NULL;
}
currNode = L->data;
while(currNode->nextNode != NULL) {
if(currNode == e) {
return currNode->nextNode;
}
currNode = currNode->nextNode;
}
return NULL;
} // 13.遍历链表
void traverseLinkedList(LinkedList* L) {
LinkedNode* currNode = NULL;
if(L->data == NULL) {
printf("链表不存在!\n");
exit();
}
currNode = L->data->nextNode;
while(currNode != NULL) {
printf("%-4d", currNode->value);
currNode = currNode->nextNode;
}
printf("\n");
} testLinkedList() {
// 声明链表对象
LinkedList list;
// 测试节点
LinkedNode* testNode;
// 初始化链表
initLinkedList(&list);
// 销毁链表
// destroyLinkedList(&list);
// 清空链表
clearLinkedList(&list);
// 获取链表长度
printf("当前链表长度:%d\n", getLinkedListSize(&list));
// 判断链表中是否存储着数据
printf("链表中是否存储着数据:%s\n", isLinkedListEmpty(&list) ? "否" : "是");
// 在表尾添加元素
insertElemAtEnd(&list, );
insertElemAtEnd(&list, );
insertElemAtEnd(&list, );
insertElemAtEnd(&list, );
insertElemAtEnd(&list, );
// 遍历链表中的元素
traverseLinkedList(&list);
// 获取某个位置的元素值
printf("当前链表中第2个元素的值是:%d\n", getLinkedElemAtPos(&list, ));
// 在某元素前插入新元素
insertLinkedElemBefore(&list, , );
insertLinkedElemBefore(&list, , );
// 遍历链表中的元素
traverseLinkedList(&list);
// 删除某位置的元素
deleteLinkedElemAtPos(&list, );
// 遍历链表中的元素
traverseLinkedList(&list);
// 获取对应值的第一个元素
testNode = getLinkedElem(&list, );
// 返回某节点的前驱节点
printf("测试节点的前驱节点的值是:%d\n", getPriorLinkedElem(&list, testNode)->value);
// 返回某节点的后继节点
printf("测试节点的后继节点的值是:%d\n", getNextLinkedElem(&list, testNode)->value);
}

  主函数所在的文件main.c中的代码:

#include <LinkedList.h>

//主函数
int main() {
testLinkedList(); // 线性表(链式存储)结构的测试
return ;
}

  运行结果如下:

创建链表成功!
清空链表成功!
当前链表长度:0
链表中是否存储着数据:否
成功在表尾添加元素1
成功在表尾添加元素2
成功在表尾添加元素4
成功在表尾添加元素5
成功在表尾添加元素6
1 2 4 5 6
当前链表中第2个元素的值是:2
在第3个位置插入3成功!
在第3个位置插入3成功!
1 2 3 3 4 5 6
成功删除第4个位置上的元素3!
1 2 3 4 5 6
测试节点的前驱节点的值是:2
测试节点的后继节点的值是:4 Process returned 0 (0x0) execution time : 0.031 s
Press any key to continue.

  链表分为好几种,上面介绍的这种链表叫做线性链表,它的特点是:线性存储,只能通过前一个节点找到后一个节点,不能通过后一个节点找到前一个节点;

  链表还有其他的几种,下面来简单介绍:

1、 循环链表:

  链表的头尾节点相连,形成一个环。

  实现方式:我们只需要将线性链表稍加改造,将尾节点的下一个节点的指针指向头结点即可。

2、双向链表:

  既可以通过前一个节点找到后一个节点,也可以通过后一个节点找到前一个节点。

  实现方式:在线性链表的节点数据结构体中,不但要定义指向一个节点的指针,也要定义指向前一个节点的指针。

【数据结构】之链表(C语言描述)的更多相关文章

  1. 数据结构与算法分析——C语言描述 第三章的单链表

    数据结构与算法分析--C语言描述 第三章的单链表 很基础的东西.走一遍流程.有人说学编程最简单最笨的方法就是把书上的代码敲一遍.这个我是头文件是照抄的..c源文件自己实现. list.h typede ...

  2. 《数据结构与算法分析——C语言描述》ADT实现(NO.00) : 链表(Linked-List)

    开始学习数据结构,使用的教材是机械工业出版社的<数据结构与算法分析——C语言描述>,计划将书中的ADT用C语言实现一遍,记录于此.下面是第一个最简单的结构——链表. 链表(Linked-L ...

  3. 最小正子序列(序列之和最小,同时满足和值要最小)(数据结构与算法分析——C语言描述第二章习题2.12第二问)

    #include "stdio.h" #include "stdlib.h" #define random(x) (rand()%x) void creat_a ...

  4. C语言学习书籍推荐《数据结构与算法分析:C语言描述(原书第2版)》下载

    维斯 (作者), 冯舜玺 (译者) <数据结构与算法分析:C语言描述(原书第2版)>内容简介:书中详细介绍了当前流行的论题和新的变化,讨论了算法设计技巧,并在研究算法的性能.效率以及对运行 ...

  5. 数据结构与抽象 Java语言描述 第4版 pdf (内含标签)

    数据结构与抽象 Java语言描述 第4版 目录 前言引言组织数据序言设计类P.1封装P.2说明方法P.2.1注释P.2.2前置条件和后置条件P.2.3断言P.3Java接口P.3.1写一个接口P.3. ...

  6. 《数据结构与算法分析-Java语言描述》 分享下载

    书籍信息 书名:<数据结构与算法分析-Java语言描述> 原作名:Data Structures and Algorithm Analysis in Java 作者: 韦斯 (Mark A ...

  7. 使用链表实现队列------《数据结构与算法分析-C语言描述》

    经过ubuntu的gcc验证 一.头文件 que_link.h #ifndef _QUE_LINK_H_ #define _QUE_LINK_H_ struct que_record; typedef ...

  8. 用链表实现栈----《数据结构与算法分析----C语言描述》

    一.头文件: #ifndef _STACK_LINK_H_ #define _STACK_LINK_H_ struct stack_record; typedef struct stack_recor ...

  9. 数据结构之链表C语言实现以及使用场景分析

    牢骚:本篇博客两个星期前已经存为草稿,鉴于发生一些糟糕的事情,今天才基本完成.本人6月份应届毕业生一枚,毕业后当天来到帝都,之后也非常顺利,面试了俩家公司都成功了.一家做C++方面电商ERP,一家做w ...

  10. 《数据结构与算法分析:C语言描述_原书第二版》CH3表、栈和队列_reading notes

    表.栈和队列是最简单和最基本的三种数据结构.基本上,每一个有意义的程序都将明晰地至少使用一种这样的数据结构,比如栈在程序中总是要间接地用到,不管你在程序中是否做了声明. 本章学习重点: 理解抽象数据类 ...

随机推荐

  1. (Java) JWT-TokenUtils

    package com.vcgeek.hephaestus.utils; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; imp ...

  2. Spring Boot 日志处理你还在用Logback?

    ▶ Log4j2 性能 https://logging.apache.org/log4j/2.x/performance.html ▶ Spring Boot 依赖与配置 Maven 依赖 <! ...

  3. 机器学习 AI 谷歌ML Kit 与苹果Core ML

    概述 移动端所说的AI,通常是指"机器学习". 定义:机器学习其实就是研究计算机怎样模拟人类的学习行为,以获取新的知识或技能,并重新组织已有的知识结构使之不断改善自身.从实践的意义 ...

  4. 第二十六章 system v消息队列(二)

    msgsnd int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); 作用: 把一条消息添加到消息队列中 参数: msqi ...

  5. 通过 Django Pagination 实现简单分页

    作者:HelloGitHub-追梦人物 文中所涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 当博客上发布的文章越来越多时,通常需要进行分页显示,以免所有的文章都堆积在一个页面, ...

  6. 史上最全 Java 中各种锁的介绍

    更多精彩原创内容请关注:JavaInterview,欢迎 star,支持鼓励以下作者,万分感谢. 锁的分类介绍 乐观锁与悲观锁 锁的一种宏观分类是乐观锁与悲观锁.乐观锁与悲观锁并不是特定的指哪个锁(J ...

  7. PyCharm安装及使用教程

    1  PyCharm下载 PyCharm的下载安装非常简单,可以直接到Jetbrains公司官网下载,具体步骤如下: (1)打开Pycharm官网http://www.jetbrains.com,选择 ...

  8. 最新开源跳板机(堡垒机)系统 Jumpserver介绍

    Jumpserver 是全球首款完全开源的堡垒机,使用 GNU GPL v2.0 开源协议,是符合 4A 的专业运维审计系统. Jumpserver 使用 Python / Django 进行开发,遵 ...

  9. jquery倒计时代码

    jquery倒计时代码<pre> <span id="day_show">0天</span> <strong id="hour_ ...

  10. 微软的分布式应用框架 Dapr

    微服务架构已成为构建云原生应用程序的标准,微服务架构提供了令人信服的好处,包括可伸缩性,松散的服务耦合和独立部署,但是这种方法的成本很高,需要了解和熟练掌握分布式系统.为了使用所有开发人员能够使用任何 ...