数据结构之链表
- 一、单链表
- 二、链表双指针
- 三、经典问题
-
- 1、反转一个单链表([LeetCode 206](https://leetcode-cn.com/problems/reverse-linked-list/))
- 2、移除链表元素([LeetCode 203](https://leetcode-cn.com/problems/remove-linked-list-elements/))
- 3、奇偶链表([LeetCode 328](https://leetcode-cn.com/problems/odd-even-linked-list/))
- 4、回文链表 ([LeetCode 234](https://leetcode-cn.com/problems/palindrome-linked-list/))
- 四、双链表
- 五、小结
-
- 1、合并两个有序链表 ([LeetCode 21](https://leetcode-cn.com/problems/merge-two-sorted-lists/))
- 2、两数相加 ([LeetCode 2](https://leetcode-cn.com/problems/add-two-numbers/))
- 3、扁平化多级双向链表 ([LeetCode 430](https://leetcode-cn.com/problems/flatten-a-multilevel-doubly-linked-list/))
- 4、复制带随机指针的链表 ([LeetCode 138](https://leetcode-cn.com/problems/copy-list-with-random-pointer/))
- 5、旋转链表 ([LeetCode 61](https://leetcode-cn.com/problems/rotate-list/))
【写在前面】所有LeetCode题的题目都去原网站看
- 单链表
- 双链表
一、单链表
1、添加操作
- ① 使用给定值初始化新节点cur
- ② 将cur节点的next链接到prev的下一个节点later
- ③ 将prev节点的next链接到cur节点
2、开头添加节点
- ① 初始化一个新的节点cur
- ② 将cur的next链接到head
- ③ 将cur更改为head
3、删除节点操作
- ① 找到cur节点的上一个节点prev及其下一个节点later
- ② 将prev的next节点链接到later
4、删除开头节点
- 简单的,直接将第二个节点指定为head
5、设计单链表(LeetCode 707)
// 链表类,定义构造方法
public class ListNode{
int val;
ListNode next;
ListNode(int x){
val = x;
}
}
// 设计单链表类
Class MyLinkedList{
int size;
ListNode head;
public MyLinkedList(){
size = 0;
head = new ListNode(0);
}
// 获得索引指定值
public int get(int index){
if(index < 0 || index >= size) return -1;
ListNode cur = head;
for(int i = 0; i < index; ++i) cur = cur.next;
return cur.val;
}
// 添加头节点(索引为0)
public void addAtHead(int val){
addAtIndex(0, val);
}
// 添加尾节点(索引为总长度)
public void addAtTail(int val){
addAtIndex(size, val);
}
// 根据索引,添加节点
public void addAtIndex(int index, int val){
if(index < 0) index = 0;
if(index > size) return;
++size;
// 创建前节点
ListNode pred = head;
// 将前节点移动到索引指定节点
for(int i = 0; i < index; ++i) pred = pred.next;
// 根据值,创建要添加的节点
ListNode toAdd = new ListNode(val);
// 首先,将toAdd节点链接到pred节点的下一个节点
toAdd.next = pred.next;
// 最后,将pred节点链接到toAdd节点
pred.next = toAdd;
}
// 根据索引,删除节点
public void deleteAtIndex(int index){
if(index < 0 || index >= size) return;
size--;
ListNode pred = head;
for(int i = 0; i < index; ++i) pred = pred.next;
// 直接将当前节点链接到下个节点的下个节点
pred.next = pred.next.next;
}
}
二、链表双指针
1、环形链表
方法1——哈希表
public boolean hasCycle(ListNode head){
// 创建链表节点集合
Set<ListNode> seen = new HashSet<ListNode>();
while(head != null){
if(!seen.add(head)){
return true;
}
head = head.next;
}
return false;
}
方法2——快慢指针(改进)
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;
}
2、相交链表(LeetCode 160)
方法1——暴力法
- 对链表A中的每一个节点ai,遍历链表B并检查是否有
bj = ai
- 时间复杂度:O(m*n)
- 空间复杂度:O(1)
方法2——哈希表法
- 将链表A每个节点存入哈希表,再检查链表B
- 时间复杂度:O(m+n)
- 空间复杂度:O(n)或O(n)
方案3——双指针法
- 将链表A与链表B互相接到后面,形成A-B链表、形成B-A链表,利用双指针遍历
- 时间复杂度:O(m+n)
- 空间复杂度:O(1)
public ListNode getIntersectionNode(ListNode headA, ListNode headB){
if(headA == null || headB == null) return null;
// 创建链表A、B的'指针'节点
ListNode pA = headA, pB = headB