目录
1.链表的引入
之前我们已经学习并且自己尝试实现过了ArrayList这一数据结构,他对数据查找的时间复杂度可以达到O(1),非常高效,但是面对大规模的数据修改,ArrayList修改慢及增容时浪费空间的缺点使得我们需要一种能够适应较多插入和删除场景是数据结构,因此:java 集合中又引入了LinkedList,即链表结构。
2.链表的概念及结构
链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
2.1 单向或者双向
2.2 带头或者不带头
2.3 循环或者非循环
当然在本篇我们将要实现的是 单向不带头非循环 链表,这个作为重点掌握
3.链表的模拟实现
链表的核心操作主要就是插入,当然在讲解插入操作之前我们要先了解链表的组成
通过这张图,我们可以看到,链表由两部分组成,一部分是value,也就是链表内所存储的数据,还有另一部分也就是链表的核心next,实际上存储的就是链表指向的下一个节点的引用,当然链表还有一个头部,也就是链表的第一个元素。
3.1 链表的插入
(1)头插法:
顾名思义是在链表的头部插入数据,这个大家应该比较好理解。
1.首先将待插入数据设置为一个节点
2.让该节点的next指向头节点head
3.头节点更新为原来的待插入节点
4.时间复杂度为O(1)
当然在此之前我们要先实现一个链表类
static class ListNode{
public int val;
public ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;//代表单链表的头结点的引用
头插法代码:
public void addFirst(int data){//头插法
ListNode node=new ListNode(data);//创建节点
node.next=this.head;
this.head=node;//更新头节点
}
(2)尾插法:
有头插法就自然有尾插法,也很好理解就是将数据插入到链表的末尾即可。
1.首先将待插入数据设置为一个节点
2.创建一个临时节点cur,因为尾插操作需要我们找到尾节点,需要我们去遍历一遍链表,而遍历链表要通过头节点来实现,但是头节点又不应该被改变,所以我们需要一个临时节点
3.找到尾节点 while(cur.next!=null) cur=cur.next;
4.将尾节点的next置为待插入节点
5.时间复杂度为O(n)
如果不理解第三点的同学可以看下图,黄色圈圈起来的实际上就是我们要找的
具体操作见如下代码
public void addLast(int data){//尾插法
ListNode node=new ListNode(data);
ListNode cur=this.head;//代替头节点
while(cur.next!=null) cur=cur.next;//找到尾节点
cur.next=node;
}
(3)在任意位置插入
1.首先需要检查插入的位置合法性
2.判断是否能使用头插/尾插
3.将待插入数据设置为一个节点
4.找到待插入位置的前一位
5.node.next=cur.next; cur.next=node;
关于4,5步操作我们看接下来的图
代码实现:
找到待插入位置前一位
/**
* 找到index-1位置的节点
* @param index
* @return 该节点的地址
*/
private ListNode findIndexSubOne(int index){
ListNode cur=this.head;
while(index-1!=0){
cur=cur.next;
index--;
}
return cur;
}
插入操作
public void addIndex(int index,int data){
checkIndexAdd(index);
if(index==0){
addFirst(data);
return;
}
if(index==size()){
addLast(data);
return;
}
ListNode node=new ListNode(data);
ListNode cur=findIndexSubOne(index);
node.next=cur.next;
cur.next=node;
}
3.2 链表的删除
这个我们直接用图来配合讲解
实际上删除链表的本质就是直接让待删除元素的上一个元素的next指向待删除元素的next
cur.next=del.next;//核心代码
完整代码
public void remove(int key){
if(this.head==null){
System.out.println("链表为空");
return;
}
if(this.head.val==key){
this.head=this.head.next;
return;
}
ListNode cur=this.head;
while(cur.next!=null){
if(cur.next.val==key){
ListNode del=cur.next;//找到删除节点
cur.next=del.next;
return;
}
cur=cur.next;
}
}
有关链表的核心功能实现就已经讲完了,全部的完整代码包括异常处理的其他代码我也将上传至gitee:MyList: 模拟实现单链表 - Gitee.com
有兴趣的观众老爷可以来看看