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

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

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

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

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

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

  下面是用 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. mysql如何解除死锁状态

    第一种: 1.查询是否锁表 show OPEN TABLES where In_use > 0; 2.查询进程(如果您有SUPER权限,您可以看到所有线程.否则,您只能看到您自己的线程) sho ...

  2. API 网关知识看这篇就足够了!

    本文已经收录自 JavaGuide (60k+ Star[Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识.) 本文授权转载自:https://github.com/java ...

  3. 基于SkyWalking的分布式跟踪系统 - 异常告警

    通过前面2篇文章我们搭建了SW的基础环境,监控了微服务,能了解所有服务的运行情况.但是当出现服务响应慢,接口耗时严重时我们需要立即定位到问题,这就需要我们今天的主角--监控告警,同时此篇也是SW系列的 ...

  4. python items和setdefault函数

    items() dict = {'runoob': '菜鸟教程', 'google': 'Google 搜索'} print("Value : %s" % dict.setdefa ...

  5. 安装ubuntu16虚拟机,下载android源码,配置编译环境

    Android 源码编译步骤: 我考虑了一下,目前电脑装了SSD,8G内存,使用虚拟机编译源码应该够用. 首先下载虚拟机软件,由于最近一直在使用virtualbox,感觉蛮不错了,下载地址: http ...

  6. python经典算法题:无重复字符的最长子串

    题目:无重复字符的最长子串. 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子 ...

  7. PHP防止客户端多次点击

    PHP防止客户端多次点击 第一种用ip判断 第二种就是用 用户名第三种就是cookie仅限 H5第四种 用swoole 用swoole id

  8. php charles 使用方法

    php charles 使用方法 打开charles 点击help菜单点击local ip address 可以获取本地ip 手机上选择代理这个ip 端口8888 然后手机访问网页 charles会弹 ...

  9. IDEA升级,提示"Connection Error Failed to prepare an update"

    问题来源: 之前修改了IDEA的默认配置文件路径,然后升级新版本时就无法升级,提示"Failed to prepare an update Temp directory inside ins ...

  10. 201871010114-李岩松《面向对象程序设计(java)》第十一周学习总结

    项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...