这一篇博客也来看看和链表有关的题目~
反转链表
思路1
创建一个新链表,将原来的链表的结点进行头插
为了方便我们使用,我们同样将结点进行重命名
代码实现:
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head)
{
//创建新链表
ListNode* newHead = NULL;
ListNode* newTail = NULL;
//遍历原来的链表头插
ListNode* pcur = head;
while (pcur)
{
//如果新链表为空
//头结点和尾结点就是当前的结点
if (newHead == NULL)
{
newHead = newTail = pcur;
}
//新链表不为空进行头插
else
{
//当前结点下一个节点指向新链表的头结点
pcur->next = newHead;
//当前结点成为新链表头结点
newHead = pcur;
}
//继续遍历
pcur = pcur->next;
}
//新链表尾结点的下一个结点置为空
newTail->next = NULL;
//返回新链表
return newHead;
}
当我们写出代码提交的时候,它说运行时间超出限制
在OJ平台上不好看出问题我们使用VS进行调试一下去发现问题。
测试代码如下:
//测试问题代码
#include<stdio.h>
#include<stdlib.h>
struct ListNode
{
int val;
struct ListNode* next;
};
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head)
{
//特殊处理链表本来就为空
if (head == NULL)
{
return NULL;
}
//创建新链表
ListNode* newHead = NULL;
ListNode* newTail = NULL;
//遍历原来的链表头插
ListNode* pcur = head;
//count记录进入循环几次
int count = 0;
while (pcur)
{
count++;
//如果新链表为空
//头结点和尾结点就是当前的结点
if (newHead == NULL)
{
newHead = newTail = pcur;
}
//新链表不为空进行头插
else
{
//当前结点下一个节点指向新链表的头结点
pcur->next = newHead;
//当前结点成为新链表头结点
newHead = pcur;
}
//继续遍历
pcur = pcur->next;
}
//新链表尾结点的下一个结点置为空
newTail->next = NULL;
//返回新链表
return newHead;
}
void sListPrint(ListNode* head)
{
ListNode* pcur = head;
while (pcur)
{
printf("%d->", pcur->val);
pcur = pcur->next;
}
printf("NULL\n");
}
int main()
{
//手动创建链表
ListNode* node1 = (ListNode*)malloc(sizeof(ListNode));
ListNode* node2 = (ListNode*)malloc(sizeof(ListNode));
ListNode* node3 = (ListNode*)malloc(sizeof(ListNode));
ListNode* node4 = (ListNode*)malloc(sizeof(ListNode));
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = NULL;
node1->val = 1;
node2->val = 2;
node3->val = 3;
node4->val = 4;
sListPrint(node1);
ListNode* newList = reverseList(node1);
sListPrint(newList);
return 0;
}
我们会发现pcur和newHead一直在1和2之间循环,没有往后面走了,这是为什么呢?
事实上,在头插时使用pcur进行操作,已经改变了pcur的指向,pcur下一个结点永远是newHead,那么pcur也就走不到NULL,也就进入了死循环,这就是为什么刚刚代码超出时间限制,死循环了。
解决方案:使用一个结点pos保存pcur,pcur直接往后面走。
正确代码(使用打印直观观察)
#include<stdio.h>
#include<stdlib.h>
struct ListNode
{
int val;
struct ListNode* next;
};
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head)
{
//特殊处理链表本来就为空
if (head == NULL)
{
return NULL;
}
//创建新链表
ListNode* newHead = NULL;
ListNode* newTail = NULL;
//遍历原来的链表头插
ListNode* pcur = head;
while (pcur)
{
//易错点
//使用pos保存pcur
//避免后面指向改变
ListNode* pos = pcur;
//pcur直接继续遍历
pcur = pcur->next;
//如果新链表为空
//头结点和尾结点就是当前的结点
if (newHead == NULL)
{
newHead = newTail = pos;
}
//新链表不为空进行头插
else
{
//当前结点下一个节点指向新链表的头结点
pos->next = newHead;
//当前结点成为新链表头结点
newHead = pos;
}
}
//新链表尾结点的下一个结点置为空
newTail->next = NULL;
//返回新链表
return newHead;
}
void sListPrint(ListNode* head)
{
ListNode* pcur = head;
while (pcur)
{
printf("%d->", pcur->val);
pcur = pcur->next;
}
printf("NULL\n");
}
int main()
{
//手动创建链表
ListNode* node1 = (ListNode*)malloc(sizeof(ListNode));
ListNode* node2 = (ListNode*)malloc(sizeof(ListNode));
ListNode* node3 = (ListNode*)malloc(sizeof(ListNode));
ListNode* node4 = (ListNode*)malloc(sizeof(ListNode));
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = NULL;
node1->val = 1;
node2->val = 2;
node3->val = 3;
node4->val = 4;
sListPrint(node1);
ListNode* newList = reverseList(node1);
sListPrint(newList);
return 0;
}
我们的代码是没有问题的,那么我们看看代码在OJ平台上是不是可以通过
提交通过~
那么有没有更加简单的方法呢?我们一起来看看思路2
思路2
1.创建三个指针n1、n2、n3
2.最开始n1指向NULL,n2指向head,n3指向n2的下一个结点
3.遍历链表,每一次进入循环n2的下一个结点指向n1,
n1走到n2的位置,n2走到n3的位置,n3走到n3后面一个位置,直到n2为空结束循环。
这一个思路的好处在于没有创建新链表,直接在原来链表进行操作改变结点指向,是不是更加地巧妙呢!
代码实现:
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head)
{
//特殊处理链表为空
if (head == NULL)
{
return NULL;
}
//创建三个指针
ListNode* n1, *n2, *n3;
n1 = NULL;
n2 = head;
n3 = n2->next;
ListNode* pcur = head;
//遍历链表
while (n2)
{
n2->next = n1;
//n1、n2、n3往后面走
n1 = n2;
n2 = n3;
if (n3)//n3先为空,判断一下避免对空指针解引用
n3 = n3->next;
}
//返回链表
return n1;
}
代码通过,这个方法更加巧妙,没有创建新链表,在原来链表改变指向就成功了。
今日练习结束,期待与各位未来的优秀程序员交流,有什么问题请私信~~~