链表专题刷题讲解

本文介绍了如何在链表中实现两数相加的逆置法,以及涉及链表操作的题目,如两两交换节点、合并有序链表、合并K个升序链表、重排链表和K个一组翻转链表,通过算法思路和代码示例展示了解决这些问题的方法。
摘要由CSDN通过智能技术生成

今日为大家分享一波关于链表中的一些题目!希望读完本篇文章,读者们也可以尝试一下这些题目,也会加强大家对链表的认识!

445. 两数相加 II

1.算法思路:

通过题目的讲解,以及用例的分析,我们可以发现,链表是以高位向低位进行连接的!而我们的加法操作都是低位相加的,所以我们只需要进行将原链表进行逆置一下,然后进行相加,得到相加后的结构再逆置一下即可。看下图:

 所以我们的思路就很简单,逆置原链表然后相加再逆置即可!需要注意的是:我们需要用一个数字来统计我们的进位!当进位为0且俩链表都走到空时的时候结束即可!进行逆置链表的时候,我们使用头插法即可!

2.代码如下:

Node* reverse(Node* head)
    {
        Node *Head=new Node(0);
        Node*pre=Head;
        Node*cur=head;
        while(cur)
        {
            Node* next=cur->next;
            cur->next=pre;
            pre=cur;
            cur=next;
        }
        delete Head;
        return pre;
    }
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2)
    {
       
        Node*newhead1=reverse(l1);
        Node*newhead2=reverse(l2);

        Node*ret=new Node(0);
        Node*pre=ret;
        int add=0;  //代表的是进位!
        while(newhead1||newhead2||add)
        {
           if(newhead1)
           {
                add+=newhead1->val;
                newhead1=newhead1->next;
           }
           if(newhead2)
           {
                add+=newhead2->val;
                newhead2=newhead2->next;
           }

           pre->next=new Node(add%10);
        
            add/=10;
            pre=pre->next;
        }
        //首先实现的是逆置两个链表!

       return reverse(ret->next);
        
    }
};

做过本道题,我们就可以尝试一下这道两数相加的题目,这道题目我们不需要进行逆置,直接进行相加处理好进位即可!希望大家下来可以尝试做一下!

2. 两数相加


24. 两两交换链表中的节点

1.算法讲解:

对于链表的题目,我们大多都是需要加上哨兵位的,因为加上哨兵位可以大大的减少题目的难度,下面我通过画图给大家分析一波如何求解这道题!

我们发现经过一次交换需要改变三个指针的指向,防止后面因为顺序问题,导致节点更改错误,我们提前将需要进行更改的节点进行存储下来即可!

下面就是修改结点指针的逻辑!

pre->next=next;

next->next=cur;

cur->next=nnext; 

下一次我们再进行交换的时候,需要将cur节点进行更换到nnext上,pre更换成上一次的cur中,先将pre进行更改,再进行更改cur!这个顺序不能颠倒,一旦颠倒就会出错! 

这是偶数个节点的情况,当节点的个数为奇数个的时候,假设没有4这个节点,我们就只需要进行一次两两交换即可!所以执行交换的逻辑是cur和next都不为空才进行交换!一个为空就不需要进行交换!

2.代码:

下面我把代码贴在下面: 



 21. 合并两个有序链表

 1.算法讲解:

本题的解法就是首先设置一个哨兵位,然后将较小的元素进行尾插即可!本题难度不大,就不详细画图讲解了。

2.代码

class Solution {
public:
    typedef ListNode Node;
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2)
    {
        //设置一个哨兵位   然后两个节点比较大小,小的直接插入即可!

        Node*newhead=new Node(0);
        Node*pre=newhead;
        while(list1&&list2)
        {
            if(list1->val<=list2->val)
            {
                //先将list1进行插入!
                pre->next=list1;
                pre=list1;
                list1=list1->next;
            }
            else 
            {
                pre->next=list2;
                pre=list2;
                list2=list2->next;
            }
        }    

        if(list1)
        {
            pre->next=list1;
        }
        if(list2)
        {
            pre->next=list2;
        }

        pre=newhead->next;
        delete newhead;
        return pre;
    }
};

LCR 078. 合并 K 个升序链表

本题要合并的是k个升序链表,我将通过递归与非递归的两种方式进行讲解!

1.非递归解法:

1.算法思路:

对于非递归的方法,我们采用优先级队列的方法进行求解,我们每次求出较小的那个值,然后进行尾插即可!我们要自己建立一个优先级队列,对于比较函数,因为我们要排的升序,所以当bool为true的时候,进行向下调整即可!仿函数的写法如下:

仿函数写好之后,我们将vector中的所有元素push到我们的优先级队列中即可!然后进行出队操作,每当出队的时候,我们出的是一个指针,如果该指针下面还有节点的话,我们还需要将其后继节点push到队中,当队为空的时候,我们的链表也就链接好了!

2.代码如下:

class Solution
{
public:

    //写一个仿函数用于比较节点内的值!

    struct cmp
    {
        bool operator()(const ListNode*l1,const ListNode*l2)
        {
                return l1->val>l2->val;
        }
    };
    ListNode* mergeKLists(vector<ListNode*>& lists)
    {
        priority_queue<ListNode*,vector<ListNode*>,cmp> heap;

        for(auto &l:lists)
        {
            //将所有元素插入到堆内!
            if(l)
            {
                heap.push(l);
            }
        }

        //下面判断堆是否为空  堆为空就结束即可!
            ListNode*head=new ListNode(0);
            ListNode*pre=head;

        while(!heap.empty())
        {


            ListNode*t=heap.top();
            if(t->next)
            {
                heap.push(t->next);
            }
            heap.pop();

            pre->next=t;
            pre=t;
        }

        pre=head->next;
        delete head;
        return pre;

    }
};

2.递归解法

1.算法思路:

对于递归而言,递归的思路就是将一个大问题转化为无限多个小问题!下面简单画一下递归处理的图,就可以理解每一步做的工作都是相同的,所以我们可以使用递归的思路进行求解!

从这个抽象图中可以看出,我们可以一直将链表进行划分,直至不能划分为两个或只有一个为止,然后我们就可以将问题转化为合并两个有序链表的问题了!其实就是类似于归并排序的思想!

2.代码如下:

class Solution {
public:

    //合并两个有序链表的逻辑!

    ListNode* mergesort(ListNode*&l1,ListNode*&l2)
    {

        //处理一下特殊情况!
        if(l1==nullptr)
        {
            return l2;
        }

        if(l2==nullptr)
        {
            return l1;
        }
        ListNode*Head=new ListNode(0);
        ListNode*pre=Head;


        while(l1&&l2)
        {
            if(l1->val<=l2->val)
            {
                pre->next=l1;
                pre=l1;
                l1=l1->next;
            }
            else
            {
                pre->next=l2;
                pre=l2;
                l2=l2->next;
            }
            
        } 

        //处理剩下的元素!
        if(l1)
        {
            pre->next=l1;
        }   
        if(l2)
        {
            pre->next=l2;
        }


        pre=Head->next;
        delete Head;
        return pre;
    }
    //此函数的作用是将k个链表进行划分,直到不能再划分即可!
    ListNode*merge(vector<ListNode*>& lists,int left,int right)
    {
        if(left>right) return nullptr;
        if(left==right)  return lists[left];

        //下面开始划分区域,一直进行划分即可!

        int mid=(left+right)/2;


        ListNode* l1=merge(lists,left,mid);
        ListNode* l2=merge(lists,mid+1,right);

        //走到这里说明无法再进行划分链表了,这时我们就开始实现合并两个有序链表即可

        return mergesort(l1,l2);
        

    }

    ListNode* mergeKLists(vector<ListNode*>& lists)
    {
        //也可以采用递归的思路进行求解!
        //递归就是将一个大问题划分为若干个小问题,然后一一进行求解即可!

        return merge(lists,0,lists.size()-1);

    }
};



LCR 026. 重排链表

1.算法讲解:

通过题目分析,我们可以得出本题其实分为三大步骤!

1.找到中间节点!

2.逆置链表!

3.合并两个链表!

所以我们先找到中间节点,然后从中间节点为结束,分为两个链表,然后将中间节点的后一个链表进行逆置即可!至于为什么要逆置中间节点的下一个节点,而不是中间节点,我们当逆置中间节点的时候我们会存在很多特殊情况,但是对于其下一个节点就不会存在那么多问题,感兴趣的伙伴们可以下来尝试一下!下面我把代码贴在下面!

2.代码如下

class Solution {
public:
    typedef ListNode Node;
    Node*Midnode(Node*head)
    {
        Node *fast=head;
        Node* slow=head;
        while(fast&&fast->next)
        {
            fast=fast->next->next;
            slow=slow->next;
        }

        return slow;
    }

    Node*Rerverse(Node*head)
    {
        Node *cur=head;
        Node*Head=nullptr;
        while(cur)
        {
            Node* next=cur->next;
            cur->next=Head;
            Head=cur;
            cur=next;
        }

        return Head;
    }
    void reorderList(ListNode* head) 
    {
        //求出中间节点!!
        Node*Mid=Midnode(head);
        //讲这个链表分为两段  分别为从头到中间节点!
        //断开分成两个链表!

        Node*list2=Mid->next;
        Node*list2head=Rerverse(list2);
        Node*list1=head;
        Mid->next=nullptr;
        


    

        //将链表二进行逆置  然后进行合并即可!!



        Node*ret=new Node(0,nullptr);
        Node*cur1=list1;
        Node*cur2=list2head;
       
        Node*pre=ret;
        while(cur1&&cur2)
        {
            if(cur1)
            {
                pre->next=cur1;
                pre=cur1;
                cur1=cur1->next;
            }

            pre->next=cur2;
            pre=cur2;
            cur2=cur2->next;
        }
      
        pre->next=cur1;
        delete ret;
    }
};


25. K 个一组翻转链表

1.算法思路:

我们只需要进行统计节点的个数,然后再求出需要翻转的次数,进行头插法一一进行链接,最后将没有遍历到元素进行链接即可!!

2.代码如下: 

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k)
    {
        //需要判断翻转几组,然后对这几组中的节点进行翻转!
        int count=0;
        ListNode* cur=head;
        //统计节点的个数!
        while(cur)
        {
            ++count;
            cur=cur->next;
        }

        //记录总共需要翻转几组!!
        int n=count/k;

        //然后使用头插法进行翻转即可!
        //需要注意的是,翻转之后需要记录之前的第一个节点,因为下一组需要与之前的第一个节点连接!
        ListNode*Head=new ListNode(0);
        ListNode*pre=Head;
        cur=head;
        for(int i=0;i<n;i++)
        {
            //tmp用于记录每组的第一个节点 因为每组的第一个节点需要与下一组的节点相连!
            ListNode* tmp=cur;
            for(int j=0;j<k;j++)
            {
                //此处进行头插操作即可!
                ListNode*next=cur->next;
                cur->next=pre->next;
                pre->next=cur;
                cur=next;
            }
            pre=tmp;
        }

        //接下来就是处理没有遍历到的元素!
        pre->next=cur;
        cur=Head->next;
        
        return Head->next;
        
    } 
};

  • 38
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值