Leetcode刷题心得(No.24:两两交换链表中的结点)(链表:三指针,递归)

        方法一:三指针 

使用三指针时,有许多需要注意的点:

        1.空链表、只有头结点

        2.在处理时,需要先处理前面的两个结点

        3.奇数结点,偶数结点(涉及到while循环的条件设定)


class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        //特殊情况(空链表或链表只有head)
        if(head == nullptr ||head->next == nullptr){
            return head;
        }
        else{
        ListNode* newhead = head;
        ListNode* p = head;
        ListNode* q = head->next;
        //普通情况
        //先把前两个交换
        if(p == head){
            //只有两个结点
            if(q->next == nullptr){
                q->next = p;
                p->next = nullptr;
                head = q;
            }
            //有两个以上结点时,交换前两个结点
            else{
                p->next = q->next;
                q->next = p;
                head = q;
                //更新一下p,q,newhead
                if(p != nullptr && q!= nullptr){
                    newhead = p;
                    p = p->next;
                    q = p->next;                    
                }
               
            }   
        }
        //前两个结点已经交换完毕,开始考虑后续结点
        while(p != nullptr && p->next != nullptr ){
            if(q->next == nullptr){
                q->next = p;
                p->next = nullptr; 
                newhead->next = q;                              
            }
            else{
            p->next = q->next;
            newhead->next = q;
            q->next = p;
            //
            newhead = p;
            p = p->next;
            q = p->next;                
            }
            
            } 
        
        }

        return head;
    }
};

​

方法二:递归 

有一说一,递归着实有点不太好思考。笔者之前一般都用双指针解题,递归用的较少(其实就是没有哈哈哈哈)。以后解题都将使用多解法。

递归思想在我的另一篇博客中有较详细的讲解。若对递归思想不太熟悉的同学,可以一步去看一下。

人理解迭代,而神理解递归————递归思想心得_FightingCSH的博客-优快云博客

        对于该题来讲。总问题是:将每两个结点颠倒顺序,在处理这个总问题时,根据C++的顺序处理逻辑,必将在遍历的同时,对每两结点调换顺序。1,2处理完,处理3,4......

        那么对于1,2;3,4;5,6......结点,计算机进行的任务都是一样的,此时递归思想便出现了。

第一步:我们考虑总问题(总函数):这个Swap()函数他的输入输出是什么样子的。因为我们要对链表进行遍历,才能处理每个结点(链表的顺序逻辑结构也决定了他只能遍历)。所以要获得每个结点的地址,我们必须从头结点开始。所以输入input是头结点地址head。而输出呢?该Swap()函数在处理完链表之后,在输出时,必然要进行遍历,所以我们也必须return head,才可全部输出。至此,第一步,确定输入输出已经结束了。(不要认为这个确定输入输出很简单,就不把他当回事,理清思路就需要从整体到局部一点一点来,尤其是对于新手,不能好高骛远。)

第二步:思考子问题是什么。很简单,将前一个结点与后一个结点调换顺序。我们任意挑链表中的需要调换顺序的两个结点来考虑,将前一个结点命名为head,后一个结点命名为newhead。

head->next = newhead->next;
newhead->next = head;
(前一对结点的newhead->next) = newhead;(仔细想想,这句话到底属不属于子问题中的一部分)

我们调换这一对接点顺序的思路是不是如上面所示。

但是对于子问题的第一行代码来讲,如果newhead的后继结点地址写成newhead->next,我们就无法与下一个子问题建立联系,数学归纳法的链子就断掉了。所以为了与下一子问题建立联系,我们需要与下一个子问题的Swap()函数挂钩。如下述代码所示。

在第三行中,前一对结点的newhead地址,我们是无从得知的。但是请各位注意,我们研究的总问题用符号可以简单表示成-》-》-》-》-》,是从前往后推的。所以子问题不需要考虑后推前,只需要确定我们的前推后只能推成功,就满足成立条件(类比数学归纳法)。所以前一对结点的newhead地址不知道,并不影响我们解决子问题。

第三步:既然在第二步中,链子已经建立了,改用脑子思考怎么顺着链子摸到最后了。

ListNode* newhead = head->next;

所以第一行中的newhead->next,我们可以从后一个子问题的解获得,

即:head->next = Swap(newhead->next);

第二行条件全部满足,直接落下即可:

newhead->next = head;

最后,子问题会返回头结点:

return newhead;

 注意这一行代码即:head->next = Swap(newhead->next);Swap()函数会在计算机中一直执行,直到最后head == nullptr || head->next == nullptr时,才会结束,然后不停的返回后续子问题中的newhead。直到我们现在研究的这个子问题的后一个子问题被计算机处理完,并返回这个问题的newhead为止。(这里有点绕,但是想明白之后,递归就通了)

        至此,递归全部结束,并返回了最终的头结点。

 可以看到,递归对于内存的占用是比较大的,在实际项目中,最好用迭代循环代替递归。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值