10道题熟练掌握并运用链表结构算法

目录

1.牛客BM3 链表中的节点每k个一组翻转

2.BM4 合并两个排序的链表

3.BM5 合并k个已排序的链表

4.BM6 判断链表中是否有环

5.BM7 链表中环的入口结点

6.BM8 链表中倒数最后k个结点

7.BM9 删除链表的倒数第n个节点

8.BM10 两个链表的第一个公共结点

9.BM11 链表相加(二)

10.BM12 单链表的排序


1.牛客BM3 链表中的节点每k个一组翻转

题目:将给出的链表中的节点每 k 个一组翻转,返回翻转后的链表,如果链表中的节点数不是 k 的倍数,将最后剩下的节点保持原样,你不能更改节点中的值,只能更改节点本身。

数据范围: \ 0 \le n \le 2000 0≤n≤2000 , 1 \le k \le 20001≤k≤2000 ,链表中每个元素都满足 0 \le val \le 10000≤val≤1000
要求空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

例如:

给定的链表是 1\to2\to3\to4\to51→2→3→4→5

对于 k = 2k=2 , 你应该返回 2\to 1\to 4\to 3\to 52→1→4→3→5

对于 k = 3k=3 , 你应该返回 3\to2 \to1 \to 4\to 53→2→1→4→5

解:这道题用递归做效率是最高的,但不好理解。  

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param head ListNode类 
 * @param k int整型 
 * @return ListNode类
 */

struct ListNode*reverse (struct ListNode*phead,int k)
{
    struct ListNode*pre ,*cur ,*nxt;//创建3个新结点
    pre=NULL;cur=phead;nxt=phead->next;//pre为头结点
    while (k--) //在k区间内反转
    {
        cur->next=pre;
        pre=cur;
        cur=nxt;
        nxt=cur->next;
    }
    return  pre;//返回反转后的头结点
}

struct ListNode* reverseKGroup(struct ListNode* head, int k ) {
    // write code here
    struct ListNode* reverseHead=(struct ListNode*)malloc(sizeof(struct ListNode));
    reverseHead->next=head;
    struct ListNode *revHead,*revEnd;
    int count=0;
    revHead=reverseHead,revEnd=reverseHead->next;
    if(k==1)return reverseHead->next;
    while(revEnd!=NULL)
    {
        revEnd=revEnd->next;
        if(++count==k)//计数count等于k则开始反转
        {
            revHead->next=reverse(revHead->next, k);
            while(count--)
            {
                revHead=revHead->next;
            }
            revHead->next=revEnd;
            count=0;
        }
    }
    return reverseHead->next;
}

2.BM4 合并两个排序的链表

题目:输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。

数据范围: 0 \le n \le 10000≤n≤1000,-1000 \le 节点值 \le 1000−1000≤节点值≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

如输入{1,3,5},{2,4,6}时,合并后的链表为{1,2,3,4,5,6},所以对应的输出为{1,2,3,4,5,6}

解:这道题和第一道比就比较简单了,直接笨想用两个指针分别指向两个链表然后比较大小,返回到新链中就可以了。

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */

/**
 * 
 * @param pHead1 ListNode类 
 * @param pHead2 ListNode类 
 * @return ListNode类
 */
struct ListNode* Merge(struct ListNode* pHead1, struct ListNode* pHead2 ) {
    // write code here
    struct ListNode*p1=pHead1,*p2=pHead2,*res;
    struct ListNode*cur=(struct ListNode*)malloc(sizeof(struct ListNode));
    cur->val=-1;
    res=cur;
    while ((p1!=NULL&&p2!=NULL)) {
        if (p1->val<p2->val) {
            res->next=p1;
            res=res->next;
            p1=p1->next;
        }
        else {
        res->next=p2;
        res=res->next;
        p2=p2->next;
        }
    }
    if ((p1!=NULL)) {
        res->next=p1;
    }
    if ((p2!=NULL)) {
        res->next=p2;
    }
    return  cur->next;
}

 向上面那么做,理解起来非常容易,但也是入门的方法;

人写迭代,神写递归,接下来看看递归的程序怎么做

这道题给的函数功能就是合并两个排序的链表,那我们可以用这个函数本身来递归。

(恍然大悟,原来如此啊!迭代看着递归,表情惊恐:这就是神吗?竟恐怖如斯,自己递归自己。 )呵呵呵呵,冷笑话~这道题其实递归和迭代时间复杂度是一样的,空间复杂度还不如迭代。

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */

/**
 * 
 * @param pHead1 ListNode类 
 * @param pHead2 ListNode类 
 * @return ListNode类
 */
struct ListNode* Merge(struct ListNode* pHead1, struct ListNode* pHead2 ) {
    // write code here
    if (!pHead1) return pHead2;
    if (!pHead2) return pHead1;
    if (pHead1->val <= pHead2->val) {
         pHead1->next = Merge(pHead1->next, pHead2);
         return pHead1;
    }
    else {
         pHead2->next = Merge(pHead1, pHead2->next);
         return pHead2;
    }
}

3.BM5 合并k个已排序的链表

题目:合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。

数据范围:节点总数 0 \le n \le 50000≤n≤5000,每个节点的val满足 |val| <= 1000∣val∣<=1000

要求:时间复杂度 O(nlogn)O(nlogn)

示例1

输入:

[{1,2,3},{4,5,6,7}]

复制返回值:

{1,2,3,4,5,6,7}

解:一样的做法,但是复杂了好多, free函数释放内存空间

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */

/**
 * 
 * @param lists ListNode类一维数组 
 * @param listsLen int lists数组长度
 * @return ListNode类
 */
struct ListNode* merger(struct ListNode** lists,int listLen,int left,int right){
    if(left == right) {
        return lists[left];
    }else{
        int mid = left + (right - left) / 2;
        struct ListNode* leftHead = merger(lists,listLen,left,mid);
        struct ListNode* rightHead = merger(lists,listLen,mid + 1,right);
        struct ListNode* newHead = (struct ListNode*)malloc(sizeof(struct ListNode));
        struct ListNode* pre = newHead, *p1 = leftHead, *p2 = rightHead;
        while(p1 && p2){
            if(p1->val < p2->val){
                pre->next = p1;
                pre = p1;
                p1 = p1->next;
            }else{
                pre->next = p2;
                pre = p2;
                p2 = p2->next;
            }
        }
        if(p1){
            pre->next = p1;
        }else if(p2){
            pre->next = p2;
        }
        pre = newHead->next;
        free(newHead);
        return pre;
    }
}
struct ListNode* mergeKLists(struct ListNode** lists, int listsLen ) {
    // write code here
    if(listsLen == 0) return NULL;
    return merger(lists,listsLen,0,listsLen - 1);
}

4.BM6 判断链表中是否有环

题目:判断给定的链表中是否有环。如果有环则返回true,否则返回false。

数据范围:链表长度 0 \le n \le 100000≤n≤10000,链表中任意节点的值满足 |val| <= 100000∣val∣<=100000

要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

输入分为两部分,第一部分为链表,第二部分代表是否有环,然后将组成的head头结点传入到函数里面。-1代表无环,其它的数字代表有环,这些参数解释仅仅是为了方便读者自测调试。实际在编程时读入的是链表的头节点。

解:这道题超简单,用双指针一快一慢循环向前,相遇的时候就是有环。 

#include <stdbool.h>
/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */

/**
 * 
 * @param head ListNode类 
 * @return bool布尔型
 */
bool hasCycle(struct ListNode* head ) {
    // write code here
    struct ListNode *fast,*slow;
    fast=head;
    slow=head;
    while(fast&&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
        {
            return true;
        }
    }
    return false;
}

5.BM7 链表中环的入口结点

题目:给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。

数据范围: n\le10000n≤10000,1<=结点值<=100001<=结点值<=10000

要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

解:大风车呀~嘎吱,嘎吱的转~转就完了,兄弟们!

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param pHead ListNode类 
 * @return ListNode类
 */
struct ListNode* EntryNodeOfLoop(struct ListNode* pHead ) {
    // write code here
    struct ListNode *fast,*slow;
    fast=pHead;
    slow=pHead;
    while(fast!=NULL&&fast->next!=NULL)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)break;
    }
    if(fast==NULL||fast->next==NULL)return NULL;
    fast=pHead;//重新指向头结点
    while(fast!=slow)
    {
        fast=fast->next;
        slow=slow->next;
    }
    return fast;
}

6.BM8 链表中倒数最后k个结点

题目:输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。

如果该链表长度小于k,请返回一个长度为 0 的链表。

数据范围:0 \leq n \leq 10^50≤n≤105,0 \leq a_i \leq 10^90≤ai​≤109,0 \leq k \leq 10^90≤k≤109

要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)

进阶:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

示例1

输入:

{1,2,3,4,5},2

复制返回值:

{4,5}

复制说明:返回倒数第2个节点4,系统会打印后面所有的节点来比较。

解:还是用双指针。

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param pHead ListNode类 
 * @param k int整型 
 * @return ListNode类
 */
struct ListNode* FindKthToTail(struct ListNode* pHead, int k ) {
    // write code here
    if(pHead==NULL)return NULL;
    struct ListNode*a,*b;
    a=b=pHead;
    int i=0;
    while(a!=NULL)
    {
        if(i<k)
        {
            i++;
        }
        else if(i==k)
        {
            b=b->next;
        }
        a=a->next;
    }
    if(i==k)
    {
        return b;
    }
    return NULL;
}

7.BM9 删除链表的倒数第n个节点

题目:给定一个链表,删除链表的倒数第 n 个节点并返回链表的头指针
例如,

给出的链表为: 1\to 2\to 3\to 4\to 51→2→3→4→5, n= 2n=2.
删除了链表的倒数第 nn 个节点之后,链表变为1\to 2\to 3\to 51→2→3→5.

数据范围: 链表长度 0\le n \le 10000≤n≤1000,链表中任意节点的值满足 0 \le val \le 1000≤val≤100

要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
备注:题目保证 nn 一定是有效的

 解:这道题本质上和上到题区别不大;

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param head ListNode类 
 * @param n int整型 
 * @return ListNode类
 */
struct ListNode* removeNthFromEnd(struct ListNode* head, int n ) {
    // write code here
    if(head==NULL)return NULL;
    struct ListNode*fast,*slow;
    fast=slow=head;
    for(int i=0;i<n;i++)
    {
        fast=fast->next;
    }
    if(fast==NULL)return head->next;
    while(fast->next!=NULL)
    {
        fast=fast->next;
        slow=slow->next;
    }
    slow->next=slow->next->next;
    return head;
}

8.BM10 两个链表的第一个公共结点

题目:输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

数据范围: n \le 1000n≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

例如,输入{1,2,3},{4,5},{6,7}时,两个无环的单向链表的结构如下图所示:

可以看到它们的第一个公共结点的结点值为6,所以返回结点值为6的结点。

解:这道题用两个指针相同速度移动,移动到空时,在到换链表移动,相等则是公共结点。 

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */

/**
 * 
 * @param pHead1 ListNode类 
 * @param pHead2 ListNode类 
 * @return ListNode类
 */
struct ListNode* FindFirstCommonNode(struct ListNode* pHead1, struct ListNode* pHead2 ) {
    // write code here
    struct ListNode*l1=pHead1,*l2=pHead2;
    while(l1!=l2)
    {
        l1=(l1==NULL)?pHead2:l1->next;
        l2=(l2==NULL)?pHead1:l2->next;
    }
    return l1;
}

9.BM11 链表相加(二)

题目:假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。

给定两个这种链表,请生成代表两个整数相加值的结果链表。

数据范围:0 \le n,m \le 10000000≤n,m≤1000000,链表任意值 0 \le val \le 90≤val≤9
要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)

例如:链表 1 为 9->3->7,链表 2 为 6->3,最后生成新的结果链表为 1->0->0->0。

 解:这道我们可以先反转链表,然后在相加,进位。很烦

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */
struct mylist{
    int cnt;
    struct ListNode *list;
};

struct ListNode *swaplist(struct ListNode *head, int *cnt)
{
    struct ListNode *cur1=head;
    struct ListNode *pre1=NULL, *nxt1=NULL;
    int count = 0;
    while(cur1)
    {
        nxt1 = cur1->next;
        cur1->next = pre1;
        pre1 = cur1;
        cur1 = nxt1;
        count++;
    }
    *cnt = count;
    return pre1;
}

/**
 * 
 * @param head1 ListNode类 
 * @param head2 ListNode类 
 * @return ListNode类
 */
struct ListNode* addInList(struct ListNode* head1, struct ListNode* head2 ) {
    // write code here
    struct mylist mylist1,mylist2;
    char flag = 0;
    int oldval=0;
    
    if(NULL == (head1 && head2))
        return head1?head1:head2;
    //获取到反转后的链表和链表长度,并保存到mylist中
    mylist1.list = swaplist(head1, &mylist1.cnt);
    mylist2.list = swaplist(head2, &mylist2.cnt);
    
    struct ListNode *pre=NULL, *nxt=NULL;
    struct ListNode *larger, *little;
    if(mylist1.cnt > mylist2.cnt){
        larger = mylist1.list;
        little = mylist2.list;
    }else{
        larger = mylist2.list;
        little = mylist1.list;
    }
    //遍历长链表
    while(larger)
    {
        if(little)//锻炼表没到尾
        {
            oldval = larger->val;
            larger->val = (little->val + oldval + flag)%10;
            //进位flag置1 否则清0
            flag = (little->val + oldval + flag >= 10) ? 1 : 0;
            little = little->next;
        }else if(flag){
            oldval = larger->val;
            larger->val = (oldval + 1)%10;
            flag = oldval+1 >= 10? 1 : 0;
        }
        
        nxt = larger->next;
        larger->next = pre;
        pre = larger;
        larger = nxt;
    }

    if(flag){
        struct ListNode *newhead = (struct ListNode *)malloc(sizeof(struct ListNode));
        newhead->next = pre;
        newhead->val = 1;
        return newhead;
    }else{
        return pre;
    }
}

10.BM12 单链表的排序

题目:给定一个节点数为n的无序单链表,对其按升序排序。

数据范围:0 < n \le 1000000<n≤100000,保证节点权值在[-10^9,10^9][−109,109]之内。

要求:空间复杂度 O(n)O(n),时间复杂度 O(nlogn)O(nlogn)

示例1

输入:

[1,3,2,4,5]

复制返回值:

{1,2,3,4,5}

 解:双指针比较大小排序

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */

/**
 * 
 * @param head ListNode类 the head node
 * @return ListNode类
 */
struct ListNode* sortInList(struct ListNode* head ) {
    // write code here
    if(head==NULL||head->next==NULL)
        return head;
    struct ListNode *fast=head;
    struct ListNode *slow=head;
    struct ListNode *brk=NULL;
    while(fast&&fast->next)
    {
        fast=fast->next->next;
        if(fast==NULL||fast->next==NULL)
            brk=slow;
        slow=slow->next;
    }
    brk->next=NULL;
    struct ListNode*head1=sortInList(head);
    struct ListNode*head2=sortInList(slow);
    struct ListNode dummy;
    struct ListNode*cur=&dummy;
    while(head1||head2)
    {
        if(head1==NULL||(head1!=NULL&&head2!=NULL&&head1->val>head2->val))
        {
            cur->next=head2;
            head2=head2->next;
            cur=cur->next;
        }
        else
        {
            cur->next=head1;
            head1=head1->next;
            cur=cur->next;
        }
    }
    return dummy.next;
}

猜你喜欢

转载自blog.csdn.net/weixin_69250798/article/details/128683354
今日推荐