双指针技巧秒杀七道链表题目(1)

双指针技巧秒杀七道链表题目

对于单链表相关的题目,双指针的运用是非常广泛的

例如:

1、合并两个有序链表

2、合并 k 个有序链表

3、寻找单链表的倒数第 k 个节点

4、寻找单链表的中点

5、判断单链表是否包含环并找出环起点

6、判断两个单链表是否相交并找出交点

合并两个有序链表

最基本的链表技巧,力扣第 21 题「 合并两个有序链表

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
         // 虚拟头结点
        ListNode* dummy =  new ListNode(-1);
        ListNode* p=dummy;
        ListNode* p1=list1;
        ListNode* p2=list2;
        while(p1!=nullptr&&p2!=nullptr){
            if(p1->val>p2->val){
                p->next=p2;
                p2=p2->next;
            }else{
                p->next=p1;
                p1=p1->next;
            }
            p=p->next;
        }
        if(p1!=nullptr){
            p->next=p1;
        }
        if(p2!=nullptr){
            p->next=p2;
        }
        return dummy->next;
    }
};

代码中还用到一个链表的算法题中是很常见的「虚拟头结点」技巧,也就是 dummy 节点。如果不使用 dummy 虚拟节点,代码会复杂很多,而有了 dummy 节点这个占位符,可以避免处理空指针的情况,降低代码的复杂性。

合并 k 个有序链表

我的思路:让链表两两合并,类似合并两个链表。

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* p1,ListNode* p2){
        if((!p1)||(!p2)) return p1?p1:p2;
        ListNode* dummy=new ListNode();
        ListNode* p=dummy;
        ListNode* p3=p1;
        ListNode* p4=p2;
        while(p3!=nullptr&&p4!=nullptr){
            if(p3->val>p4->val){
                p->next=p4;
                p4=p4->next;
            }else{
                p->next=p3;
                p3=p3->next;
            }
            p=p->next;
        }
        p->next=p3?p3:p4;
        return dummy->next;
    }

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode* p=nullptr;
        for(int i=0;i<lists.size();i++){
            p=mergeTwoLists(p,lists[i]);
        }
        return p;
    }
};

但是这样的时间复杂度与空间复杂度并不理想,因此可以考虑使用优先级队列(二叉堆)这种数据结构,把链表节点放入一个最小堆,就可以每次获得 k 个节点中的最小节点

C++代码:

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        //lambda
        //因为lambda的类型是由编译器自动解析的,所以你不需要考虑像参数的类型是函数  指针或函数之类的事情。

        //无论如何,decltype(...)都会解析为可调用的对象。
        //lambda 二叉堆每次top放最小的 放进来的是b,
        auto compare=[](ListNode* a,ListNode* b){
            return a->val>=b->val;
        };
        
        //使用优先级队列(二叉堆)实现
        priority_queue<ListNode*,vector<ListNode*>,decltype(compare)>pq(compare);
        //虚拟头节点
        ListNode* dummy=new ListNode(-1);
        ListNode* p=dummy;
        //将k个链表的头节点加入最小堆
        for(ListNode* head:lists){
            if(head!=nullptr){
                pq.push(head);
            }
        }
        while(!pq.empty()){
            //获得最小节点,接到结果链表中
            ListNode* node=pq.top();
            pq.pop();
            p->next=node;
            if(node->next!=nullptr){
                pq.push(node->next);
            }
            //p指针不断前进
            p=p->next;
        }
        return dummy->next;
    }
};
posted @ 2022-04-03 11:56  BailanZ  阅读(29)  评论(0编辑  收藏  举报