定义
链表是一种通过指针串联在一起的线性结构,每一个节点包括数据域value和指针域next两部分(指向节点)。链表的入口节点称为链表的头结点head;最后一个节点的指针域指向最后一个节点的指针域指向null空指针。
- 类型:
- 单链表,一个数据接一个指针(单向操作)
- 双链表,每个节点两个指针域,一个prev指向下一个节点,一个next指向上一个节点(双向操作)
- 循环链表,链表首尾相接(解决约瑟夫环问题)
- 与数组相关操作时间复杂度对比:
类型 | 插入/删除 | 查询 | 适用场景 |
---|---|---|---|
数组 | O(n) | O(1) | 数据量固定,频繁查询,较少增删 增删影响后续位置元素的内存变化 |
链表 | O(1) | O(n) | 数据量不固定,频繁增删,较少查询 查询得顺着链表一直找 |
再认识一下“类”
之前的题目都是基于一个主函数类开展的,没有引用其他的类和其中的方法,所以对于具体类的构造不太清楚,前面博客中提及的类也只是一个大致的概念,这部分参考文章Java类详解(五大成员))写一下注意点。
- 成员变量
- 在类中定义的为全局变量也叫做成员变量,包括静态变量(static属性修饰)和实例变量,区别就是实例变量得创建一个对象后才能引用,静态变量只在类初始化时执行一次。
- 局部变量作用域仅限于声明它的方法、构造函数或代码块(如for语句中的i的定义)。
- 方法
- 自定义方法的五要素:修饰符,返回值类型,方法名,参数列表,方法体。
- 实参是函数调用时括号里的参数,形参是函数定义时括号里的参数。
- static修饰的方法为静态方法,只在初始化时执行一次,而且只能用静态变量和成员方法;this是实例化的对象的代指,所以静态方法中不能出现this。
- 构造器
创建对象实例时初始化,要求名字必须和类名一致且无返回值。 - 代码块
{},分为static修饰的;类中自带的,构造函数中的几类
父类静态代码块 > 子类静态代码块 > main()方法 > 父类代码块 > 父类构造器 > 子类代码块 > 子类构造器 - 内部类
一个类定义在另一个类里面或者一个方法里面
LinkedList类
- 定义
LinkedList(): 创建一个新的空链表。LinkedList<String> list = new LinkedList<>();
- 增
add(E element)/addLast(E element): 将指定的元素添加到链表的末尾。泛型数据
addFirst(E element): 将指定的元素添加到链表的开头。
add(int index, E element): 将指定的元素插入到链表的指定位置。 - 删
remove(int index| E element): 删除链表中指定位置的元素或指定元素,并返回被删除的元素。
clear(): 清空链表中的所有元素。 - 改
toArray(): 将链表转换为数组。 - 查
get(int index): 返回链表中指定位置的元素。
size(): 返回链表中元素的数量。
contains(Object element): 检查链表中是否包含指定的元素,返回true或false。
isEmpty(): 检查链表是否为空,返回true或false。
indexOf(Object element): 返回链表中第一次出现指定元素的索引位置,如果不存在则返回-1。
lastIndexOf(Object element): 返回链表中最后一次出现指定元素的索引位置,如果不存在则返回-1。
自定义链表类
- 定义链表
public class ListNode {
// 类属性、成员变量
int val;//值
ListNode next;//指向下一个节点
// 节点的构造函数
public ListNode(int val) {
this.val = val;
}
- 头插法,新节点当头结点
public void addFirst(int data){
ListNode node = new ListNode(data);
node.next = this.head;//this指的是实例化的链表
this.head = node;
}
- 尾插法,先排除空链表,当前链表的最后一个节点的下一个就是新节点
public void addLast(int data){
ListNode node = new ListNode(data);
if(this.head == null){
this.head = node;
}else {
ListNode cur = this.head;
while(cur.next != null){
cur = cur.next;
}
cur.next = node;
}
}
- 打印链表
public void display(){
ListNode cur = this.head;
while(cur != null){
System.out.printf("%d%c",cur.val, cur.next == null ? '\n' : ' ');//结束换行,没结束输出空格
cur = cur.next;
}
}
}
- 删除指定元素,排除空链表,然后判断是否是删除头结点(掐头,原来旧头的指向置null),尾节点(去尾,直接赋值覆盖最后一个指向)
/**
* 删除第一次出现的值为val的节点
*/
public void removeValueOnce(int val) {
if (head == null) {
System.err.println("链表为空,无法删除");
return;
}
if (head.val == val) {
// 此时头结点就是第一个值为val的结点
Node x = head;
head = head.next;
x.next = null;
size--;
} else {
// 当前头结点不是待删除的结点,需要遍历
// 这里同样找到待删除的前驱结点
// 能否知道前驱结点移动步数?
Node prev = head;
while (prev.next != null) {
// 至少还有后继结点
if (prev.next.val == val) {
// 此时prev.next就是待删除的结点
Node node = prev.next;
prev.next = node.next;
node.next = null;
size--;
return;
}
prev = prev.next;
}
}
}