反转链表的五种方法

一、题目介绍

题目链接(力扣上第206题)

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例:

二、反转链表五种方法 

1.方法一:原地反转链表(本质上是头插法)

代码:

 ListNode* reverseList(ListNode* head) {
        if(head==NULL)
        return NULL;
        struct ListNode* dummy=new ListNode(0,head);
        struct ListNode* cur=dummy->next->next;
        while(cur)
        {
            head->next=cur->next;
            cur->next=dummy->next;
            dummy->next=cur;
            cur=head->next;
        }
        head=dummy->next;
        delete dummy;
        dummy=NULL;
        return head;
    }

两个指针:

dummy:哨兵位结点,其next指向链表头结点(力扣中的题目一般都是没有哨兵位结点的,需要自己创建)

cur:当前遍历的结点,即需要被操作的结点;

主要算法步骤:

a.head->next=cur->next;  cur头插后,连接空缺位置;

b.cur->next=dummy->next;  dummy->next=cur; 将cur提前后连接操作;

c.cur=head->next;   遍历结点;

注意细节:

a.要画图来理解原地反转的过程,在这个过程中,cur每次被head->next赋值,即被操作的结点永远是head的下一个结点,当head->next=NULL时,循环结束;

2.方法二:迭代法

代码:

  ListNode* reverseList(ListNode* head) {
        struct ListNode* pre=NULL;
        struct ListNode* cur=head;
        while(cur)
        {
            struct ListNode* next=cur->next;
            cur->next=pre;
            pre=cur;
            cur=next;
        }
        return pre;
    }

 三个指针:

cur:指向当前遍历的结点;

pre:指向cur的上一个,用于cur->next=pre操作,初始化为NULL;

next:指向cur的下一个,用来保存cur的下一个,防止cur->next丢失;

迭代步骤:

a.next=cur->enxt; 保存cur的下一个结点,防止丢失;

b.cur->next=pre;

c.pre=cur;   cur=next;  向后遍历直到当前遍历结点为NULL,即cur=NULL,迭代结束;

注意细节:

a.pre先置空,因为对链表头结点操作时需要把head->next=NULL;

b.错误写法

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        struct ListNode* pre=NULL;
        struct ListNode* cur=head;
        struct ListNode* next=head->next;
        while(cur)
        {
            cur->next=pre;
            pre=cur;
            cur=next;
            next=next->next;
        }
        return pre;
    }
};

这种写法看似没有什么问题,但是有两点问题:

a.没有考虑到head=NULL的情况;

b.next=netxt->next操作可能会出现报错。当next=NULL时,会出现报错,应为此时循环的条件为while(cur),既使将循环条件改为while(next),也是错的,此时链表的最后一个元素不会被操作;

c.既然要保存cur->next,应该很自然地在while循环里定义next,而且定义在前面;

d.此种方法的正确写法要考虑head以及head->next;

3.方法三:头插法

代码:

ListNode* reverseList(ListNode* head) {
    struct ListNode* dummy=new ListNode(0,NULL);
    struct ListNode* cur=head;
    while(cur)
    {
        struct ListNode* next=cur->next;
        cur->next=dummy->next;
        dummy->next=cur;
        cur=next;
    }
    struct ListNode* hhead=dummy->next;
    delete dummy;
    dummy=NULL;
    return hhead;
}

三个指针:

dummy:哨兵位结点,其next指向链表头结点(力扣中的题目一般都是没有哨兵位结点的,需要自己创建)

cur:遍历需要被反转的链表的指针;

next:保存cur的next的指针;

主要步骤:

a.next=cur->next;  先保存cur的next指针

b.cur->next=dummy->next;  dummy->next=cur;  将被操作的cur指针头插到新的链表中,并且连接;

c.cur=next;  遍历操作;

错误写法:

ListNode* reverseList(ListNode* head) {
    struct ListNode* dummy=new ListNode(0,NULL);
    struct ListNode* cur=head;
    while(cur)
    {
        struct ListNode* p=cur;
        p->next=dummy->next;
        dummy->next=p;
        cur=cur->next;
    }
    struct ListNode* hhead=dummy->next;
    delete dummy;
    dummy=NULL;
    return hhead;
}

注意细节:

a.不要忘记了要保存cur的next。

b.操作cur时,不需要再定义一个p指针了,直接对cur进行操作就可以了,对cur操作完再将cur赋新的值即可; 

4.方法四:递归法

代码:

  ListNode* reverseList(ListNode* head) {
        if(head==NULL||head->next==NULL)
        return head;
        struct ListNode* newhead=reverseList(head->next);
        head->next->next=head;
        head->next=NULL;
        return newhead;        
    }

递归思路:

a.先确定函数返回值类型和参数:最后要返回一个头结点,故返回值类型为ListNode* ;从头结点开始递归过程,故传参ListNode*(head);

b.判断递归终止条件:想象一下你想要最后返回的是什么?因为是反转链表,故最后返回的是最后一个结点,即当head->next=NULL时终止,同时要判断head一开始是否是空结点;

c.单层递归逻辑:最后我们要得到的是链表的头结点,故每次都把newhead返回;根据当前递归过程中的head来进行操作,例如最后一个过程中head指的是倒数第二个结点,想象一下应该对倒数第二个结点进行什么操作?即(head->next)->next=head;又因为第一个结点最后要将其next域置空,故还要进行head->next=NULL操作;最后再返回newhead;

5.方法五:运用栈

代码:

   ListNode* reverseList(ListNode* head) {
        vector<struct ListNode*> list;
        while(head)
        {
            list.push_back(head);
            head=head->next;
        }
         struct ListNode* dummy=new ListNode(0,NULL);
         struct ListNode* cur=dummy;
         while(!list.empty())
         {
            struct ListNode* newnode=list.back();
            newnode->next=NULL;
            cur->next=newnode;
            cur=cur->next;
            list.pop_back();
         }
         head=dummy->next;
         delete dummy;
         return head;
    }

主要思路:

创建一个数组,按照反转前的遍历顺序存放链表结点指针,再反方向遍历数组,创建一个新链表,依次将数组中的结点连接起来;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值