反转链表 | 牛客刷题 | 剑指offer JZ24

反转链表

  • 题目描述

给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。
数据范围: 0≤n≤1000
要求:空间复杂度 O(1) ,时间复杂度 O(n) 。
例如当输入链表{1,2,3}时,经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。
题目描述

第一种做法:改值法

  • 解题思路
    👊万事先考虑特殊情况【空链表】,此时直接返回空链表就行。
    我们对链表操作时一定要记住,链表不是数组,每个节点看起来是连在一起的, 但在实际物理空间中(也就是在内存中并非连续存储的,如果失去了指针next你就找不到“下一个”节点了!)
    改值法的核心是——两边向中间渗透,利用两个指针tailphead将链表的值进行swap交换。

    1. 预处理:tail指针指向链表phead指向链表

    2. 明确指针移动多少步。我们需要让tailphead相逢但是不相遇。
      ⭕️重点在for循环的控制上。
      我们phead需要移动 l e n g t h 2 − 1 \frac{length}{2}-1 2length1次,
      tail要往前移动一格,怎么移动?
      我这里是先把tail放到phead的位置,再往后移动 l e n g t h − 2 ∗ ( i + 1 ) length-2*(i+1) length2(i+1)
      PS:这里的i是外层循环phead的移动步数控制,但是由于我设置的初始值为0,所以需要利用(i+1)来控制tail的移动。
      🌰举个栗子:
      如果不理解可以进行自己画一个链表示意图出来推算一下,假设这里有单链表{1,2,3,4,5,6}
      1️⃣第一层循环i=0中,tail指向 6 6 6phead指向 1 1 1
      swap交换数值;
      tail先拿到phead的位置,即指向 1 1 1,为了让他指向 5 5 5,我们要移动4次;
      紧接着将phead往后移一格,指向 2 2 2
      2️⃣进入第二层循环i=1中,tail指向 5 5 5,phead指向 2 2 2
      swap交换数值;
      tail先拿到phead的位置,即指向第二格,为了移动到倒数第三格,即指向 4 4 4,我们要移动2次;
      再把phead往后移一格,指向 3 3 3;

    3. 实现数值交换【就是“如何交换两个水杯中的可乐和橙汁”的问题】
      借助一个辅助变量存储一杯水即可。

改值法

  • 完整代码
/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    ListNode* ReverseList(ListNode* head) {
        // write code here
        if(head==nullptr){
            return head;
        }
        ListNode* tail=head,*phead=head;
        int length=1;
        //将尾指针表示出来
        while(tail->next!=nullptr){
            tail=tail->next;
            length++;
        }
        //向中间扩散
        for(int i =0 ;i<= (length/2-1);i++){
            int t=phead->val;
            phead->val=tail->val;
            tail->val=t;
            //先把tail拿回来!
            tail=phead;
            //挪到倒数第i个位置【重点】
            for(int j=0;j<(length-2*(i+1));j++){
                tail=tail->next;
            }
            phead=phead->next;
        }
        return head;
    }
};

第二种做法——修改指针方向【强烈安利!】

  • 解题思路
    这个就更好理解指针的寓意了!💯
    突破口】尾指针的next必是NULL
    我们都知道单链表没了next就是断掉了,节点在内存中不连续存储就导致我们失去某个节点的next就找不到该节点的下一个节点了❗️
    这意味着该节点后边的所有节点你都找不到了!所以在“断开”链表的时候一定要谨慎啊!!!
    于是我就想到了,我从尾节点开始向链表头的方向修改指针方向,因为尾节点它的next没有东西。
    1. 预处理:tail指向链表phead指向链表倒数第二格,即tail前面一格的节点。
    2. new_head存储修改方向后的链表。这个是我们最后的返回值!
    3. 考虑循环结束条件。【当phead移动到原始链表的头部head的时候就证明我们所有节点的指针方向next反向了】
    4. 修改所有节点指针方向。
      核心在于tail->next=phead将指针反向;
      然后tail=pheadtail指针移动到phead处,也就是tail向前移动一格;
      再将phead移动到tail的前一格,循环往复至原链表头head即可。

修改指针方向

  • 完整代码
/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
#include <cstddef>
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    ListNode* ReverseList(ListNode* head) {
        // write code here
        //如果是空链表直接返回空!
        if(head==nullptr){
            return head;
        }
        ListNode* tail=head,*phead=head;
        //将尾指针表示出来
        while(tail->next!=nullptr){
            tail=tail->next;
            if(phead->next->next==nullptr) break;//把phead挪到尾指针前面
            phead=phead->next;
        }
        ListNode* new_head=tail;
        while(phead!=head){
            tail->next=phead;
            //别忘了把tail挪到phead的位置
            tail=phead;
            phead=head;
            while(phead->next!=tail){
                //再次将phead挪到尾指针前面
                phead=phead->next;
            }
            if(phead==head){
                tail->next=phead;
                phead->next=NULL;
                break;
            }
        }
        return new_head;;
    }
};

虽然难度是“简单”,但是指针的操作更熟悉了!加油加油!!小白打怪升级!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值