一、单链表的理解
链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。
单链表是一种链式存取的数据结构,用一组任意的存储单元存放线性表中的数据元素。链表中的数据是以结点表示的,每个结点由元素和指针组成。
带头结点的单链表

带头结点的单链表第一个结点作为头结点,并在其下半部分存放第一个结点的地址,之后的每个结点都会在上半部分存放元素值,在下半部分存放下一个结点的地址。
不带头结点的单链表

不带头结点的单链表从第一个结点开始就存放数据元素,并且下半部分存放所指向下一个元素的地址。
二、单链表的代码实现
首先我们要先构建一个单链表并初始化单链表。【接下来我们讨论的情况无特殊说明都是使用带头结点的单链表来实现代码】
class ListNode{
public int val;//存储该结点的值
public ListNode next;//存储下一个结点的地址
public ListNode (int val){
this.val = val;
}
}
在其中创造了一个构造方法用来存放元素数值。
头插法
如果我们要在一个单链表表头插入一个结点,我们需要先让此结点成为一个新的头结点,并让该新头结点的地址域存放原来第一个结点的地址。

调整过后如图:

public void addFirst(int data) {
ListNode node = new ListNode(data);
node.next = this.head;
this.head = node;
}
尾插法
如果我们要在单链表的尾部插入一个元素,只要将原来单链表尾部的元素地址域指向下一个结点的地址即可。

在尾插的时候我们要注意该链表是否为空链表,以及尾插的位置在哪里
public void addLast(int data) {
ListNode node = new ListNode(data);
ListNode cur = this.head;
if (this.head == null) {
this.head = node;
}
while (cur.next != null) {
cur = cur.next;
}
cur.next = node;
}
任意位置插入,第一个数据节点为0号下标
在进行插入时有三个注意事项:
如果在链表头部插入,则直接使用头插法插入
如果在链表尾部插入,则直接使用尾插法插入
如果在链表中部插入,则需要先判断插入位置是否合理,然后寻找插入位置的前一个元素即可插入

public void addIndex(int index, int data) {
if (index < 0 || index > size()) {
System.out.println("index不合法");
return;
}
if (index == 0) {
addFirst(data);
return;
}
if (index == size()) {
addLast(data);
return;
}
ListNode pre = findIndex(index);
ListNode node = new ListNode(data);
node.next = pre.next;
pre.next = node;
}
查找是否包含关键字key是否在单链表中
判断关键字我们只需要遍历数组并返回是否正常寻找即可
public boolean contains(int key) {
ListNode cur = this.head;
while (cur != null) {
if (cur.val == key) {
return true;
}
cur = cur.next;
}
return false;
}
删除第一次出现关键字为key的节点
如果我们要删除的结点是第一个结点,则直接使头结点连接第二个结点即可,如果是删除其他的结点,则需要找到该结点的前驱结点在进行删除

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 pre = head;
ListNode cur = head.next;
while(cur != null){
if(cur.val == key){
pre.next = cur.next;
break;
}else {
pre = cur;
}
cur = cur.next;
}
}
删除所有值为key的结点
此时我们需要通过循环来遍历链表并删除结点
利用cur遍历一遍单链表,如果cur.value == key值,则直接利用prev.next = cur.next完成删除操作
如果cur.value != key,则prev = cur; cur = cur.next; 两个都往后顺移一位,如果遇到cur.value == key这种情况,则进行第一步操作.
等到一次遍历结束后,需要再判断一下第一个结点的value值是否等于key,如果等于,则head = head.next即可.

public void removeAllKey(int key) {
if(this.head == null){
throw new RuntimeException("单链表为空,无法进行删除");
}
ListNode prev = this.head;
ListNode cur = prev.next;
while(cur != null){
if(cur.val == key){
prev.next = cur.next;
cur = cur.next;
this.usedSize--;
} else{
prev = cur;
cur = cur.next;
}
}
if(this.head.val == key){
this.head = this.head.next;
this.usedSize--;
}
}
清空链表
直接将head结点置为null即可实现清空操作,当head结点置为null后,之后的结点对象都变成了未被引用的对象,垃圾回收器会自动回收这些未引用的对象
也可以单个释放,一个一个删除结点即可
public void clear() {
ListNode cur = this.head;
while (cur != null) {
cur.next.next = cur.next;
cur.next = null;
cur = cur.next.next;
}
this.head = null;
}