✨专栏:《Java SE语法》
❤️感谢大家点赞收藏⭐评论✍,您的三连就是我持续更新的动力❤️
小杨水平有限,欢迎各位大佬指点,相互学习进步!
在上一篇《顺序表》中,我们已经熟悉了 ArrayList
的使用并且进行了简单的模拟实现。ArrayList
底层使用数组来存储元素,由于其底层是一段连续的空间,当ArrayList
任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后移动,时间复杂度为O(n),效率比较低,因此ArrayList
不适合做任意位置插入和删除比较多的场景。因此:Java集合这种又引入了 LinkedList
,即链表结构。
链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中引用链接次序实现的。
注意:
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
单向或者双向
带头或者不带头
循环或者非循环
虽然有这么多的链表结构,但是我们重点掌握两种:
LinkedList
底层实现就是无头双向循环链表创建一个链表
public class MySingleList {
// 节点
static class ListNode {
public int val; // 数值域 - 存放当前节点的值
public ListNode next; // next域 指向下一个节点
public ListNode(int val) {
this.val = val;
}
}
// 链表的属性 链表的头节点
public ListNode head; // null
public void createList() {
ListNode node1 = new ListNode(1);
ListNode node2 = new ListNode(2);
ListNode node3 = new ListNode(3);
ListNode node4 = new ListNode(4);
node1.next = node2;
node2.next = node3;
node3.next = node4;
this.head = node1;
}
}
画图表示:
怎么从第一个节点走到第二个节点?
答:head = head.next
什么时候算是把节点都遍历完成?
答: head == null
代码实现:
/***
* 打印链表
*/
@Override
public void display() {
ListNode cur = head;
while (cur != null) {
System.out.print(cur.val + " ");
cur = cur.next;// 让cur这个节点 可以从一个节点走到下一个节点
}
System.out.println();
}
在链表的第一个位置插入元素。
思路:
next
指向head
head
指向插入元素代码实现:
/**
* 头插法
* @param data
*/
@Override
public void addFirst(int data) {
ListNode node = new ListNode(data); // 定义一个节点
node.next = head;
head = node;
}
在链表的最后个位置插入元素
思路:
next
指向插入的元素。代码实现:
/**
* 尾插法
* @param data
*/
@Override
public void addLast(int data) {
ListNode node = new ListNode(data); // 定义一个节点
if (head == null) { // 链表一个元素都没有
head = node;
} else {
ListNode cur = head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = node;
}
}
思路:
index
是否合法(index < 0 或者 index 大于链表长度
),如果不合法则抛出异常。index
等于0或者index
等于链表长度,则使用头插法或尾插法cur
找到index - 1
位置next
指向cur
的next
cur
的next
指向插入的元素代码实现:
/**
* 在index位置 插入data
* @param index
* @param data
*/
@Override
public void addIndex(int index, int data) throws IndexException{
if (index < 0 || index > size()) {
throw new IndexException("index不合法:" + index);
}
ListNode node = new ListNode(data); // 定义一个节点
if (head == null) {
head = node;
return;
}
if (index == 0) {
addFirst(data);
return;
}
if (index == size()) {
addLast(data);
return;
}
ListNode cur = searchPrevIndex(index);
node.next = cur.next;
cur.next = node;
}
/**
* 找到index-1的位置
* @param index
* @return
*/
private ListNode searchPrevIndex(int index) {
ListNode cur = head;
int count = 0;
while (count != index - 1) {
cur = cur.next;
count++;
}
return cur;
}
异常类:
public class IndexException extends RuntimeException{
public IndexException() {
}
public IndexException(String msg) {
super(msg);
}
}
代码实现:
/***
* 求当前链表 是否存在key
* @param key
* @return
*/
@Override
public boolean contains(int key) {
ListNode cur = head;
while (cur != null) {
if (cur.val == key) {
return true;
}
cur = cur.next;
}
return false;
}
代码实现:
/**
* 求当前链表 有多少个节点
* @return
*/
@Override
public int size() {
ListNode cur = head;
int count = 0;
while (cur != null) {
count++;
cur = cur.next;
}
return count;
}
思路:
head
指向head
的next
next
指向删除节点的next
代码实现:
/**
*
* @param key
*/
@Override
public void remove(int key) {
if (head == null) {
return;
}
if (head.val == key) {
head = head.next;
return;
}
ListNode cur = findPrevKey(key);
if (cur == null) {
return;// 链表里要没有删除的数字
}
ListNode del = cur.next;
cur.next = del.next;
}
/**
* 找到删除节点的前一个节点
* @param key
* @return
*/
private ListNode findPrevKey(int key) {
ListNode cur = head;
while (cur.next != null) {
if (cur.next.val == key) {
return cur;
} else {
cur = cur.next;
}
}
return null;
}
思路:
cur
:可能要删除的节点prev
:可能要删除的节点的前驱cur
的val
是不是要删除的元素,如果是prev
的next
指向cur
的next
,cur
指向cur
的next
;否则prev
指向cur
,cur
指向cur
的next
val
是否为的元素,如果是头节点指向头节点的neext
代码实现:
/**
* 删除链表中所有的key
* @param key
*/
@Override
public void removeAllKey(int key) {
if (head == null) {
return;
}
ListNode prev = head; // 表示当前可能要删除的节点
ListNode cur = head.next; // 可能要删除节点的前驱
while (cur != null) {
if (cur.val == key) {
prev.next = cur.next;
cur = cur.next;
} else {
prev = cur;
cur = cur.next;
}
}
if (head.val == key) {
head = head.next;
}
}
当一个对象,没有被引用的时候,就会被回收掉
/**
* 清空链表
*/
@Override
public void clear() {
head = null;
}
代码链接