目录
- [1. 双向不带头非循环链表的介绍](#1. 双向不带头非循环链表的介绍)
- [2. 相关功能的实现](#2. 相关功能的实现)
-
- [2.1 基本框架](#2.1 基本框架)
- [2.2 size](#2.2 size)
- [2.3 addFirst](#2.3 addFirst)
- [2.4 addLast](#2.4 addLast)
- [2.5 addIndex](#2.5 addIndex)
- [2.6 contains](#2.6 contains)
- [2.7 remove](#2.7 remove)
- [2.8 removeAllKey](#2.8 removeAllKey)
- [2.9 clear](#2.9 clear)
- [3. 全部代码](#3. 全部代码)
前面我们学习了最简单的链表:单链表,今天我们学习双向不带头非循环链表,这也是Java的集合框架中LinkedList的底层实现
1. 双向不带头非循环链表的介绍
双向指的是:每个链表结点有三个域,一个是数据域,另外两个保存了前驱结点的地址和后继结点的地址,如图,第一个结点的前驱为null,最后一个结点的后继也为null
2. 相关功能的实现
2.1 基本框架
同样定义泛型类myLinkedList,每个结点定义为静态内部类ListNode,提供构造方法给val初始化,定义结点first表示链表的第一个结点,定义last表示链表的最后一个结点
java
public class myLinkedList<T> {
//结点类
static class ListNode {
public ListNode prev;//前驱
public Object val;//数据
public ListNode next;//后继
//构造方法
public ListNode(Object val) {
this.val = val;
}
}
public ListNode first;//第一个结点
public ListNode last;//最后一个结点
//....其他方法
}
2.2 size
size的功能是获取链表长度,定义计数器size,遍历链表每个结点,每遍历一个结点size+1,直至遍历完链表,返回size
java
//得到单链表的长度
public int size() {
int size = 0;
ListNode cur = first;
while (cur != null) {
cur = cur.next;
size++;
}
return size;
}
2.3 addFirst
addFirst意为头插,在链表的头部插入数据,如果链表没有数据,first等于null,则让first和last等于新的结点,然后return;如果链表已有数据,先让新的结点绑定链表,再让原来的头部的prev指向新的结点,接着更新first
java
//头插法
public void addFirst(T val) {
ListNode newNode = new ListNode(val);
//如果没有元素
if (first == null) {
first = last = newNode;
return;
}
newNode.next = first;
first.prev = newNode;
first = newNode;
}
2.4 addLast
addLast表示尾插,在链表尾部插入数据,如果链表没有数据,让first和last等于新的结点,然后return;如果链表已有数据,让新的结点的prev指向链表的last,接着让last的next指向新的结点,然后更新last的值
java
//尾插法
public void addLast(T val) {
ListNode newNode = new ListNode(val);
//如果没有元素
if (last == null) {
first = last = newNode;
return;
}
newNode.prev = last;
last.next = newNode;
last = newNode;
}
2.5 addIndex
addIndex表示在Index位置插入数据,插入前需要判断Index的合法性(Index不能小于0,不能大于链表的长度),定义IndexNotLegalException异常类,如果Index不合法就抛出这个异常
java
public class IndexNotLegalException extends RuntimeException {
public IndexNotLegalException(String s) {
super(s);
}
public IndexNotLegalException() {
super();
}
}
如果Index等于0,表示头插,如果Index等于链表长度,表示尾插,此时直接调用方法;在插入之前先找到Index位置的结点,接着先绑定后面的数据
java
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index, T val) {
ListNode newNode = new ListNode(val);
//index合法性检查
try {
if (index < 0 || index > size()) {
throw new IndexNotLegalException("Index不合法");
}
} catch (IndexNotLegalException e) {
e.printStackTrace();
}
//index=0 头插
if (index == 0) {
addFirst(val);
return;
}
//index=size 尾插
if (index == size()) {
addLast(val);
return;
}
//
ListNode cur = first;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
ListNode prev = cur.prev;
//开始绑定
newNode.next = cur;
newNode.prev = prev;
prev.next = newNode;
cur.prev = newNode;
}
2.6 contains
查找链表中是否包含某个数据(key),如果包含返回true否则,返回false
java
//查找是否包含关键字key是否在单链表当中
public boolean contains(T key) {
if (first == null) {
return false;
}
ListNode cur = first;
while (cur != null) {
if (cur.val.equals(key)) {
return true;
}
cur = cur.next;
}
return false;
}
2.7 remove
删除链表中第一个值为key的结点,如果链表为空,直接返回。定义del来遍历链表,如果del的值与key相等,分情况讨论:
1.del为第一个结点,此时只需将first指向下一个结点即可
2.del为最后一个结点,此时将last指向前一个结点,然后将last的next置为空
3.一般情况:要删除的结点在中间,定义两个变量:prevNode,表示要删除的结点的前一个结点;nextNode,表示要删除的结点的后一个结点,让prevNode的next指向nextNode,让nextNode的prev指向prevNode,此时就跳过了要删除的结点,没人引用它也就意味着删除了它
java
//删除第一次出现关键字为key的结点
public void remove(T key) {
if (first == null) {
return;
}
ListNode del = first;
while (del != null) {
if (del.val.equals(key)) {
//删除
ListNode preNode = del.prev;
ListNode nextNode = del.next;
//pre==null?
if (pre == null) {
//第一个是key
first = first.next;
return;
}
if (next == null) {
//最后一个是key
last = last.prev;
last.next = null;
return;
}
preNode.next = nextNode;
nextNode.prev = preNode;
return;
}
del = del.next;
}
}
2.8 removeAllKey
删除链表中所有值为key的结点,分情况讨论:
1.前面部分全是要删除的结点:first往后走,如果first为空了,说明整个链表的值都是key,这个时候直接return
2.后面部分全是要删除的结点:让last往前走
3.一般情况:情况1处理了链表前部分都是key的情况,情况2处理了链表后部分都是key的情况,此时的key只能出现在中间部分,删除的逻辑remove一样,只不过remove删除完一个之后return了,removeAllKey则是删除之后继续删除
java
//删除所有值为key的结点
public void removeAllKey(T key) {
if (first == null) {
return;
}
//处理前面的key
while (first.val.equals(key)) {
first = first.next;
if (first == null) {
return;
}
}
//处理后面的key
while (last.val.equals(key)) {
last = last.prev;
last.next = null;
}
//
ListNode del = first;
while (del != null) {
if (del.val.equals(key)) {
//删除
ListNode preNode = del.prev;
ListNode nextNode = del.next;
//
preNode.next = next;
nextNode.prev = preNode;
}
del = del.next;
}
}
2.9 clear
clear表示将链表置空,只需循环遍历链表,将每一个结点都置空
java
public void clear() {
while (first != null) {
ListNode next = first.next;
first.prev = first.next = null;
first = next;
}
last = null;
}
3. 全部代码
java
//不带头 双向 非循环 链表
public class myLinkedList<T> {
//结点类
static class ListNode {
public ListNode prev;//前驱
public Object val;//数据
public ListNode next;//后继
//构造方法
public ListNode(Object val) {
this.val = val;
}
}
public ListNode first;//第一个结点
public ListNode last;//最后一个结点
//头插法
public void addFirst(T val) {
ListNode newNode = new ListNode(val);
//如果没有元素
if (first == null) {
first = last = newNode;
return;
}
newNode.next = first;
first.prev = newNode;
first = newNode;
}
//尾插法
public void addLast(T val) {
ListNode newNode = new ListNode(val);
//如果没有元素
if (last == null) {
first = last = newNode;
return;
}
newNode.prev = last;
last.next = newNode;
last = newNode;
}
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index, T val) {
ListNode newNode = new ListNode(val);
//index合法性检查
try {
if (index < 0 || index > size()) {
throw new IndexNotLegalException("Index不合法");
}
} catch (IndexNotLegalException e) {
e.printStackTrace();
}
//index=0 头插
if (index == 0) {
addFirst(val);
return;
}
//index=size 尾插
if (index == size()) {
addLast(val);
return;
}
//
ListNode cur = first;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
ListNode prev = cur.prev;
//开始绑定
newNode.next = cur;
newNode.prev = prev;
prev.next = newNode;
cur.prev = newNode;
}
//查找是否包含关键字key是否在单链表当中
public boolean contains(T key) {
if (first == null) {
return false;
}
ListNode cur = first;
while (cur != null) {
if (cur.val.equals(key)) {
return true;
}
cur = cur.next;
}
return false;
}
//删除第一次出现关键字为key的节点
public void remove(T key) {
if (first == null) {
return;
}
//
ListNode del = first;
while (del != null) {
if (del.val.equals(key)) {
//删除
ListNode pre = del.prev;
ListNode next = del.next;
//pre==null?
if (pre == null) {
//第一个是key
first = first.next;
return;
}
if (next == null) {
//最后一个是key
last = last.prev;
last.next = null;
return;
}
pre.next = next;
next.prev = pre;
return;
}
del = del.next;
}
}
//删除所有值为key的节点
public void removeAllKey(T key) {
if (first == null) {
return;
}
//处理前面的key
while (first.val.equals(key)) {
first = first.next;
if (first == null) {
return;
}
}
while (last.val.equals(key)) {
last = last.prev;
last.next = null;
}
//
ListNode del = first;
while (del != null) {
if (del.val.equals(key)) {
//删除
ListNode pre = del.prev;
ListNode next = del.next;
//
pre.next = next;
next.prev = pre;
}
del = del.next;
}
}
//得到单链表的长度
public int size() {
int size = 0;
ListNode cur = first;
while (cur != null) {
cur = cur.next;
size++;
}
return size;
}
public void clear() {
while (first != null) {
ListNode next = first.next;
first.prev = first.next = null;
first = next;
}
last = null;
}
}
今天的内容就到这里,感谢老铁们的点赞、收藏、评论~❤