算法题学习---链表反转

72 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情

题目

描述

给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。

数据范围: 0≤n≤1000

要求:空间复杂度 O(1)O(1) ,时间复杂度 O(n)O(n) 。

如当输入链表{1,2,3}时,

经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。

以上转换过程如下图所示:

示例1

输入:{1,2,3}
返回值:{3,2,1}

示例2

输入:{}
返回值:{}
说明:空链表则输出空 

分析

考虑特殊情况

在进行问题分析过程前,我们先考虑一下特殊情况,那么在这道问题中,有哪些情况是特殊的呢?

不难发现,如下两种情况:

  • 空链表,即头指针为NULL,这种情况我们直接返回头指针即可;
  • 链表只有一个节点,即pHead->next为NULL,这种情况我们也直接返回头指针即可。

考虑一般情况

对于这类问题,一般都需要遍历整个链表,那我们就要考虑一下,如何在遍历一次链表的过程中,完成链表的反转,我们考虑这样一个普遍的情况如下,p(1)到p(n-1)的节点都已经翻转了,当前遍历指针处理到p(n),不妨设这个指针为pCur.

<---p(n-1) p(n)--->p(n+1)--->p(n+2)

现在,我们来想想,如果这次遍历将遍历指针移到p(n+1),那在这之前我们需要做什么呢?

首先:我们需要将p(n)->next指向p(n-1),这时就出现了一个问题,如果这样做我们就不知道下一个位置了,所以我们需要一个局部变量来存储pCur的下一个位置,不妨设为pNext,同理,我们也需要保存一下pCur之前的一个位置,不妨设为pPre;

然后:我们需要考虑怎么移动上面提到的三个指针pPre,pCur,pNext,一个朴素的想法是按照从前往后的方式更新即可,即:

pPre = pCur;

pCur = pNext;

pNext = pNext->next;

最后:我们需要考虑遍历结束的条件,考虑到当pCur指向最后的数据时,pNext是NULL,这时再取pNext->pNext就会出现空指针的情况,所以我们将条件设置为pNext != NULL。在这种情况下,我们要注意将最后返回值确认清楚,即pCur

代码演示

版本一

根据上面的描述,我们可以写出如下版本的代码:

/**
 * struct ListNode {
 *  int val;
 *  struct ListNode *next;
 * };
 */

/**
 * 
 * @param pHead ListNode类 
 * @return ListNode类
 */
struct ListNode* ReverseList(struct ListNode* pHead ) {
    // write code here
    if(NULL == pHead || NULL == pHead->next)
        return pHead;
    
    struct ListNode *pPre = NULL;
    struct ListNode *pCur = pHead;
    struct ListNode *pNext = pHead->next;

    while(NULL != pNext){
        pCur->next = pPre;
        pPre = pCur;
        pCur = pNext;
        pNext = pNext->next;
    }
    
    pCur->next = pPre;
    return pCur;
}

注意上面版本一代码中的这两句,实际上与循环中的语句是一样的,那么我们是不是可以调整一下循环条件,然后将语句放到循环中去呢,实际上是可以的。

struct ListNode *pNext = pHead->next;
...
pCur->next = pPre;

可以看到,我们希望的是在循环结束条件之前,我们多执行一次pCur->next = pPre;,其实我们只需要将条件中的pNext前移为pCur即可。

同理,对于struct ListNode *pNext = pHead->next;,我们就需要在循环中先初始化pNext = pCur->next.

注意我们返回值也要改变为pCur,即所有指针都往前进行移动一个位置。

整理如下:

版本二

/**
 * 
 * @param pHead ListNode类 
 * @return ListNode类
 */
struct ListNode* ReverseList(struct ListNode* pHead ) {
    // write code here
    if(NULL == pHead || NULL == pHead->next)
        return pHead;
    
    struct ListNode *pPre = NULL;
    struct ListNode *pCur = pHead;
    struct ListNode *pNext = NULL;

    while(NULL != pCur){
        pNext = pCur->next;
        pCur->next = pPre;
        pPre = pCur;
        pCur = pNext;
    }

    return pPre;
}