# 链表
> 原文: [https://javabeginnerstutorial.com/data-structure/linked-list/](https://javabeginnerstutorial.com/data-structure/linked-list/)
## 什么是链表?
链表是另一种数据结构,也是一种常见的数据结构,它包括按顺序分为两组的一组节点,每个节点由数据和下一个节点的地址部分组成,并形成一个链。 它用于创建树和图。
![](https://img.kancloud.cn/99/83/998343c0da0882f32c65a56d23ac18e5_556x218.png)
**优点**
* 本质上是动态的,并在需要时分配内存。
* 有两个可以在链表中轻松实现的操作,即插入和删除。
* 减少访问时间。
**缺点 **
* 内存浪费了,因为指针需要额外的存储空间。
* 该元素不能随机访问,可以顺序访问。
* 在链表中,反向遍历很困难。
## 在哪里使用链表?
1. 它们用于实现栈,队列,图形等。
2. 它们使您可以在列表的开头和结尾插入元素。
3. 在此不需要事先知道大小。
## 链表的类型
### 单链表
这种类型的列表包含具有数据部分和地址部分的节点,即`next`,它指向给定节点序列中的下一个节点。 我们可以对单链列表执行的操作包括插入,删除和遍历。
![](https://img.kancloud.cn/6f/55/6f559e564146b8803ea18f845d5c3973_562x139.png)
### 双链表
在这种类型的列表中,每个节点包含两个链接,第一个链接将指向序列中的上一个节点,下一个链接将指向序列中的下一个节点。
![](https://img.kancloud.cn/7e/60/7e60a0fe72e925c7bae7f256119d3b25_557x113.png)
循环链表 - 在这种类型的列表中,列表的最后一个节点包含第一个节点的地址,并将形成一个循环链。
![](https://img.kancloud.cn/0e/4a/0e4ae975435810ca79477471ecfe056a_596x78.png)
## 单链表
单链表是其中每个节点仅包含一个指向下一个节点的链接字段的列表。 在这种情况下,节点分为两部分,第一部分是数据部分,另一部分是包含下一个节点地址的链接部分。 第一个节点是标头节点,其中包含下一个节点的数据和地址,依此类推。 单链列表也称为单向列表,因为它只能从左到右遍历,而另一种方式则是不可能的。
![](https://img.kancloud.cn/6f/55/6f559e564146b8803ea18f845d5c3973_562x139.png)
### 在哪里使用单链表?
单链列表可以在使用后进先出概念的栈中使用。 此列表维护与列表中头节点的链接,每个节点都指向列表中的下一个节点。 它也可以用于先入先出的队列中。
**在 C 中实现单链表**
```c
#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
int data;
struct Node *next;
}node;
void insert(node *ptr, int data)
{
while(ptr->next!=NULL)
{
ptr = ptr -> next;
}
ptr->next = (node *)malloc(sizeof(node));
ptr = ptr->next;
ptr->data = data;
ptr->next = NULL;
}
int find(node *ptr, int key)
{
ptr = ptr -> next;
while(ptr!=NULL)
{
if(ptr->data == key)
{
return 1;
}
ptr = ptr -> next;
}
return 0;
}
void delete(node *ptr, int data)
{
while(ptr->next!=NULL && (ptr->next)->data != data)
{
ptr = ptr -> next;
}
if(ptr->next==NULL)
{
printf("Element %d is not present in the list\n",data);
return;
}
node *temp;
temp = ptr -> next;
ptr->next = temp->next;
free(temp);
return;
}
void print(node *ptr)
{
if(ptr==NULL)
{
return;
}
printf("%d ",ptr->data);
print(ptr->next);
}
int main()
{
node *start,*temp;
start = (node *)malloc(sizeof(node));
temp = start;
temp -> next = NULL;
printf("1. Insert\n");
printf("2. Delete\n");
printf("3. Print\n");
printf("4. Find\n");
while(1)
{
int option;
scanf("%d",&option);
if(option==1)
{
int data;
scanf("%d",&data);
insert(start,data);
}
else if(option==2)
{
int data;
scanf("%d",&data);
delete(start,data);
}
else if(option==3)
{
printf("The list is ");
print(start->next);
printf("\n");
}
else if(option==4)
{
int data;
scanf("%d",&data);
int result = find(start,data);
if(result)
{
printf("The element is found \n");
}
else
{
printf("The element is not found\n");
}
}
}
}
```
## 双链表
双链列表也称为双向列表或双向链。 在双向链表中,两个链接字段被保留,而不是像在单个链表中那样保留一个链接字段。 双链表是一个线性数据结构,其中每个节点都有两个链接,其中第一个链接用于指向前一个节点,下一个链接指向下一个节点。 在双向链表上执行的操作是插入,删除,搜索和遍历。
![](https://img.kancloud.cn/7e/60/7e60a0fe72e925c7bae7f256119d3b25_557x113.png)
### 在哪里使用双链表?
1. 用于表示游戏中的纸牌。
2. 在具有“最近使用”列表的应用中使用。
3. 在 Word 或 Photoshop 中用作撤消功能。
4. 在浏览器缓存中使用,它使我们可以单击后退按钮。
**使用 C 实现双向链表**
```c
#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
int data;
struct Node *next;
struct Node *prev;
}node;
void insert(node *ptr, int data)
{
while(ptr->next!=NULL)
{
ptr = ptr -> next;
}
ptr->next = (node *)malloc(sizeof(node));
(ptr->next)->prev = ptr;
ptr = ptr->next;
ptr->data = data;
ptr->next = NULL;
}
int find(node *ptr, int key)
{
ptr = ptr -> next;
while (ptr!=NULL)
{
if(ptr->data == key)
{
return 1;
}
ptr = ptr -> next;
}
return 0;
}
void delete(node *ptr, int data)
{
while(ptr->next!=NULL && (ptr->next)->data != data)
{
ptr = ptr -> next;
}
if(ptr->next==NULL)
{
printf("The element %d is not present in the list\n",data);
return;
}
node *temp;
temp = ptr -> next;
ptr->next = temp->next;
temp->prev = ptr;
free(temp);
return;
}
void print(node *ptr)
{
if(ptr==NULL)
{
return;
}
printf("%d ",ptr->data);
print(ptr->next);
}
int main()
{
node *start,*temp;
start = (node *)malloc(sizeof(node));
temp = start;
temp -> next = NULL;
temp -> prev = NULL;
printf("1. Insert\n");
printf("2. Delete\n");
printf("3. Print\n");
printf("4. Find\n");
while(1)
{
int option;
scanf("%d",&option);
if(option==1)
{
int data;
scanf("%d",&data);
insert(start,data);
}
else if(option==2)
{
int data;
scanf("%d",&data);
delete(start,data);
}
else if(option==3)
{
printf("The list is ");
print(start->next);
printf("\n");
}
else if(option==4)
{
int data;
scanf("%d",&data);
int result = find(start,data);
if(result)
{
printf("The element is found\n");
}
else
{
printf("The element is not found\n");
}
}
}
}
```
## 循环链表
循环链表是有点复杂的链接数据结构。 在此列表中,我们可以在列表中的任何位置插入元素,而在数组中,我们不能在列表中的任何位置插入元素,因为它在连续内存中。 在此列表中,上一个元素存储下一个元素的地址,最后一个元素存储第一个元素的地址。 列表中的元素以圆形的方式相互指向,形成圆形的链。 该列表具有动态大小,这意味着可以在需要时分配内存。
![](https://img.kancloud.cn/0e/4a/0e4ae975435810ca79477471ecfe056a_596x78.png)
### 在哪里使用循环链表?
使用此列表的实际应用是在其上运行多个应用的PC。 循环链表在操作系统中很常见,因为它会将正在运行的应用放在列表中,并且当列表即将到达其末端时,操作系统很容易使用循环链表,因为操作系统可以循环运行到列表的最前面。 将该时隙分配给列表中的每个应用。
**在 C 中实现循环链表**
```c
#include<stdio.h>
#include<stdlib.h>
struct node
{
int data;
struct node *next;
}node;
void insert(node *pointer, int data)
{
node *start = pointer;
while(pointer->next!=start)
{
pointer = pointer -> next;
}
pointer->next = (node *)malloc(sizeof(node));
pointer = pointer->next;
pointer->data = data;
pointer->next = start;
}
void delete(node *pointer, int data)
{
node *start = pointer;
while(pointer->next!=start && (pointer->next)->data != data)
{
pointer = pointer -> next;
}
if(pointer->next==start)
{
printf("Element %d is not present in the list\n",data);
return;
}
node *temp;
temp = pointer -> next;
pointer->next = temp->next;
free(temp);
free( )
return;
}
void display(node *start,node *pointer)
{
if(pointer = = start)
{
return;
}
printf("%d ",pointer->data);
display(start,pointer->next);
}
void main()
{
node *start,*temp;
start = (struct node *)malloc(sizeof(struct node));
temp = start;
temp -> next = start;
printf("1. Insert\n");
printf("2. Delete\n");
printf("3. Display\n");
while(1)
{
int query;
scanf("%d",&query);
if(query==1)
{
int data;
scanf("%d",&data);
insert(start,data);
}
else if(query==2)
{ int data;
scanf("%d",&data);
delete(start,data);
}
else if(query==3)
{ printf("The list is ");
display(start,start->next);
printf("\n");
} }
getch( );
}
```
## 线性链表
在链表中,我们可以通过三种方式插入元素:
* 插入列表的开头。
* 插入列表的中间。
* 插入到列表的末尾。
在将节点插入列表之前,我们将使用关键字`struct`创建一个结构。 例如:
```c
struct node
{ int data;
struct node *next;
};
struct employee *start=NULL, *temp,*q;
```
并且在定义了结构之后,在从列表中插入或删除节点的同时,给出了特定的功能定义或功能原型。 例如:
```c
void insertbeg( );
void insertmiddle( );
void insertlast( );
void deletebeg( );
```
上面给出的函数定义在`main`函数之前定义,即`void main()`或`int main()`。
**在开头的插入元素**
在开始时插入节点的步骤:
1. 创建一个新节点。
2. 在数据部分输入数据。
3. 将地址部分或下一部分设为`NULL`。
4. 现在将这个新创建的节点附加到起始或头部。
5. 现在,将此起始节点设为起始节点或标头节点。
**在中间的插入元素**
在中间插入节点的步骤:
1. 将要添加的新节点的数据写入列表及其位置。
2. 通过调用`malloc()`创建一个新的空节点。
3. 将数据插入新节点的数据部分。
4. 将此新节点添加到列表中的所需位置。
5. 转到步骤 1,直到您在列表中添加了所有值。
**实现**
```c
void insertmiddle()
{ int pos,i,num;
if(start==NULL)
{ printf("\nList is empty!! Sorry...");
}
temp=(struct node*)malloc(sizeof(struct node));
printf("\nEnter the details:");
scanf("%d",num);
printf("\nEnter position:");
scanf("%d",&pos);
temp->data=num;
q=start
for(i=1;i<pos-1;pos++)
{
if(q->next==NULL)
{ printf("\nLess elements in the list");
}
q=q->next;
}
temp->next=q->next;
q->next=temp;
getch();
}
```
**在列表的末尾插入元素:**
最后插入节点的步骤:
1. 创建新节点。
2. 将数据输入到节点的数据部分。
3. 将节点的下一部分设为`NULL`。
4. 要在最后一个位置插入节点,因此我们必须遍历到最后一个节点。
5. 在最后一个节点和新节点之间建立链接。
**实现**
```c
void insertlast()
{ int num;
temp=(struct node*)malloc(sizeof(struct node));
printf("\nEnter the details:");
scanf("%d",&num);
temp->data=num;
temp->next=NULL;
if(start==NULL) //If list is empty
{
start=temp;
}
else
{
q=start;
while(q->next!=NULL)
q=q->next;
q->next=temp;
}
}
```
**删除**
可以通过三种方式删除该元素:
* 从列表的开头删除。
* 从列表的中间删除。
* 从列表末尾删除。
**从开头删除元素**
```c
Implementation
void deletebeg()
{ if(start==NULL)
{ printf("\nThe list is empty...");
}
else
{
q=start;
start=start->next;
free(q);
printf("\nElement deleted...");
}
}
```
**从中间删除元素**
实现
```c
void deletemiddle()
{ int pos,i;
if(start==NULL)
{ printf("\nThe list is empty...");
}
printf("\nEnter position to delete:");
scanf("%d",&pos);
for(i=1;i<pos-1;pos++)
{
if(q->next==NULL)
{
printf("\nLess elements...");
getch();
}
q=q->next;
}
temp=q->next;
q->next=temp->next;
free(temp);
printf("\nElement deleted...");
getch();
}
```
**从末尾删除元素**
实现
```c
void deletelast()
{ if(start==NULL)
{
printf("\nThe list is empty...");
}
else
{
q=start;
while(q->next->next!=NULL)
q=q->next;
temp=q->next;
q->next=NULL;
free(temp);
printf("\nElement deleted...");
}
}
```
**显示**
在执行任何操作后显示列表的元素。
```c
void display()
{ struct node *q;
q=start;
while(q!=NULL)
{
printf("%d\t",q->data);
q=q->next;
}
getch();
}
```
- JavaBeginnersTutorial 中文系列教程
- Java 教程
- Java 教程 – 入门
- Java 的历史
- Java 基础知识:Java 入门
- jdk vs jre vs jvm
- public static void main(string args[])说明
- 面向初学者的 Java 类和对象教程
- Java 构造器
- 使用 Eclipse 编写 Hello World 程序
- 执行顺序
- Java 中的访问修饰符
- Java 中的非访问修饰符
- Java 中的数据类型
- Java 中的算术运算符
- Java 语句初学者教程
- 用 Java 创建对象的不同方法
- 内部类
- 字符串构建器
- Java 字符串教程
- Java 教程 – 变量
- Java 中的变量
- Java 中的局部变量
- Java 中的实例变量
- Java 引用变量
- 变量遮盖
- Java 教程 – 循环
- Java for循环
- Java 教程 – 异常
- Java 异常教程
- 异常处理 – try-with-resources语句
- Java 异常处理 – try catch块
- Java 教程 – OOPS 概念
- Java 重载
- Java 方法覆盖
- Java 接口
- 继承
- Java 教程 – 关键字
- Java 中的this关键字
- Java static关键字
- Java 教程 – 集合
- Java 数组教程
- Java 集合
- Java 集合迭代器
- Java Hashmap教程
- 链表
- Java 初学者List集合教程
- Java 初学者的Map集合教程
- Java 初学者的Set教程
- Java 初学者的SortedSet集合教程
- Java 初学者SortedMap集合教程
- Java 教程 – 序列化
- Java 序列化概念和示例
- Java 序列化概念和示例第二部分
- Java 瞬态与静态变量
- serialVersionUID的用途是什么
- Java 教程 – 枚举
- Java 枚举(enum)
- Java 枚举示例
- 核心 Java 教程 – 线程
- Java 线程教程
- Java 8 功能
- Java Lambda:初学者指南
- Lambda 表达式简介
- Java 8 Lambda 列表foreach
- Java 8 Lambda 映射foreach
- Java 9
- Java 9 功能
- Java 10
- Java 10 独特功能
- 核心 Java 教程 – 高级主题
- Java 虚拟机基础
- Java 类加载器
- Java 开发人员必须知道..
- Selenium 教程
- 1 什么是 Selenium?
- 2 为什么要进行自动化测试?
- 3 Selenium 的历史
- 4 Selenium 工具套件
- 5 Selenium 工具支持的浏览器和平台
- 6 Selenium 工具:争霸
- 7A Selenium IDE – 简介,优点和局限性
- 7B Selenium IDE – Selenium IDE 和 Firebug 安装
- 7C Selenium IDE – 突破表面:初探
- 7D Selenium IDE – 了解您的 IDE 功能
- 7E Selenium IDE – 了解您的 IDE 功能(续)。
- 7F Selenium IDE – 命令,目标和值
- 7G Selenium IDE – 记录和运行测试用例
- 7H Selenium IDE – Selenium 命令一览
- 7I Selenium IDE – 设置超时,断点,起点
- 7J Selenium IDE – 调试
- 7K Selenium IDE – 定位元素(按 ID,名称,链接文本)
- 7L Selenium IDE – 定位元素(续)
- 7M Selenium IDE – 断言和验证
- 7N Selenium IDE – 利用 Firebug 的优势
- 7O Selenium IDE – 以所需的语言导出测试用例
- 7P Selenium IDE – 其他功能
- 7Q Selenium IDE – 快速浏览插件
- 7Q Selenium IDE – 暂停和反射
- 8 给新手的惊喜
- 9A WebDriver – 架构及其工作方式
- 9B WebDriver – 在 Eclipse 中设置
- 9C WebDriver – 启动 Firefox 的第一个测试脚本
- 9D WebDriver – 执行测试
- 9E WebDriver – 用于启动其他浏览器的代码示例
- 9F WebDriver – JUnit 环境设置
- 9G WebDriver – 在 JUnit4 中运行 WebDriver 测试
- 9H WebDriver – 隐式等待
- 9I WebDriver – 显式等待
- 9J WebDriver – 定位元素:第 1 部分(按 ID,名称,标签名称)
- 9K WebDriver – 定位元素:第 2 部分(按className,linkText,partialLinkText)
- 9L WebDriver – 定位元素:第 3a 部分(按cssSelector定位)
- 9M WebDriver – 定位元素:第 3b 部分(cssSelector续)
- 9N WebDriver – 定位元素:第 4a 部分(通过 xpath)
- 9O WebDriver – 定位元素:第 4b 部分(XPath 续)
- 9P WebDriver – 节省时间的捷径:定位器验证
- 9Q WebDriver – 处理验证码
- 9R WebDriver – 断言和验证
- 9S WebDriver – 处理文本框和图像
- 9T WebDriver – 处理单选按钮和复选框
- 9U WebDriver – 通过两种方式选择项目(下拉菜单和多项选择)
- 9V WebDriver – 以两种方式处理表
- 9W WebDriver – 遍历表元素
- 9X WebDriver – 处理警报/弹出框
- 9Y WebDriver – 处理多个窗口
- 9Z WebDriver – 最大化窗口
- 9AA WebDriver – 执行 JavaScript 代码
- 9AB WebDriver – 使用动作类
- 9AC WebDriver – 无法轻松定位元素? 继续阅读...
- 10A 高级 WebDriver – 使用 Apache ANT
- 10B 高级 WebDriver – 生成 JUnit 报告
- 10C 高级 WebDriver – JUnit 报表自定义
- 10D 高级 WebDriver – JUnit 报告自定义续
- 10E 高级 WebDriver – 生成 PDF 报告
- 10F 高级 WebDriver – 截屏
- 10G 高级 WebDriver – 将屏幕截图保存到 Word 文档
- 10H 高级 WebDriver – 发送带有附件的电子邮件
- 10I 高级 WebDriver – 使用属性文件
- 10J 高级 WebDriver – 使用 POI 从 excel 读取数据
- 10K 高级 WebDriver – 使用 Log4j 第 1 部分
- 10L 高级 WebDriver – 使用 Log4j 第 2 部分
- 10M 高级 WebDriver – 以无头模式运行测试
- Vue 教程
- 1 使用 Vue.js 的 Hello World
- 2 模板语法和反应式的初探
- 3 Vue 指令简介
- 4 Vue Devtools 设置
- 5 数据绑定第 1 部分(文本,原始 HTML,JavaScript 表达式)
- 6 数据绑定第 2 部分(属性)
- 7 条件渲染第 1 部分(v-if,v-else,v-else-if)
- 8 条件渲染第 2 部分(v-if和v-show)
- 9 渲染列表第 1 部分(遍历数组)
- 10 渲染列表第 2 部分(遍历对象)
- 11 监听 DOM 事件和事件修饰符
- 12 监听键盘和鼠标事件
- 13 让我们使用简写
- 14 使用v-model进行双向数据绑定
- 15 表单输入绑定
- 18 类绑定
- Python 教程
- Python 3 简介
- Python 基础知识 - 又称 Hello World 以及如何实现
- 如何在 Windows 中安装 python
- 适用于 Windows,Mac,Linux 的 Python 设置
- Python 数字和字符串
- Python 列表
- Python 集
- Python 字典
- Python 条件语句
- Python 循环
- Python 函数
- 面向对象编程(OOP)
- Python 中的面向对象编程
- Python 3 中的异常处理
- Python 3:猜数字
- Python 3:猜数字 – 回顾
- Python 生成器
- Hibernate 教程
- Hibernate 框架基础
- Hibernate 4 入门教程
- Hibernate 4 注解配置
- Hibernate 4 的实体关系
- Hibernate 4 中的实体继承模型
- Hibernate 4 查询语言
- Hibernate 4 数据库配置
- Hibernate 4 批处理
- Hibernate 4 缓存
- Hibernate 4 审计
- Hibernate 4 的并发控制
- Hibernate 4 的多租户
- Hibernate 4 连接池
- Hibernate 自举