链表问题
203. 移除链表元素
- 链表删除操作: 使用dummyHead, 遍历cur.next值是否等于val, 再执行删除操作, 使用while (cur.next != null) 循环
- 递归方法: 递归可以方便的进行重建链表, 重建过程中删除等于val就满足了条件。
//增加虚拟头节点: 使得头节点与其他节点处理方式一致化
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode(0, head);
//开始遍历操作: 链表中,定义cur节点进行遍历操作, pre,next等可以作为前后节点
ListNode cur = dummyHead;
while (cur.next != null) {
if (cur.next.val == val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return dummyHead.next;
}
}
//直接处理: 头节点与其他节点分开处理
class Solution2 {
public ListNode removeElements(ListNode head, int val) {
//在链表循环中: p!=null 往往作为中止条件
while (head != null && head.val == val) {
head = head.next;
}
ListNode cur = head;
if (cur == null) {
return null;
}
while (cur.next != null) {
if (cur.next.val == val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return head;
}
}
//递归: 链表具有递归性质, 可以使用递归处理,递归的代码顺序: 递归出口-> 递归逻辑-> 递归返回, 递归的编码顺序: 递归逻辑-> 递归返回-> 递归出口
class Solution3 {
public ListNode removeElements(ListNode head, int val) {
//递归出口
if (head == null) {
return null;
}
//递归逻辑
head.next = removeElements(head.next, val);
//递归返回
return head.val == val ? head.next : head;
}
}
设计链表
class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
}
ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
class MyLinkedList {
ListNode dummyHead;//虚拟头节点
int size;//链表大小
/**
* Initialize your data structure here.
*/
public MyLinkedList() {
this.dummyHead = new ListNode(0);
this.size = 0;
}
/**
* Get the value of the index-th node in the linked list. If the index is invalid, return -1.
*/
public int get(int index) {
if (index < 0 || index >= size) {
return -1;
}
ListNode cur = this.dummyHead.next;
while (index > 0) {
cur = cur.next;
index--;
}
return cur.val;
}
/**
* Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.
*/
public void addAtHead(int val) {
addAtIndex(0, val);
}
/**
* Append a node of value val to the last element of the linked list.
*/
public void addAtTail(int val) {
addAtIndex(size, val);
}
/**
* Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted.
*/
public void addAtIndex(int index, int val) {
if (index < 0 || index > size) {
return;
}
ListNode newNode = new ListNode(val);
ListNode cur = this.dummyHead;
while (index > 0) {
cur = cur.next;
index--;
}
newNode.next = cur.next;
cur.next = newNode;
size++;
}
/**
* Delete the index-th node in the linked list, if the index is valid.
*/
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
ListNode cur = this.dummyHead;
while (index > 0) {
cur = cur.next;
index--;
}
cur.next = cur.next.next;
size--;
}
}
/*
Your MyLinkedList object will be instantiated and called as such:
MyLinkedList obj = new MyLinkedList();
int param_1 = obj.get(index);
obj.addAtHead(val);
obj.addAtTail(val);
obj.addAtIndex(index,val);
obj.deleteAtIndex(index);
*/
206.反转链表
206.反转链表
使用dummyHead,需要pre 和 cur同时移动
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
while (cur != null) {
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
两两交换链表
- 使用dummyHead, 分三步控制指向变化
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummyHead = new ListNode(0, head);
ListNode cur = dummyHead;
while (cur.next != null && cur.next.next != null) {
ListNode temp = cur.next;
ListNode temp2 = cur.next.next.next;
cur.next = cur.next.next;
cur.next.next = temp;
cur.next.next.next = temp2;
cur = cur.next.next;
}
return dummyHead.next;
}
}
删除倒数第N个节点
- 快慢指针间隔N进行同步移动, 快指针移动到null时, 删除慢指针位置的下一个位置.
// 快慢指针保持距离n同步移动: 0-> 1-> 2-> 3->null
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
//0<n<=size
ListNode dummyHead = new ListNode(0, head);
ListNode slowIndex = dummyHead, fastIndex = dummyHead;
//fastIndex向前走n步: 因为向前走n步slowIndex位置为要删除的位置,没有上一步的记录-> 改为:向前走n+1步即n>0改为n>=0
while (n >= 0) {
fastIndex = fastIndex.next;
n--;
}
//保持距离同步移动到fastIndex到末尾
while (fastIndex != null) {
fastIndex = fastIndex.next;
slowIndex = slowIndex.next;
}
//slowIndex的下一个位置就是要删除的位置
slowIndex.next = slowIndex.next.next;
return dummyHead.next;
}
}
链表相交
// set解法: 遍历一个链表元素存入set中, 遍历另一个链表,元素逐个和set中对比, 相等则返回, 最后返回null
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
HashSet<ListNode> hashSet = new HashSet<>();
for (ListNode cur = headA; cur != null; cur = cur.next) {
hashSet.add(cur);
}
for (ListNode cur = headB; cur != null; cur = cur.next) {
if (hashSet.contains(cur)) {
return cur;
}
}
return null;
}
}
环形链表
- 判断是否有环: HashSet存储已遍历的节点, 后序遇到后则证明存在环, 并且是入口点
- 快慢指针, 快指针步长2, 慢指针步长1, 最后会在环内相交, 寻找入口的: 指针一从头遍历. 指针2从相交出遍历, 最后在入口点相交。
public class Solution {
public ListNode detectCycle(ListNode head) {
HashSet<ListNode> hashSet = new HashSet<>();
ListNode cur = head;
while (cur != null) {
hashSet.add(cur);
if (hashSet.contains(cur.next)) {
return cur.next;
} else {
cur = cur.next;
}
}
return null;
}
}