📚 目录
1.单向无头非循环链表简单介绍
链表我们可以理解为:一辆火车,每节火车车厢里面存储着内容和下一个车厢的节点。

无头单向非循环链表,是链表最基础的一种形态:
- 无头:链表没有额外的头结点,第一个节点就是数据节点本身;
- 单向:每个节点只保存一个指向下一个节点的引用,只能从前往后遍历;
- 非循环:最后一个节点的next为null,不指向链表的开头。
[🔙 返回目录](#🔙 返回目录)
2.链表属性
接下来我们以整型单向无头链表为例子: 链表所需要的属性: 可以定义接口的,也可以不定义接口。
以定义接口为例子:
java
public interface IMyList {
//头插
void addFirst(int data);
//尾插
void addLast(int data);
//随便插
void addIndex(int index,int val);
//获取链表大小
int size();
//清除
void clear();
//删除
void remove(int kay);
//删除所以有关这个值
void removeAll(int kay);
//查找
boolean contains(int data);
//打印内容
void display();
}
然后我们需要创建一个MyLink类,里面有一个静态内部类LinkNode也就是我们的节点。
和一个头节点。
java
public class MyLink implements ILink{
//节点
static class LinkNode{
private int val;
private LinkNode next;
public LinkNode(int val) {
this.val = val;
}
}
//头节点
public LinkNode head;
//---------------------头插
@Override
public void addFirst(int val) {
}
//---------------------尾插
@Override
public void addLast(int val) {
}
//---------------------任意位置
@Override
public void addIndex(int index, int val) {
}
//---------------------链表大小
@Override
public int size() {
}
//---------------------清空链表
@Override
public void clear() {
}
//---------------------删除链表一个相关节点
@Override
public void remove(int key) {
}
//---------------------删除所有相关的节点
@Override
public void removeAll(int key) {
}
//---------------------查找
@Override
public boolean contains(int val) {
return false;
}
//---------------------打印
@Override
public void display() {
}
}
[🔙 返回目录](#🔙 返回目录)
3.模拟实现链表
头插

有4个节点,我们需要一次进行头插。
需要让添加的 数据的下一个为head,然后再让head走到当前要添加的位置。

java
public void addFirst(int val) {
LinkNode cur = new LinkNode(val);
cur.next = head;+
head = cur;
}

此时我们能看到我们的头插没有任何问题。
尾插

假设有4个节点。
此时我们需要判断此时链表是否为第一个元素,如果为第一个元素就让head = 我们要尾插的节点。
后面需要通过循环找到当前链表最后一个元素,让最后一个元素尾插。

java
public void addLast(int val) {
LinkNode linkNode = new LinkNode(val);
if(head==null) {
head = linkNode;
return;
}
//此时链表不是空,我们就需要找到尾节点的位置。
LinkNode cur = getLastIndex();
cur.next = linkNode;
}
//专门服务于addLast的方法
private LinkNode getLastIndex() {
//定义一个零时的节点来找最后一个节点
LinkNode cur = head;
while (cur.next!=null) {//只要节点的下一个不为null就让他进去遍历。
cur = cur.next;
}
//直到找到最后一个带有数据的节点。
return cur;
}

此时链表成功倒插。
任意位置插入

此时有一个节点想要插入到2的位置。
首先我们得检查次链表的合法性:是不是为空?插入的位置是否合法?

添加的自定义异常:用来判断是否合法。
java
public class LinkLegal extends RuntimeException{
public LinkLegal() {
}
public LinkLegal(String message) {
super(message);
}
}
java
public void addIndex(int index, int val) {
//判断是否合法,此时我们需要自定义一个异常。
legal(index);
//此时我们需要判断是否为头插
if(index == 0) {
addFirst(val);
return;
}
//此时判断是不是尾插
if(index == size()) {
addLast(val);
return;
}
//定义要插入的元素;
LinkNode linkNode = new LinkNode(val);
//此时既不是头插也不是尾插
LinkNode cur = getIndexPrevious(index);
//让定义的下一个元素等于cur的下一个
linkNode.next = cur.next;
cur.next = linkNode;
}
//专门为addIndex服务的方法
private void legal(int index) {
if(index>size()||index<0) {
throw new LinkLegal("下标不合法");
}
}
//专门帮cur寻找插入位置前一个节点
private LinkNode getIndexPrevious(int index) {
int count = 0;
LinkNode cur = head;
while (count!=index-1) {
cur = cur.next;
count++;
}
return cur;
}

结果看来:我们的也是正确的。
链表长度
求链表长度就比较简单了,我们会遍历链表就行。
我们只要需要定义一个临时变量:count每次cur不等于null就++;
java
public int size() {
LinkNode cur = head;
int count = 0;
while (cur!=null) {
count++;
cur = cur.next;
}
return count;
}
链表的打印
和求长度一样,需要我们变量链表。
java
public void display() {
LinkNode cur = head;
while (cur!=null) {
System.out.print(cur.val+" ");
cur = cur.next;
}
System.out.println();
}

移除指定值的第一个节点。

我们需要找到删除的前一个节点,再定义一个节点来保存删除节点的下一个节点。

java
public void remove(int key) {
//判断链表为不为null
if(head==null) {
System.out.println("链表为空");
return;
}
//判断要删除的节点是否为头部
if(head.val==key) {
head = head.next;
return;
}
//此时我们需要找到删除节点的前一个几点。
LinkNode cur = getKeyPrevious(key);
//判断当前值是否为null
if(cur==null|| cur.next==null) {
System.out.println("没有你要删除的元素");
return;
}
//此时找到了当前这个元素。
//此时我们需要一个新的LinkNode类型的pre
LinkNode pre = cur.next;
//让cur的下一个等于pre
cur.next = pre.next;
}
//专门服务于remove这个方法
private LinkNode getKeyPrevious(int key) {
LinkNode cur = head;
while (cur.next!=null) {
//找到要删除的前一个元素
if(cur.next.val==key) {
//如果是可以返回删除节点的前一个节点
return cur;
}
cur = cur.next;
}
//找不到则返回null
return null;
}
删除头删除尾删除中间都没有问题。

删除所有相关的节点

假设我们要删除链表中所有值为1的节点。
首先我们还是得要判断head的合法性。
我们需要定义一个cur等于head的下一个节点,再顶一个一个pre等于head。

java
public void removeAll(int key) {
if(head==null) {
System.out.println("链表为空");
return;
}
LinkNode pre = head;
LinkNode cur = head.next;
while (cur!=null) {
if(cur.val==key) {
pre.next = cur.next;
}else {
pre = cur;
}
cur = cur.next;
}
//最后再判断链表的头部是否为我们要删除的节点
if(head.val==key) {
head = head.next;
}
}
结果来看我们也是成功模拟实现了。

查找
查找也是相较于添加和删除的比较简单。

java
public boolean contains(int val) {
LinkNode cur = head;
while (cur!=null) {
if(cur.val==val) {
return true;
}
cur = cur.next;
}
return false;
}
清空链表
和顺序表类似,我们需要让每一个节点都置为null。

java
public void clear() {
LinkNode cur = head;
while (cur!=null) {
LinkNode pre = cur.next;
cur.next = null;
cur = pre;
}
head = null;
}

[🔙 返回目录](#🔙 返回目录)
4. 代码汇总
java
public interface ILink {
//头插
void addFirst(int val);
//尾插
void addLast(int val);
//随便插
void addIndex(int index,int val);
//获取链表大小
int size();
//清除
void clear();
//删除
void remove(int key);
//删除所以有关这个值
void removeAll(int key);
//查找
boolean contains(int val);
//打印内容
void display();
}
java
public class LinkLegal extends RuntimeException{
public LinkLegal() {
}
public LinkLegal(String message) {
super(message);
}
}
java
public class MyLink implements ILink{
static class LinkNode{
private int val;
private LinkNode next;
public LinkNode(int val) {
this.val = val;
}
}
public LinkNode head;
//---------------------头插
@Override
public void addFirst(int val) {
LinkNode linkNode = new LinkNode(val);
linkNode.next = head;
head = linkNode;
}
//---------------------尾插
@Override
public void addLast(int val) {
LinkNode linkNode = new LinkNode(val);
if(head==null) {
head = linkNode;
return;
}
//此时链表不是空,我们就需要找到尾节点的位置。
LinkNode cur = getLastIndex();
cur.next = linkNode;
}
//专门服务于addLast的方法
private LinkNode getLastIndex() {
//定义一个零时的节点来找最后一个节点
LinkNode cur = head;
while (cur.next!=null) {
cur = cur.next;
}
return cur;
}
//---------------------任意位置
@Override
public void addIndex(int index, int val) {
//判断是否合法,此时我们需要自定义一个异常。
legal(index);
//此时我们需要判断是否为头插
if(index == 0) {
addFirst(val);
return;
}
//此时判断是不是尾插
if(index == size()) {
addLast(val);
return;
}
//定义要插入的元素;
LinkNode linkNode = new LinkNode(val);
//此时既不是头插也不是尾插
LinkNode cur = getIndexPrevious(index);
//让定义的下一个元素等于cur的下一个
linkNode.next = cur.next;
cur.next = linkNode;
}
//专门为addIndex服务的方法
private void legal(int index) {
if(index>size()||index<0) {
throw new LinkLegal("下标不合法");
}
}
//专门帮cur寻找插入位置前一个节点
private LinkNode getIndexPrevious(int index) {
int count = 0;
LinkNode cur = head;
while (count!=index-1) {
cur = cur.next;
count++;
}
return cur;
}
//---------------------链表大小
@Override
public int size() {
LinkNode cur = head;
int count = 0;
while (cur!=null) {
count++;
cur = cur.next;
}
return count;
}
//---------------------清空链表
@Override
public void clear() {
LinkNode cur = head;
while (cur!=null) {
LinkNode pre = cur.next;
cur.next = null;
cur = pre;
}
head = null;
}
//---------------------删除链表一个节点
@Override
public void remove(int key) {
//判断链表为不为null
if(head==null) {
System.out.println("链表为空");
return;
}
//判断要删除的节点是否为头部
if(head.val==key) {
head = head.next;
return;
}
//此时我们需要找到删除节点的前一个几点。
LinkNode cur = getKeyPrevious(key);
//判断当前值是否为null
if(cur==null|| cur.next==null) {
System.out.println("没有你要删除的元素");
return;
}
//此时找到了当前这个元素。
//此时我们需要一个新的LinkNode类型的pre
LinkNode pre = cur.next;
//让cur的下一个等于pre
cur.next = pre.next;
}
//专门服务于remove这个方法
private LinkNode getKeyPrevious(int key) {
LinkNode cur = head;
while (cur.next!=null) {
//找到要删除的前一个元素
if(cur.next.val==key) {
return cur;
}
cur = cur.next;
}
//找不到则返回null
return null;
}
//---------------------删除所有相关的节点
@Override
public void removeAll(int key) {
if(head==null) {
System.out.println("链表为空");
return;
}
LinkNode pre = head;
LinkNode cur = head.next;
while (cur!=null) {
if(cur.val==key) {
pre.next = cur.next;
}else {
pre = cur;
}
cur = cur.next;
}
//最后再判断链表的头部是否为我们要删除的节点
if(head.val==key) {
head = head.next;
}
}
//---------------------查找
@Override
public boolean contains(int val) {
LinkNode cur = head;
while (cur!=null) {
if(cur.val==val) {
return true;
}
cur = cur.next;
}
return false;
}
//---------------------打印
@Override
public void display() {
LinkNode cur = head;
while (cur!=null) {
System.out.print(cur.val+" ");
cur = cur.next;
}
System.out.println();
}
}
java
public class Test {
public static void main(String[] args) {
MyLink myLink = new MyLink();
myLink.addLast(1);
myLink.addLast(2);
myLink.addLast(1);
myLink.addLast(4);
myLink.addIndex(1,123);
myLink.remove(2);
myLink.removeAll(1);
System.out.println(myLink.contains(1));
System.out.println("清空前");
myLink.display();
myLink.clear();
System.out.println("清空后");
myLink.display();
}
}
[🔙 返回目录](#🔙 返回目录)