当前位置:网站首页>LinkedList与链表
LinkedList与链表
2022-07-30 11:27:00 【即将秃头的菜鸟】
目录
一、链表的概念和结构
概念
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
在我看来,链表和火车车厢的结构比较类似,是由一节一节的车厢连接起来的。

注意:
- 这个时候就能看出,链式结构在逻辑上是连续的,但是在物理上不一定是连续的。
- 现实中的节点都是从堆上申请的空间开辟的。
结构
实际中链表的结构主要分以下几个方面:
1、单向或双向

2、带头或不带头

3、循环或非循环

注意:Java集合中LinkedList底层是无头双向循环链表

二、链表的实现
1、无头单向非循环链表
1.1头插法
注意:
单链表中头节点很烦人,不论是插入还是删除均需考虑头节点,因为其没有前驱。
所有的点操作(".")均需注意空指针情况
//头插法
public void addFirst(int data) {
//1.拿到一个实体
ListNode node = new ListNode(data);
//2.插入
//如果是第一次插入,直接找到头节点
if (this.head == null) {
this.head = node;
last = node;
}else {
//不是第一次插入
node.next = head;
head.prev = node;
head = node;
}
}1.2尾插法
//尾插法
public void addLast(int data) {
ListNode node = new ListNode(data);
if (head == null) {
head = node;
last = node;
}else {
ListNode cur = head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = node;
}
}有了头插法和尾插法我们就能调用这两个方法来创建链表,这里我就没吧代码拿出来了。
1.3任意位置插入数据
//检查index下标是否合法
private void checkIndex(int index) {
if(index < 0 || index > size()) {
//throw new IndexOutOfBoundsException("index位置不合法!");
throw new IndexNotLegalException("index位置不合法!");
}
}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data) {
checkIndex(index);
if (index == 0) {
addFirst(data);
return;
}
if (index == size()) {
addLast(data);
return;
}
//不是头插尾插,且index合法
ListNode node = new ListNode(data);
ListNode cur = findIndexSubOfOne(index);
node.next = cur.next;
cur.next = node;
}
private ListNode findIndexSubOfOne(int index) {
ListNode cur = head;
while (index - 1 != 0) {
cur = cur.next;
index--;
}
return cur;
}1.4查找是否存在某个值
就是简单的遍历链表
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key) {
ListNode cur = head;
while (cur != null) {
if(cur.val == key) {
return true;
}
cur = cur.next;
}
return false;
}1.5删除第一次出现的key节点
在删除某个节点的时候,我们以防在删除之后,这个节点后面的节点丢失,我们在删除这个节点的时候要先把这个节点后面的节点记录并链接起来
//删除第一次出现关键字为key的节点
public void remove(int key) {
//情况没处理
if(head.val == key) {
head = head.next;
return;
}
ListNode cur = searchPrevOfKey(key);
if(cur == null)
return;
ListNode del = cur.next;
cur.next = del.next;
}
/**
* 找到关键字key的前驱
* @param key
* @return
*/
private ListNode searchPrevOfKey(int key) {
ListNode cur = this.head;
while (cur.next != null) {
if(cur.next.val == key) {
return cur;
}
cur = cur.next;
}
return null;
}1.6删除所有值为key的节点
//删除所有值为key的节点
public void removeAllKey(int key) {
if(head == null) return;
ListNode prev = head;
ListNode cur = head.next;
while (cur != null) {
if(cur.val == key) {
prev.next = cur.next;
cur = cur.next;
}else {
prev = cur;
cur = cur.next;
}
}
if(head.val == key) {
head = head.next;
}
}1.7得到单链表的长度
//得到单链表的长度
public int size() {
ListNode cur = head;
int count = 0;
while (cur != null) {
count++;
cur = cur.next;
}
return count;
}1.8打印单链表的值
//打印
public void display() {
ListNode cur = head;
while (cur != null) {
System.out.print(cur.val+" ");
cur = cur.next;
}
System.out.println();
}1.9清空链表
//清空
public void clear() {
ListNode cur = head;
while (cur != null) {
ListNode curNext = cur.next;
cur.next = null;
cur = curNext;
}
head = null;
}三、LinkedList的模拟实现
无头双向链表
首先定义这个类哦

3.1头插法
//头插法
public void addFirst(int data){
ListNode node = new ListNode(data);
if (head == null) {
head = node;
last = node;
}else {
node.next = head;
head.pre = node;
head = node;
}
}3.2尾插法
//尾插法
public void addLast(int data) {
ListNode node = new ListNode(data);
if (head == null) {
head = node;
last = node;
}else {
last.next = node;
node.pre = last;
last = last.next;
}
}3.3求链表长度
//计算链表长度
public int size() {
int count = 0;
ListNode cur = head;
while (cur != null) {
count++;
cur = cur.next;
}
return count;
}
3.4任意位置插入
//检查index是否合法
private boolean checkIndex(int index) {
if (index < 0 || index > size()) {
throw new IndexOutOfBoundsException("插入位置不合法!");
}else {
return true;
}
}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data) {
if(checkIndex(index) == true) {
if (index == 0) {
addFirst(index);
}
if (index == size()) {
addLast(index);
}
ListNode node = new ListNode(data);
//找到当前index节点的地址
ListNode cur = findIndex(index);
node.next = cur;
cur.pre.next = node;
node.pre = cur.pre;
cur.pre = node;
}
}
//找到当前index节点的地址
private ListNode findIndex(int index) {
ListNode cur = head;
while (index != 0) {
cur = cur.next;
index--;
}
return cur;
}3.5检查链表是否包含某个值
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key) {
ListNode cur = head;
while (cur != null) {
if(cur.val == key) {
return true;
}
cur = cur.next;
}
return true;
}3.6删除第一次出现关键字的节点
//删除第一次出现关键字为key的节点
public void remove(int key) {
ListNode cur = head;
while (cur != null) {
if(cur.val == key) {
//处理头部问题
if(cur == head) {
head = head.next;
if(head != null) {
//防止只有一个节点
head.pre = null;
}
}else {
cur.pre.next = cur.next;
if(cur.next != null) {
//删除的不是尾巴节点
cur.next.pre = cur.pre;
}else {
//删除的是尾巴节点
last = cur.pre;
}
}
return;
}
cur = cur.next;
}
/*while (cur != null) {
if (cur.val == key) {
cur.pre.next = cur.next;
cur.pre = cur.next.pre;
}else {
cur = cur.next;
}
}*/
}3.7删除所有出现关键字的节点
//删除所有值为key的节点
public void removeAllKey(int key) {
ListNode cur = head;
while (cur != null) {
if(cur.val == key) {
//处理头部问题
if(cur == head) {
head = head.next;
if(head != null) {
//防止只有一个节点
head.pre = null;
}
}else {
cur.pre.next = cur.next;
if(cur.next != null) {
//删除的不是尾巴节点
cur.next.pre = cur.pre;
}else {
//删除的是尾巴节点
last = cur.pre;
}
}
}
cur = cur.next;
}
}3.8打印链表
//打印链表
public void display() {
ListNode cur = head;
while (cur != null) {
System.out.print(cur.val + " ");
cur = cur.next;
}
System.out.println();
}3.9清空链表
//清空链表
public void clear() {
this.head = null;
}四、LinkedList的使用
简单介绍
LinkedList的底层是双向链表结构,由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后是通过引用将节点连接起来的,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。
下面是LinkedList的部分源码

在集合框架中,LinkedList也实现了List接口

说明:
- LinkedList实现了List接口。
- LinkedList的底层使用了双向链表。
- LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问。
- LinkedList的任意位置插入和删除元素时效率比较高,时间复杂度为O(1)。
LinkedList的构造
| 方法 | 解释 |
| LinkedList() | 无参构造 |
| public LinkedList(Collection<? extends E> c) | 使用其他集合容器中元素构造List |
LinkedList的一些常用方法
| 方法 | 解释 |
| boolean add(E e) | 尾插e |
| void add(int index, E element) | 将 e 插入到 index 位置 |
| boolean addAll(Collection<? extends E> c) | 尾插 c 中的元素 |
| E remove(int index) | 删除 index 位置元素 |
| boolean remove(Object o) | 删除遇到的第一个 o |
| E get(int index) | 获取下标 index 位置元素 |
| E set(int index, E element) | 将下标 index 位置元素设置为 element |
| void clear() | 清空 |
| boolean contains(Object o) | 判断 o 是否在线性表中 |
| int indexOf(Object o) | 返回第一个 o 所在下标 |
| int lastIndexOf(Object o) | 返回最后一个 o 的下标 |
| List<E> subList(int fromIndex, int toIndex) | 截取部分 list |
注意事项
1、下图是add方法的源码,可以看到源码采用的是尾插

五、ArrayList和LinkedList的区别
| 不同点 | ArrayList | LinkedList |
| 存储空间上 | 物理上一定连续 | 逻辑上连续,物理上不一定连续 |
| 随机访问 | 支持 O(1) | 不支持 O(n) |
| 头插 | 需要搬移元素,效率低O(n) | 只需修改引用的指向,时间复杂幅度O(1) |
| 插入 | 空间不够时需要扩容 | 没用容量的概念 |
| 应用 | 需要频繁的使用时+元素高效存储 | 任意位置的插入和删除频繁 |
边栏推荐
- 周鸿祎:微软抄袭了360安全模式 所以成为美国最大的安全公司
- 基于.NetCore开发博客项目 StarBlog - (16) 一些新功能 (监控/统计/配置/初始化)
- Verilog语法基础HDL Bits训练 08
- 京东校招笔试题+知识点总结
- 【云筑共创】华为云携手鸿蒙,端云协同,培养创新型开发者
- RY-D1/1 Voltage Relay
- Hu-cang integrated e-commerce project (1): project background and structure introduction
- The battle-hardened programmer was also deceived by a fake programmer from a certain fish. The trust between programmers should be the highest, and he alone destroyed this sense of trust
- 时间序列曲线相似性
- 原生js 创建表格
猜你喜欢
随机推荐
ODrive应用 #4 配置参数&指令「建议收藏」
Jingdong school recruited written test questions + summary of knowledge points
模糊离散事件系统的可测性
汇编实现冒泡排序
Verilog语法基础HDL Bits训练 08
EA中的业务对象和业务实体你分得清吗?
Typroa 替代工具marktext
【数据库基础】redis使用总结
Program environment and preprocessing (detailed)
高能产出!腾讯内部的MyCat中间件手册,理论实操齐下
数据库性能系列之索引(上)
Flexible distribution parameters of mechanical system modeling and control of research and development
牛客-TOP101-BM42
LCD1602 display experiment developed by single chip microcomputer
Vim plugin GrepIt
[Cloud-Building Co-creation] Huawei Cloud and Hongmeng collaborate to cultivate innovative developers
Manage reading notes upward
English line break
Microsoft SQL server hacked, bandwidth stolen
Introduction to IoT Technologies: Chapter 6








