反转链表的几种方法及分析:

对于不带头结点的单链表,给定头结点head,返回反转后的头结点

struct ListNode {

  int val;
  ListNode *next;
  ListNode() : val(0), next(nullptr) {}
  ListNode(int x) : val(x), next(nullptr) {}
  ListNode(int x, ListNode *next) : val(x), next(next) {}

};

几种参考思路如下:
1.将每个结点的next指向前面结点
2.从第二个结点依次开始插入到“头结点”的前面
3.最后那个“尾结点”是反转后的“头结点”,所以从第一个结点依次开始插入
“尾结点”的后面
4.联想到栈先入先出的特性,可以考虑使用辅助栈

思路一:(递归)

ListNode* reverseList(ListNode* head) 
{
    if(!head || !head->next) return head;
    
    //newHead将是最后返回的新链表头结点
    //每次递归后head的next指向一个已经反转的子链表
    //所以只需要将head的指针指向它的前面结点即可
    //可以有多种实现方法
    //比如使用辅助头指针pre,令pre->next = head
    //这里采用的是:将head和他后面的节点围成环,再从head后面割裂
    //总之目的是让链表中每个节点的next指针指向原链表中他前面的结点
    
    ListNode* newHead = reverseList(head->next);
    
    head->next->next = head;
    
    head->next = nullptr;
    
    return newHead; 
}

分析:
时间复杂度主要来源是递归的层层调用,即O(n)
空间复杂度主要来源是每次递归都会创建一个newHead变量,即O(n)

在这里插入图片描述

思路一:(非递归)

ListNode* reverseList(ListNode* head) 
{
    if(!head || !head->next) return head;
    
    //使用双指针来同时移动
    //其中pre指向当前节点的前面结点,pi指向当前结点
    //随着pi顺着单链表移动,不断反转pi的next指针
    //最终跑完整个链表,pre刚好指向原链表最后一个结点
    //注意此时原链表head指针此时存在环路结构,要把它的next领域赋零
    ListNode *pre = head, *pi = head->next;
    ListNode* temp;
    while(pi)
    {
        temp = pi->next;
        pi->next = pre;
        pre = pi;
        pi = temp;
    }
    head->next = nullptr;
    return pre;
}

分析:
时间复杂度同样来源于对链表的遍历操作,O(n)开销
空间复杂度来源于pre、pi、temp指针,为常数开销O(1)

在这里插入图片描述

思路二:
ListNode* reverseList(ListNode* head)
{
if(!head || !head->next) return head;

    ListNode* cursor = head;
    ListNode* temp = nullptr;
    while(head->next)
    {
        temp = head->next;
        head->next = temp->next;
        temp->next = cursor;
        cursor = temp;
    }
    return cursor;
}

分析:
时间复杂度O(n)
空间复杂度只使用了两个临时变量,O(1)
在这里插入图片描述

思路三:
(这种方法其实很适合于带有头结点的单链表,在这种不带头结点的情况其实很冗余)

ListNode* reverseList(ListNode* head) 
{
    if(!head || !head->next) return head;
    
    ListNode* pi = head;
    while(pi->next) pi = pi->next;

    ListNode* Hp = new ListNode;
    Hp->next = head;

    while(Hp->next != pi)
    {
        head = Hp->next;
        Hp->next = head->next;
        head->next = pi->next;
        pi->next = head;
    }
    
    head = pi;
    delete Hp;
    return head;
}

分析:
时间复杂度O(n)
空间复杂度来源于几个临时变量,为O(1)
在这里插入图片描述

思路四:
ListNode* reverseList(ListNode* head)
{

    stack<ListNode*> myStack;
    ListNode* temp;
    while(head)
    {
        temp = head->next;
        head->next = nullptr;
        myStack.push(head);
        head = temp;
    }

    ListNode* Hp = new ListNode;
    head = Hp;

    while(!myStack.empty())
    {
        head->next = myStack.top();
        myStack.pop();
        head = head->next;
    }

    head = Hp->next;
    delete Hp;
    return head;
}

分析:
时间复杂度来源入栈和出栈,仍是链表遍历的时间,还是O(n)级别
空间复杂度来源于栈,O(n)
当然,本人对于栈仅有粗略研究,代码没有更好优化,速度可能会稍微快一点但也就这样了。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虐鼠无情麻子刘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值