1、合并两个有序链表
-
问题:# 合并两个有序链表
-
思路: 遍历链表,定义两个指针结点分别指向两个链表,并比较大小,小的加入新的链表中,最后,将不为空的链表部分放在后边。
-
代码:
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode head=new ListNode(-1);
ListNode cur=head;
while(list1!=null&&list2!=null){
if(list1.val<list2.val){
cur.next=list1;
list1=list1.next;
}else{
cur.next=list2;
list2=list2.next;
}
cur=cur.next;
}
cur.next=list1==null?list2:list1;
return head.next;
}
}
2、分隔链表
- 问题:#分隔链表
- 思路: 维护两个链表 small和 large即可,small链表按顺序存储所有小于 x 的节点,large链表按顺序存储所有大于等于 x 的节点。遍历完原链表后,我们只要将 small 链表尾节点指向 large 链表的头节点即能完成对链表的分隔。
- 代码
class Solution {
public ListNode partition(ListNode head, int x) {
ListNode small = new ListNode(0);
ListNode smallHead = small;
ListNode large = new ListNode(0);
ListNode largeHead = large;
while (head != null) {
if (head.val < x) {
small.next = head;
small = small.next;
} else {
large.next = head;
large = large.next;
}
head = head.next;
}
large.next = null;
small.next = largeHead.next;
return smallHead.next;
}
}
3、合并K个有序链表
- 问题:合并K个有序链表
- 思路:使用优先队列(二叉堆)这种数据结构,把链表结点放入一个最小堆,这样每次获得K个结点中的最小结点。
- 代码
初始化最小堆/最大堆
PriorityQueue<Integer> pq1=new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2; // 小顶堆, return 前减后(o1-o2);大顶堆, return 后减前(o2-o1);
};
});
public class MergeKLinkedList {
public ListNode mergeKLinkedList(ListNode[] lists){
ListNode dummy=new ListNode(-1);
ListNode cur=dummy;
if(lists.length==0) return null;
PriorityQueue<ListNode> pq=new PriorityQueue<>(lists.length,(a,b)->(a.val-b.val));
// 将K个有序链表的头结点加入最小堆
for(ListNode head:lists){
if(head!=null){
pq.add(head);
}
}
while(!pq.isEmpty()){
//每次取出最小结点加入新链表结构
ListNode node=pq.poll();
cur.next=node;
if(node.next!=null){
pq.add(node.next);
}
cur=cur.next;
}
return dummy.next;
}
}
4、单链表的倒数第K个结点
- 问题:单链表的倒数第K个结点
- 思路:怎么一次遍历获取到倒数第K个结点?双指针。 指针p1先走k步,指针p2再与p1同步走,当p1为null时,p2正好指向倒数第K个结点。
- 代码
// 主函数
public ListNode removeNthFromEnd(ListNode head, int n) {
// 虚拟头结点
ListNode dummy = new ListNode(-1);
dummy.next = head;
// 删除倒数第 n 个,要先找倒数第 n + 1 个节点
ListNode x = findFromEnd(dummy, n + 1);
// 删掉倒数第 n 个节点
x.next = x.next.next;
return dummy.next;
}
// 返回链表的倒数第 k 个节点
ListNode findFromEnd(ListNode head, int k) {
ListNode p1 = head;
// p1 先走 k 步
for (int i = 0; i < k; i++) {
p1 = p1.next;
}
ListNode p2 = head;
// p1 和 p2 同时走 n - k 步
while (p1 != null) {
p2 = p2.next;
p1 = p1.next;
}
// p2 现在指向第 n - k + 1 个节点,即倒数第 k 个节点
return p2;
}
5、判断链表是否包含环
- 问题:寻找链表中点或判断链表是否包含环
- 思路:快慢指针,每当慢指针
slow前进一步,快指针fast就前进两步。
如果 fast 最终遇到空指针,说明链表中没有环;如果 fast 最终和 slow 相遇,那肯定是 fast 超过了 slow 一圈,说明链表中含有环。
- 代码
boolean hasCycle(ListNode head) {
// 快慢指针初始化指向 head
ListNode slow = head, fast = head;
// 快指针走到末尾时停止
while (fast != null && fast.next != null) {
// 慢指针走一步,快指针走两步
slow = slow.next;
fast = fast.next.next;
// 快慢指针相遇,说明含有环
if (slow == fast) {
return true;
}
}
// 不包含环
return false;
}