7.单链表的六大解题套路
作者:互联网
单链表的六大解题套路
合并两个有序链表
将两个升序链表合并为一个新的升序链表并返回,新链表是通过拼接给定的两个链表的所有节点组成的
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(-1),p = dummy;//虚拟头结点
ListNode p1=l1,p2=l2;
while(p1!=null&&p2!=null){//比较p1和p2两个指针
//将值较小的节点接到p指针
if(p1.val>p2.val){
p.next=p2;
p2=p2.next;
}else{
p.next=p1;
p1=p1.next;
}
p=p.next;//p指针不断前进
}
if(p1!=null){
p.next=p1;
}
if(p2!=null){
p.next=p2;
}
return dummy.next;
}
while循环每次比较p1和p2的大小,把较小的节点接到结果链表上
链表算法题,很常见的【虚拟头结点】技巧,就是dummy节点,有了这个占位符,可以避免处理空指针的情况,降低代码复杂性
合并k个有序链表?
逻辑类似合并两个有序链表,难点在于,如何快速得到 k 个节点中的最小节点,接到结果链表上?
用到优先级队列(二叉堆)这种数据结构,把链表节点放入一个最小堆,就可以每次获得 k 个节点中的最小节点
public ListNode mergeKLists(ListNode[] lists) {
if(lists.length == 0)return null;
ListNode dummy = new ListNode(-1), p = dummy;//虚拟头结点
//优先级队列,最小堆?
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();//获取最小节点,接到结果链表中
p.next = node;
if(node.next!=null){
pq.add(node.next);
}
p=p.next;//p指针不断前进
}
return dummy.next;
}
优先队列 pq 中的元素个数最多是k,所以一次 poll或者 add方法的时间复杂度是O(logk)所有的链表节点都会被加入和弹出 pq,所以算法整体的算法复杂度是O(Nlogk),其中 k 是链表的条数,N是这些链表的节点总数
单链表的倒数第k个节点
从前往后寻找单链表第 k 个节点很简单,一个for循环遍历过去就找到了
让快指针指向链表头结点 head,先走 k 步
再用一个慢指针指向链表头结点head
让快慢指针同时向前走,快指针走到链表末尾的空指针,走了n-k步,那慢指针也走了n-k步,也就是链表的倒数第k个节点。
//返回链表的倒数第 k 个节点
ListNode findFromEnd(ListNode head,int k){
ListNode p1 = head, p2 = head;
for(int i=0; i < k;i++){
p1 = p1.next;
}
while(p1 != null){
p2 = p2.next;
p1 = p1.next;.
}
return p2;
}
无论遍历一次链表和遍历两次链表的时间复杂度都是O(N),但是上述算法更有技巧性
删除链表的倒数第N个结点
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode fast,slow;
fast =slow = head;
while(n-- > 0){
fast = fast.next;
}
if(fast == null){
return head.next;
}
while(fast!=null && fast.next != null){
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return head;
}
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode x = findFromEnd(dummy,n+1);
x.next = x.next.next;
return dummy.next;
}
private ListNode findFromEnd(ListNode head,int k){
ListNode p1 =head,p2 =head;
for(int i =0;i< k;i++){
p1= p1.next;
}
while(p1!=null){
p1= p1.next;
p2=p2.next;
}
return p2;
}
单链表的中点
解决这个问题的关键是,通过某些方式,让p1和p2能够同时到达相交节点
让两个指针分别遍历两个链表,相当于【逻辑上】两条链表接在一起,可以让两个指针,同时进入公共部分,也就是同时到达相交节点。
如果没有相交点,相当于c1节点是null空指针,可以正确返回null
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode p1 =headA,p2 = headB;
while(p1!=p2){
if(p1 == null) p1 = headB;
else p1 = p1.next;
if(p2 == null)p2 = headA;
else p2 = p2.next;
}
return p1;
}
标签:p2,head,单链,ListNode,套路,p1,next,链表,解题 来源: https://www.cnblogs.com/autumnmoonming/p/16282440.html