剑指Offer——翻转链表

题目:写出一个函数,给定一个链表的头节点,反转该链表并输出其头节点

链表的每个节点定义为一个struct,其包含元素以及下一个节点的地址

struct Node{
	int Value;
	Node *next;
	} *LinkList

对于链表头和链表尾,也有多种定义方式。比如:

  • 链表头定义为表的第一个节点,链表尾定义为最后一个元素。此时
LinkList list;
int value = list->Value;//第一个元素

Node *SecondNode = list->Next;//下一个节点

Node *tail = LastNode; //假设LastNode为链表最后一个元素
tail->Next==nullptr; //tail的下一个节点为空指针
  • 链表头为一个节点,无有效元素,节点的下一个节点为第一个元素。
  • 链表尾同样是无有效元素的节点,节点的下一个元素为空指针。
LinkList list;
Node *FirstNode = list->Next;//第一个元素节点

Node *tail = LastNode->Next;//尾指针为最后一个节点的下一个元素
tail->Next==nullptr;

当链表为空链表的时候,头指针的下一个元素为尾指针。

此题为方便起见,采用第一种定义,链表头定义为链表的第一个节点,尾定义为链表的最后一个节点。

链表的操作比较复杂,其中包含大量指针操作,稍有不慎就会出错。为了分析清楚,下边画图来进行分析。在这里插入图片描述

假设a为需要反转的链表,b为反转过程进行到C的时候。可以看到,原本C的下一个元素是D,反转要求C的下一个元素指向B,但是如果直接进行操作的话,C和D之间就发生断裂,再也无法找到D的地址。所以在反转之前要先保存D的地址。

同样地,当链表进行到C时,此时由于是单向链表,无法回头找到B元素的地址,因此在进行到C之前,需要把B的地址保存下来。

因此在链表处于某一个位置时(如C),必须要实现保存其上一个节点位置(如B),和下一个位置(如C),才能实现反转。

下边看看链表的首尾如何处理。对于头结点,其反转后变为尾节点,其下一个元素应为空节点,也就是头结点的mPrev设置为空就可以了。
对于尾节点,其下一个节点为空,因此反转过程可以进行到某个节点的下个节点为空指针时停止,此时该节点即为头结点,将其反转后输出即可。
实现代码如下:

typedef struct ListNode{
  int Value;
  ListNode *Next;
} *LinkList;

void ReverseLinkList(LinkList &List){
  if(List==nullptr) {
    printf("List is empty");
    return ;
  }

  LinkList pPrev = nullptr;
  LinkList pNext = nullptr;

  while(List->Next!=nullptr){
    pNext = List->Next;
    List->Next = pPrev;

    pPrev = List;
    List = pNext;
  }

  List->Next = pPrev;
}

算法是直接对输入的链表进行操作,操作完成后的链表为已经反转的链表。

首先对输入进行判断,如果链表为空指针,则不用反转直接返回。

如果链表只有1个元素,则也不用反转,List->Next为空,不会程序中的while循环,直接返回。

当链表元素多于两个时,对于链表的第一个节点,先保存其下一个节点到pNext,再将List->Next设置为mPrev,也就是空指针。此时反转已完成,将自身节点保存为mPrev后,进入下一个节点。

进入下一个节点时,会判断该节点的是否为尾节点,如果不是循环一直进行。如果是,则退出循环,直接将节点Node->Next设置为pPrev即完成。

总结

反转链表容易出错的地方有几个:

  • 输入链表如果为空指针,或者链表只有1个元素,则不用反转,程序必须有对其进行处理的步骤。
  • 对于链表的中间某个节点,其前一个和后一个节点都不为空,其处理是可以用循环完成的,但是对于头结点为尾节点,其前节点或后节点为空,因此程序设计的时候要考虑到对其的处理。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值