👀作者简介:大家好,我是大杉。
🚩🚩 个人主页:爱编程的大杉
支持我:点赞+关注~不迷路🧡🧡🧡
✔系列专栏:javase基础⚡⚡⚡
(❁´◡`❁)励志格言:问题是怎样让工业的车轮继续转动,而又不增加世界上的财富。必须生产出货物来,却又必须不去将之分配。实践中,只能通过不断的战争才能达到这一目标。(from 《1984(纪念版)》)🤞🤞
文章目录
一.单链表的构成及分类
1.构成
🔥🌀单链表由一个一个节点串联而成,每一个节点又由两部分组成,分为数字区域,和引用区域(存储下个节点的引用)
2.分类
分为带头节点的单链表和不带头节点的单链表
🌎带头节点的单链表
带头节点的单链表就是单独有一个节点作为整个单链表头,此时这个头的指向固定,永远为头部,不会被改变和删除
🌎不带头节点的单链表
不带头节点的单链表中的头只是表示第一个节点,可以删除,删除后head指向下一个节点
3.代码搭建
明确一点:每一个节点都是一个对象
class Node
//自定义Node类型
{
public int date;
public Node next;
public Node(int date)
{
this.date=date;
this.next=null;
}
}
二、头插法
🌎头插法顾名思义就是在单链表头节点的前面插入节点。
头插法时应该注意:是否是第一次插入。如果是第一次插入,此时单链表并没有节点元素。此时的head指向为空,只需将head指向插入的目标节点即可。
🌎一定注意要先将node.next=this.head;因为如果先将head指向指向node则此时的头节点的位置已经发生改变,就会造成自己指向自己的情况,且会出现空指针异常
🌎代码实现
public void addFirst(int data)
{
Node node=new Node(data);
if(this.head==null)
{
this.head=node;
return;
//void中的return只是使方法结束调用
}
node.next = this.head;
this.head = node;
}
三、尾插法
🌎尾插法就是在单链表末尾节点的后面插入节点
public void addLast(int data)
{
Node node=new Node(data);
Node cur=this.head;
//没有节点时直接将head指向node
if (this.head==null)
{
this.head=node;
return;
}
//找到最后一个节点
while (cur.next!=null)
{
cur=cur.next;
}
//最后一个节点的next域指向所要插入节点的引用
cur.next=node;
}
四.任意位置插入节点(遍历一遍)
🌎在任意位置要考虑是在头插入还是在尾插入,还是在中间部分插入。如果是在头或尾插入,直接调用上面的方法即可。如果是在中间插入,则需先找到插入位置的前一个节点,将前一个节点的next赋值给插入节点的next,再将前一个节点的next指向插入节点的引用
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data)
{
Node node=new Node(data);
//头插
if(index==0)
{
this.addFirst(data);
return;
}
//尾插(size为求单链表长度的方法,下面会提到)
if (index==this.size())
{
this.addLast(data);
return;
}
//获得前一个节点的地址
Node cur=searchIndex(index);
//查找节点
node.next=cur.next;
cur.next=node;
}
private Node searchIndex(int index)
{
if(index<0||index>this.size())
{
throw new RuntimeException("index不合法");
}
Node cur=this.head;
//在index位置插入,走index-1次可以走到它前一个节点
while (index-1!=0)
//从head开始运行index-1次找到index-1位置的引用
{
cur=cur.next;
index--;
}
return cur;
}
五.获得链表长度
public int size()
{
int length=0;
Node cur=this.head;
while (cur!=null)
{
length++;
cur=cur.next;
}
return length;
}
六.查找关键字key是否在单链表当中
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key)
{
Node cur=this.head;
while (cur!=null)
{
if (cur.date==key)
{
return true;
}
cur = cur.next;
}
return false;
}
七.删除第一次出现关键字为key的节点
🌎要先找到所要删除节点的前一个节点,将前一个节点的next赋值为要删除节点的next,使它跳过要删除的节点直接指向下一个节点
public void remove(int key)
{
//head==null还没有添加节点,所以没有节点可以删除直接结束方法
if (this.head==null)
{
return;
}
//第一个节点恰好是要删除的节点,直接让头节点后移指向下一个节点
if(this.head.date==key)
{
this.head=this.head.next;
return;
}
Node prev=getPrev(key);
if (prev==null)
{
System.out.println("找不到此节点");
return;
}
Node del=prev.next;
prev.next=del.next;
}
private Node getPrev(int key)
{
Node prev=this.head;
//prev.next==null的时候已经走到最后一个节点了,他后面没有节点了,
//不可能是某个节点的前一个节点,无需再进入循环
while (prev.next!=null)
{
if (prev.next.date==key)
{
return prev;
}
else
{
prev=prev.next;
}
}
return null;
}
八.删除所有值为key的节点(双指针思想)
前提:链表已经排好序
在节点已经排好序的情况下,相同的节点是紧挨着的。定义两个node(引用)类型,两两向后遍历。
//删除所有值为key的节点
public void removeAllKey(int key)
{
Node prev=this.head;
Node cur=this.head.next;
while (cur!=null)
{
if (cur.date==key)
{
//删除cur
prev.next=cur.next;
}
else
{
//向后推进
prev=cur;
}
//向后推进
cur=cur.next;
}
//排除头节点正好是要删除的节点的情况
if (this.head.date==key)
{
this.head=this.head.next;
}
}
九.清除链表(jvm回收机制)
1.思路一:将所有的next域置为空(比较麻烦时间复杂度高)
2.思路二:直接将head.next置为空。
原因:jvm回收机制:jvm在回收内存的时候,当该对象没有人在引用他的时候。这个对象才会被回收。
将head置为空,下一个节点不被引用后被删除。下一个节点被删除,下下个节点就不会被引用进而被删除,这样会产生链式反应,将所有的节点删除。
十.功能整合
class Node
{
public int date;
public Node next;
public Node(int date)
{
this.date=date;
this.next=null;
}
}
public class My_list {
public Node head;
//只是一个标识,第一次一开始什么也不指向,为null
//第一次之后head不是null了已经指向新的node了
//头插法
public void addFirst(int data)
{
Node node=new Node(data);
if(this.head==null)
{
this.head=node;
return;
//void中的return只是使方法结束调用
}
node.next = this.head;
this.head = node;
}
//尾插法
public void addLast(int data)
{
Node node=new Node(data);
Node cur=this.head;
if (this.head==null)
{
this.head=node;
return;
}
while (cur.next!=null)
{
cur=cur.next;
}
cur.next=node;
}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data)
{
Node node=new Node(data);
if(index==0)
{
this.addFirst(data);
return;
}
if (index==this.size())
{
this.addLast(data);
return;
}
//获得前一个节点的地址
Node cur=searchIndex(index);
//查找节点
node.next=cur.next;
cur.next=node;
}
private Node searchIndex(int index)
{
if(index<0||index>this.size())
{
throw new RuntimeException("index不合法");
}
Node cur=this.head;
while (index-1!=0)
//从head开始运行index-1次找到index-1位置的引用
{
cur=cur.next;
index--;
}
return cur;
}
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key)
{
Node cur=this.head;
while (cur!=null)
{
if (cur.date==key)
{
return true;
}
cur = cur.next;
}
return false;
}
//得到单链表的长度
public int size()
{
int length=0;
Node cur=this.head;
while (cur!=null)
{
length++;
cur=cur.next;
}
return length;
}
private Node getPrev(int key)
{
Node prev=this.head;
while (prev.next!=null)
{
if (prev.next.date==key)
{
return prev;
}
else
{
prev=prev.next;
}
}
return null;
}
//删除第一次出现关键字为key的节点
public void remove(int key)
{
if (this.head==null)
{
return;
}
if(this.head.date==key)
{
this.head=this.head.next;
return;
}
Node prev=getPrev(key);
if (prev==null)
{
System.out.println("找不到此节点");
return;
}
Node del=prev.next;
prev.next=del.next;
}
//删除所有值为key的节点
public void removeAllKey(int key)
{
Node prev=this.head;
Node cur=this.head.next;
while (cur!=null)
{
if (cur.date==key)
{
prev.next=cur.next;
}
else
{
prev=cur;
}
cur=cur.next;
}
if (this.head.date==key)
{
this.head=this.head.next;
}
}
public void display()
{
Node cur=this.head;
while (cur!=null)
{
System.out.print(cur.date+" ");
cur=cur.next;
}
}
public void clear()
{
this.head=null;
}
}