前言:
🎈链式存储数据分为顺序存储和链表存储,链表就是存在多个节点,每个节点之间存在数据域和指向域(存储下一个节点的引用),这样的数结构可以保证每个节点之间相互关联,从而达到数据之间的关联。
🎈不同的数据结构,就会有不同优缺点。链表相对于顺序表,增加数据不需要挪动数据,只需要将新的节点插入即可,删除数据只需要删除相应的节点等等。顺序表是一块连续的内存,而链表是以节点构成数据相关联,内存都是分布开的,对于一些数据的访问顺序表优于链表,等等一些优点。各有所长,根据需求来选择合适的数据存储方式,以提高代码效率。
🎈单链表的实现,需牢牢把把握住结构的特点,每个节点是相互关联的。对链表进行增删查改等操作时,需维护好这样的结构特点,必要时可画图帮助自己理解。在清空链表时,需了解JVM对于内存的回收机制,当一个对象没有被引用时,就会被JVM所回收。意味着,只需要将头节点的next置空,那么一连串的节点都会被回收。
代码实现
public class SingleLinkedList {
static class ListNode {
private int val;
private ListNode next;
private ListNode(int val) {
this.val = val;
}
}
private ListNode head;
//判断下一个节点的值,找上一个节点
private ListNode findPrevNode(int key) {
ListNode cur = head;
while(cur.next != null) {
if(cur.next.val == key) {
return cur;
}
cur = cur.next;
}
return null;
}
//狗撵兔法找上一个节点
private ListNode findPrevNode2(int key) {
ListNode cur = head;
ListNode prev = null;
//限制cur,防止空指针异常
while(cur != null && cur.val != key) {
prev = cur;
cur = cur.next;
}
if(cur == null) {
return null;
}
return prev;
}
public void remove(int key) {
if(this.head == null) {
System.out.println("链表为空");
return;
}
//删头节点
if(this.head.val == key) {
head = head.next;
return;
}
ListNode prev = findPrevNode2(key);
if(prev == null) {
System.out.println("没有你要删除的数据");
return;
}
prev.next = prev.next.next;
}
//前后指针遍历
public void removeAllKey(int key) {
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;
}
}
public boolean contains(int key) {
ListNode cur = this.head;
while(cur != null) {
if(cur.val == key) {
return true;
}
cur = cur.next;
}
return false;
}
public void addIndexOf(int index, int date) throws IndexIllegalExpection{
//首先判断index的合法性
if(index < 0 || index > size()) {
System.out.println("位置不合法");
throw new IndexIllegalExpection("位置不合法");
}
if(index == 0) {
addFirst(date);
}
if(index == size()) {
addEnd(date);
}
ListNode cur = this.head;
//找上一个节点
while(index - 1 != 0) {
cur = cur.next;
index--;
}
ListNode newNode = new ListNode(date);
newNode.next = cur.next;
cur.next = newNode;
}
public void addEnd(int date) {
ListNode newNode = new ListNode(date);
ListNode cur = head;
if(cur == null) {
head = newNode;
return;
}
//找尾
while(cur.next != null) {
cur = cur.next;
}
cur.next = newNode;
}
public void addFirst(int date) {
ListNode newNode = new ListNode(date);
newNode.next = this.head;
this.head = newNode;
}
public void display() {
ListNode cur = this.head;
while(cur != null) {
System.out.print(cur.val + " ");
cur = cur.next;
}
}
public int size() {
int count = 0;
ListNode cur = this.head;
while(cur != null) {
count++;
cur = cur.next;
}
return count;
}
//没有引用则被jvm回收
public void clear() {
this.head = null;
}
public void createNode() {
ListNode Node1 = new ListNode(1);
ListNode Node2 = new ListNode(2);
ListNode Node3 = new ListNode(3);
ListNode Node4 = new ListNode(4);
ListNode Node5 = new ListNode(5);
this.head = Node1;
Node1.next = Node2;
Node2.next = Node3;
Node3.next = Node4;
Node4.next = Node5;
}
}
自定义异常
public class IndexIllegalExpection extends RuntimeException{
public IndexIllegalExpection() {
}
public IndexIllegalExpection(String message) {
super(message);
}
}
测试模块
public class Main {
public static void main(String[] args) {
SingleLinkedList linkedList = new SingleLinkedList();
linkedList.createNode();
linkedList.addFirst(8);
linkedList.addFirst(2);
linkedList.addEnd(25);
try {
linkedList.addIndexOf(2,95);
linkedList.addIndexOf(5,99);
} catch (IndexIllegalExpection e) {
e.printStackTrace();
}
linkedList.remove(25);
linkedList.removeAllKey(2);
linkedList.clear();
linkedList.display();
System.out.println();
System.out.println(linkedList.contains(257));
System.out.println("大小:" + linkedList.size());
}
}
小结:
🐵在学习不同的数据结构时,需抓住每种结构的特点,根据这种特点去思考,会有不一样的收获。