链表相关问题

博客围绕链表相关问题展开,包括求链表中倒数第k个结点,介绍了设置双指针的巧妙方法;还涉及合并两个排序链表,给出非递归和递归两种思路;以及合并K个排序链表,对比了顺序遍历和分治两种方法,并给出分治思路下的C++代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链表中的倒数第k个结点

输入一个链表,输出该链表中倒数第k个结点。

思路

在牛客网上看到一个很巧妙的方法。设置两个指针p,q。其中q先移动k-1步,然后p和q一起往后移,直至q移动到链表末尾。此时p指向的元素即为倒数第k个元素。

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        ListNode* p=pListHead;
        ListNode* q=pListHead;
        unsigned int count=0;
        while(p!=NULL&&count!=k-1){
            p=p->next;
            count++;
        }
        if(p==NULL) return NULL;
        else{
            while(p->next!=NULL){
                p=p->next;
                q=q->next;
            }
        }
        return q;
    }
};

合并两个排序的链表

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

思路1(非递归)

创建一个头结点t_head(方便返回)和一个pre指针,pre指针一开始指向头结点t_head。比较l1,l2此时指向的结点值的大小,比如此时l1->val<=l2->val,那么将l1接在pre的后面,l1后移,pre后移;l2同理。循环直到l1或l2为空,再把剩余元素接到pre后面,返回头结点t_head的下一个结点即可。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode* t_head=new ListNode(-1);
        ListNode* pre=t_head;
        while(l1!=NULL&&l2!=NULL){
            if(l1->val<=l2->val){
                pre->next=l1;
                l1=l1->next;
            }
            else{
                pre->next=l2;
                l2=l2->next;
            }
            pre=pre->next;
        }
        pre->next=l1==NULL?l2:l1;
        return t_head->next;
    }
};

思路2(递归)

按两列表当前结点的大小关系递归两列表,借助递归栈保存之前结点的递归顺序,在递归结束返回时,按之前递归的顺序返回结点指针,最终将两链表串为一个有序的链表。

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1==NULL) return pHead2;
        if(pHead2==NULL) return pHead1;
        if(pHead1->val>pHead2->val){
            pHead2->next=Merge(pHead1,pHead2->next);
            return pHead2;
        }
        else{
            pHead1->next=Merge(pHead1->next,pHead2);
            return pHead1;
        }
    }
};

合并K个排序链表

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-k-sorted-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

之前想的是顺序遍历数组中每两个链表,两两合并。这样时间复杂度有点大 O ( n k ) O(nk) O(nk)(其中 n n n为链表长度, k k k为链表数量)。
后来看了LeetCode的题解,可以利用分治的想法,每一次合并 k 2 \frac{k}{2} 2k个链表,过程如下图所示。时间复杂度为 O ( n l o g 2 k ) O(nlog_2k) O(nlog2k)(其中 n n n为链表长度, k k k为链表数量)。在这里插入图片描述

代码(c++)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* merge(ListNode* l1,ListNode* l2){
        ListNode* head=new ListNode(-1);
        ListNode* pre=head;
        while(l1!=NULL&&l2!=NULL){
            if(l1->val<=l2->val){
                pre->next=l1;
                l1=l1->next;
            }
            else{
                pre->next=l2;
                l2=l2->next;
            }
            pre=pre->next;
        }
        pre->next=l1==NULL?l2:l1;
        return head->next;
    }
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if(lists.size()==0) return NULL;
        if(lists.size()==1) return lists[0];
        int interval=1;
        while(interval<lists.size()){
            for(int i=0;i<lists.size()-interval;i+=interval*2){
                lists[i]=merge(lists[i],lists[i+interval]);
            }
            interval*=2;
        }
        return lists[0];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值