如何优雅的反转单链表以及变种形式

【题目描述】

反转单链表。
例如链表为: 1->2->3->4

反转后为: 4->3->2->1

【要求】 :如果链表的长度为 N, 时间复杂度达到 O(N), 额外空间复杂度达到 O(1).

【解答】
方法1
这道题还是挺简单的,当我们在反转一个节点的时候,把一个节点的后驱改为指向它前驱就可以了。这里需要注意的点就是,当你把当前节点的后驱指向前驱的时候,这个时候链表会被截断,也就是说后面的节点和当前节点分开了,所以我们需要一个变量来保存当前节点的后驱,以访丢失。
代码如下:

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

方法二
这道题也可以用递归来做,假设 方法 reverse() 的功能是将单链表进行逆转。采用递归的方法时,我们可以不断着对子链表进行递归。例如对于如下的链表:
在这里插入图片描述
我们对子链表 2->3->4 进行递归,即
Node newList = reverse(head.next)。递归之后的结果如下:
在这里插入图片描述
逆转之后子链表 2->3->变为了 4->3->2。
注意,我刚才假设 reverse() 的功能就是对链表进行逆转。不过此时节点 1 仍然是指向节点 2 的。这个时候,我们再把节点1 和 2逆转一下,然后 1 的下一个节点指向 null 就可以了。如图:
在这里插入图片描述
递归的结束条件就是:当子链表只有一个节点,或者为 null 时,递归结束。代码如下:

class Solution {
public:
    ListNode* reverseList(ListNode* head)
    {
   if(nullptr == head ||nullptr == head->next)
            return head;
        ListNode* NewNode =reverseList(head->next);
        head->next->next = head;
        head->next = nullptr;
        return NewNode;
   }
};       

题目:反转部分链表节点

题目: 给定一个单向链表的头结点head,以及两个整数from和to ,在单项链表上把第from个节点和第to个节点这一部分进行反转
列如: 1->2->3->4->5->null,from=2,to=4
结果: 1->4->3->2->5->null
列如: 1->2->3->null from=1,to=3
结果: 3->2->1->null
【要求】
1、如果链表长度为N,时间复杂度要求为O(N),额外空间复杂度要求为O(1)
2、如果不满足1<=from<=to<=N,则不调整

 class Solution {
public:
     ListNode* reverseList(ListNode* head,int from,int to)
    {
        int len = 1//记录链表的长度
        ListNode* node = head;
        ListNode* fpre = null;//指向from-1的节点
        ListNode* tpos = null;//指向to+1的节点
        while(node1 != null)
       {
            len++;
          if(len == from-1)
           fpre = node1;
          if(len == to+1)
            tpos = node1->next; 
       }
      //判断给定的值是否合理  
        if(from > to || from<1 || to>len)
            return head;
      // 把from-toz这部分链表逆转
      //node1指向部分链表的第一个节点
      node1 = fpre = null?head:fpre->next;
      ListNode* cur = node1->next;//把cur指向当前要处理的节点
      node1->next = tpos;//先把第一个节点反转处理
      ListNode* next = nulll;
      while(cur != tpos)
      {
        next = cur->next;//保存当前节点的下一节点
        cur->next = node1;
        node1 = cur;
        cur = next;
      }      
        if(fpre != null)
        {
            fpre->next = node1;
            return head;
        }
      return head1;
 }  

题目:反转部分链表节点

给定一个单链表的头节点 head,实现一个调整单链表的函数,使得每K个节点之间为一组进行逆序,并且从链表的尾部开始组起,头部剩余节点数量不够一组的不需要逆序。(不能使用队列或者栈作为辅助)
例如: 链表:1->2->3->4->5->6->7->8->null, K = 3。那么 6->7->8,3->4->5,1->2各位一组。

调整后: 1->2->5->4->3->8->7->6->null。其中 1,2不调整,因为不够一组。

解答
这道题的难点在于,是从链表的尾部开始组起的,而不是从链表的头部,如果是头部的话,那我们还是比较容易做的,因为你可以遍历链表,每遍历 k 个就拆分为一组来逆序。但是从尾部的话就不一样了,因为是单链表,不能往后遍历组起。不过这道题肯定是用递归比较好做。

先做一道类似的反转题
在做这道题之前,我们不仿先来看看如果从头部开始组起的话,应该怎么做呢?例如:链表:1->2->3->4->5->6->7->8->null, K = 3。调整后:3->2->1->6->5->4->7->8->null。其中 7,8不调整,因为不够一组。
对于这道题。这道题我们可以用递归来实现,假设方法reverseKNode()的功能是将单链表的每K个节点之间逆序(从头部开始组起的哦);reverse()方法的功能是将一个单链表逆序。
那么对于下面的这个单链表,其中 K = 3。
在这里插入图片描述
我们把前K个节点与后面的节点分割出来:
在这里插入图片描述
temp指向的剩余的链表,可以说是原问题的一个子问题。我们可以调用reverseKNode()方法将temp指向的链表每K个节点之间进行逆序。再调用reverse()方法把head指向的那3个节点进行逆序,结果如下:
在这里插入图片描述
接着,我们只需要把这两部分给连接起来就可以了。最后的结果如下:
在这里插入图片描述
代码如下:

//k个为一组逆序
    public ListNode reverseKGroup(ListNode* head, int k)
     {
        ListNode* temp = head;
        for (int i = 1; i < k && temp != null; i++) 
        {
            temp = temp->next;
        }
        //判断节点的数量是否能够凑成一组
        if(temp == null)
            return head;

        ListNode* t2 = temp->next;
        temp->next = null;
        //把当前的组进行逆序
        ListNode* newHead = reverseList(head);
        //把之后的节点进行分组逆序
        ListNode* newTemp = reverseKGroup(t2, k);
        // 把两部分连接起来
        head->next = newTemp;
          return newHead;
    }

    //逆序单链表
    ListNode* reverseList(ListNode* head)
    {
      if(nullptr == head ||nullptr == head->next)
            return head;
        ListNode* NewNode =reverseList(head->next);
        head->next->next = head;
        head->next = nullptr;
        return NewNode;
   }

其实这道题很好做滴,你只需要先把单链表进行一次逆序,逆序之后就能转化为从头部开始组起了,然后按照我上面的解法,处理完之后,把结果再次逆序即搞定。两次逆序相当于没逆序。

例如对于链表(其中 K = 3)
在这里插入图片描述
我们把它从尾部开始组起,每 K 个节点为一组进行逆序。步骤如下

1、先进行逆序
在这里插入图片描述
逆序之后就可以把问题转化为从头部开始组起,每 K 个节点为一组进行逆序。

2、处理后的结果如下

在这里插入图片描述

3、接着在把结果逆序一次,结果如下
在这里插入图片描述
代码如下

 ListNode* solve(ListNode* head, int k)
  {
    // 调用逆序函数
    head = reverse(head);
    // 调用每 k 个为一组的逆序函数(从头部开始组起)
    head = reverseKGroup(head, k);
    // 在逆序一次
    head = reverse(head);
    return head;
}

类似于这种需要先进行逆序的还要两个链表相加,这道题字节跳动的笔试题也有出过,如下图的第二题
在这里插入图片描述
这道题就需要先把两个链表逆序,再节点间相加,最后在合并了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值