目录
- 一、链表基本概念和基本代码实现
- 二、链表、动态数组整合(面向接口编程)
- 三、clear()
- 四、add(int index, E element)
- (1) 找到 index 位置的节点
- (2) get(int index) 和 set(int index, E element)
- (3) add(int index, E element)
- 五、remove(int index)
- 六、indexOf(E element)
- 七、三个 Leetcode 的练习题
- (1) 删除链表中的节点
- (2) 反转一个链表
- ① 递归反转一个链表
- ② 迭代反转一个链表
- (3) 判断一个链表是否有环(快慢指针)
一、链表基本概念和基本代码实现
🍃 动态数组有个明显的缺点:可能会造成内存空间的大量浪费
🍃 能否用到多少就申请多少内存:链表可以办到
🍃 链表是一种链式存储的线性表,所有元素的内存地址不一定是连续的
🍀 链表(LinkedList)中有 size 属性【记录着链表中元素的个数、节点的个数)
🍀 链表中的 first 被称作头指针【它引用着链表中的第一个节点(头节点)】
🍀 链表中的元素是存储在Node 节点中的,一个Node 节点可被简单看作是一个元素
🍀 Node 也是一个类
🍀 Node 节点中的element
属性是该 Node 节点 存储的数据
🍀 Node 节点中的next
属性引用着下一个节点(next
存储着下一个节点的内存地址)
🍀 尾节点的next
属性不存储任何内容(① 引用着 null;② 指向 null)
public class LinkedList<E> {
private int size;
// 头指针
private Node<E> first;
/**
* 每一个节点就是 Node 类的一个实例
*/
private static class Node<E> {
E element;
Node<E> next;
public Node(E element, Node<E> next) {
this.element = element;
this.next = next;
}
}
}
二、链表、动态数组整合(面向接口编程)
🧡 ArrayList、LinkedList 继承(extends)抽象类 AbstractList
🧡 抽象类 AbstractList 抽取 ArrayList 和 LinkedList 的公共代码,并实现(implements)了 List 接口
🧡 List 接口定义了 LinkedList 和 ArrayList 要实现的全部代码
三、clear()
🧡 没有被引用的对象会被 Java 的垃圾回收器自动回收
public void clear() {
size = 0;
first = null;
}
四、add(int index, E element)
🌼 要往 index 位置插入元素
🌼 找到index - 1
位置的节点(记作:prev)
🌼 让新节点(记作:newNode)的 next 指向prev.next
(是新节点的下一个节点)
🌼 让 prev 的 next 指针指向新节点(记作:newNode)
🌼 size++
(1) 找到 index 位置的节点
☃️ 要找 0 号位置的节点【first 头指针指向的就是 0 号位置的节点】
☃️ 要找 1 号位置的节点【first 头指针的 next 指向的就是 1 号位置的节点】
☃️ 要找 2 号位置的节点【first 头指针的 next 的 next 指向的就是 2 号位置的节点】
☃️ 要找0号位置的节点是0次 next,要找1号位置的节点是1次 next,要找2号位置的节点是2次 next,要找 index 位置的节点是 index 次 next
/**
* 返回 index 位置的节点
*/
private Node<E> node(int index) {
rangeCheck(index);
// 从头节点开始(默认指向头节点)
Node<E> moveNode = first;
for (int i = 0; i < index; i++) {
moveNode = moveNode.next;
}
return moveNode;
}
(2) get(int index) 和 set(int index, E element)
🌴 因为
node()
方法返回了 index 位置的节点,所以get(int index)
方法(返回 index 位置的元素 ) 方法也得以实现
🌴 因为node()
方法返回了 index 位置的节点,所以set(int index, E element)
方法(设置 index 位置的元素为 element ) 方法也得以实现
@Override
public E get(int index) {
Node<E> node = node(index);
return node.element;
}
@Override
public E set(int index, E element) {
Node<E> node = node(index);
E old = node.element;
node.element = element;
return old;
}
(3) add(int index, E element)
🍀 编写链表代码过程中,要注意边界测试
🍀 比如 index 为 0
、size – 0
、size
时
@Override
public void add(int index, E element) {
if (index == 0) { // 把元素插入到头节点的位置
first = new Node<>(element, first);
} else {
// 拿到【index - 1】位置的节点
Node<E> prev = node(index - 1);
prev.next = new Node<>(element, prev.next);
}
size++;
}
五、remove(int index)
@Override
public E remove(int index) {
Node<E> node = first;
if (index == 0) { // 删除头节点
first = node.next;
} else {
Node<E> prev = node(index - 1);
node = prev.next;
prev.next = node.next;
}
size--;
return node.element;
}
🌴 注意 index 等于 0 的时候
六、indexOf(E element)
🌴 遍历整个链表,取值比较
@Override
public int indexOf(E element) {
Node<E> moveNode = first;
if (element == null) {
for (int i = 0; i < size; i++) {
if (null == moveNode.element) return i;
moveNode = moveNode.next;
}
} else {
for (int i = 0; i < size; i++) {
if (element.equals(moveNode.element)) return i;
moveNode = moveNode.next;
}
}
return ELEMENT_NOT_FOUND;
}
七、三个 Leetcode 的练习题
(1) 删除链表中的节点
题目地址:https://leetcode.cn/problems/delete-node-in-a-linked-list/
🌼有一个单链表的 head
,我们想删除它其中的一个节点 node
🌼 给你一个需要删除的节点 node
🌼 你无法访问第一个节点 head
🌼 链表的所有值都是唯一的
🌼 给定的节点 node
不是链表中的最后一个节点
🌼 删除节点并不是指从内存中删除它
而是:
① 给定节点的值不应该存在于链表中
② 链表中的节点数应该减少 1
③node
前面的所有值顺序相同
④node
后面的所有值顺序相同
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public void deleteNode(ListNode node) {
node.val = node.next.val;
node.next = node.next.next;
}
}
🍃 用要被删除的节点(delNode)的下一个节点的值(delNode.next.val)覆盖 delNode 的值
🍃 delNode 的 next 指向 delNode 的 next 的 next
(2) 反转一个链表
https://leetcode-cn.com/problems/reverse-linked-list/
① 递归反转一个链表
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
}
② 迭代反转一个链表
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode newHead = null;
while (head != null) {
ListNode tmp = head.next;
head.next = newHead;
newHead = head;
head = tmp;
}
return newHead;
}
}
(3) 判断一个链表是否有环(快慢指针)
https://leetcode.cn/problems/linked-list-cycle/
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) return false;
ListNode slow = head;
ListNode fast = head.next;
while (slow != fast) {
if (fast == null || fast.next == null) return false;
slow = slow.next;
fast = fast.next.next;
}
return true;
}
}
完整代码:https://gitee.com/zgq666good/datastructureandalgorithm.git
如有错误请不吝赐教