- ArrayList的缺陷
通过ArrayList上节课的学习,我们了解到如果ArrayList要删除或插入一个元素,后面的元素都要进行移动,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置的插入和删除操作比较多的场景。因此java集合又引入了ListedList,即链表结构。
- 链表
2.1链表的结构
链表是一种物理储存上非连续的储存结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。
【注意】
从图中可以看出,链表结构在逻辑上是连续的,但是在物理上是不连续的;
实际中的节点都是在堆上的;
从堆上申请的空间,是按照一定的策略进行分配的,两个节点的储存空间可能是连续的可能是不连续的(链表的优点就是不需要物理上连续的大块空间)。
现实中链表的种类很多,但基本就以下8种情况:
单向或双向
带头或者不带头
循环或者非循环
这3种大的类型再任意搭配,如:单向不带头非循环链表,双向不带头循环链表等。但是我们重点掌握的只有两种:
无头单项非循环链表:结构简单,一般不会用来储存数据,实际中更多的是作为其他数据结构的子结构,但是这种结构在笔试和面试中常见。
无头双向链表:在Java中的集合框架LinkedList底层的实现都是无头双向循环链表。
2.2无头单向非循环链表的实现
MyLinkedList的简单实现
package Demo1;
import java.util.LinkedList;
/**
* Describe:这里简单实现一下LinkedList
* User:lenovo
* Date:2023-01-04
* Time:16:21
*/
class ListNode {
public int val;
public ListNode next;
public ListNode() {}
public ListNode(int val) {
this.val = val;
}
}
public class MyLinkedList {
ListNode head;
//为了下面的更好的调试,我们自己创造一个方法,来打印这个链表,但是这个方法并不是有的
public void display() {
ListNode cur = head;
while(cur != null) {
System.out.print(cur.val + " ");
cur = cur.next;
}
System.out.println();
}
//头插法
public void addFirst(int data) {
ListNode cur = new ListNode(data);
cur.next = head;
head = cur;
}
//尾插法
public void addLast(int data) {
ListNode cur = head;
//判断链表是否为空
if(head == null) {
head = new ListNode(data);
}
//找到链表的最后一个节点
while(cur.next != null) {
cur = cur.next;
}
ListNode tmp = new ListNode(data);
cur.next = tmp;
}
//任意位置插入,第一个节点为0号下表
public void addIndex(int index, int data) {
if(index < 0) {
throw new ListIndexOutOfException("请检查坐标合法性");
}
//如果原链表为空
if(head == null) {
head = new ListNode(data);
return;
}
ListNode cur = head;
ListNode tmp = new ListNode(data);
int count = 0;
//如果是头插
if(index == 0) {
tmp.next = head;
head = tmp;
return;
}
//不是头插
while(cur != null) {
if(count + 1 == index) {
tmp.next = cur.next;
cur.next = tmp;
return;
}else {
count++;
cur = cur.next;
}
}
if(count != index) {
throw new ListIndexOutOfException("请检查坐标的合法性");
}
}
//查找关键字key是否在单链表中
public boolean contains(int key) {
if(head == null) {
return true;
}
ListNode cur = head;
while(cur != null) {
if(cur.val == key) {
return true;
}else {
cur = cur.next;
}
}
return false;
}
//删除第一次出现关键字的节点
public void remove(int key) {
if(head == null) {
return;
}
ListNode cur = head;
//第一个节点判断
if(head.val == key) {
head = head.next;
return;
}
//其他节点的判断
while(cur.next != null) {
if(cur.next.val == key) {
cur.next = cur.next.next;
return;
}else {
cur = cur.next;
}
}
}
//删除所有的关键字key的节点
public void removeAllKey(int key) {
//链表为空
if(head == null) {
return;
}
//除了第一个节点,其他节点的判断
ListNode cur = head;
while(cur.next != null) {
if(cur.next.val == key) {
cur.next = cur.next.next;
}else {
cur = cur.next;
}
}
//第一个节点的判断
if(head.val == key) {
head = head.next;
}
}
//清除所有节点
public void clear() {
head = null;
}
//得到单链表的长度
public int size() {
int count = 0;
ListNode cur = head;
while(cur != null) {
count++;
cur = cur.next;
}
return count;
}
}
异常类型
package Demo1;
/**
* Describe:
* User:lenovo
* Date:2023-01-04
* Time:19:13
*/
public class ListIndexOutOfException extends RuntimeException{
public ListIndexOutOfException() {}
public ListIndexOutOfException(String message) {
super(message);
}
}
对代码进行测试
import Demo1.MyLinkedList;
import java.util.ArrayList;
import java.util.LinkedList;
/**
* Describe:
* User:lenovo
* Date:2023-01-04
* Time:15:47
*/
public class Test {
public static void main(String[] args) {
MyLinkedList a = new MyLinkedList();
System.out.println("============头插法============");
a.addFirst(6);
a.addFirst(5);
a.addFirst(4);
a.addFirst(3);
a.addFirst(2);
a.addFirst(1);
a.display();
System.out.println("===========尾插法==============");
a.addLast(7);
a.addLast(8);
a.display();
System.out.println("==========插入任意位置========");
a.addIndex(0,0);
a.addIndex(1,1);
a.addIndex(10,9);
a.display();
System.out.println("==========在链表中查找是否包含关键字=======");
System.out.println(a.contains(0));
System.out.println(a.contains(99));
System.out.println("============删除第一个0============");
a.remove(0);
a.remove(99);
a.display();
System.out.println("===========删除所有的1============");
a.removeAllKey(1);
a.removeAllKey(9);
a.display();
System.out.println("===========获得链表的长度==========");
System.out.println(a.size());
System.out.println("==============清空链表=============");
a.clear();
a.display();
}
}
结果展示
3.单项链表相关习题
我会以博客的形式为大家展现,完成后会将链接放在下面。
希望大家多多鼓励和支持!!!!