这题是牛客网的BM1,主要涉及到链表的操作以及栈数据结构的使用。
给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。
数据范围: 0≤n≤10000≤n≤1000
要求:空间复杂度 O(1),时间复杂度O(n) 。
如当输入链表{1,2,3}时,
经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。
以上转换过程如下图所示:
示例1
输入:
{1,2,3}
返回值:
{3,2,1}
示例2
输入:
{}
返回值:
{}
说明:
空链表则输出空
先不考虑链表,我们只考虑数据。对于1,2,3的输入,我想让其输出3,2,1。
这个就类似于栈的先进后出。
那么现在定义栈,存放链表的节点数据,全部存放完之后,再出栈即可。
因为涉及到空链表的情况,所以要单独判断链表是否为空。
C++中使用vector或者stack。vector的适用性更广,包含了stack。
vector中使用back()获取栈顶元素,我这里使用reverse函数,直接倒序,即123变成321,再依次遍历赋值输出。
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) : val(x), next(nullptr) {}
* };
*/
#include <cstdio>
#include <cstdlib>
#include <iterator>
#include <stack>
#include <vector>
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param head ListNode类
* @return ListNode类
*/
ListNode* ReverseList(ListNode* head) {
// write code here
if (head == nullptr)//如果空则直接返回
return head;
vector<ListNode*>Contain;//存放链表节点的vector容器
while (head != nullptr) {
//容器不为空则依次入栈
Contain.push_back(head);
head = head->next;
}
//反转容器
reverse(Contain.begin(), Contain.end());
ListNode* newHead = Contain[0];
ListNode* cur = newHead;
for (int i = 1; i < Contain.size(); i++) {
cur->next = Contain[i];
cur = cur->next;
}
cur->next = nullptr;//最后一个节点的下一节点指向置空,避免循环链表
return newHead;
}
};
对于给定的顺序链表,将每个节点的后继节点作为当前节点的前置节点,即可完成倒置。
对于1,2,3。我需要将2的下一个节点指向1,新的链表头就是2。
将3的下一个节点指向2,2同时又指向1,那么链表就完成了倒置。
这里我就要用到一个节点用来保存新的表头节点,同时节点与节点之间原来的关系要打乱,要断开原来的先后关系,重新构建。
下面的代码,首先定义了一个新的表头节点pre,置空。将下一个要翻转的表头保存;遍历给定的链表,找到第一个表头p,将这个表头的下一个节点p->next指向新的表头节点p。同时这个表头p原来指向的下一个节点,作为新的倒置表头。新的表头节点pre从空节点指向要翻转表的表头p。
重复上面的步骤,最终得到一个翻转的链表。
这个方法主要是断开原来的链表,组建新的链表的过程。
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) : val(x), next(nullptr) {}
* };
*/
#include <cstdio>
#include <cstdlib>
#include <iterator>
#include <stack>
#include <vector>
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param head ListNode类
* @return ListNode类
*/
ListNode* ReverseList(ListNode* p) {
ListNode* pre = nullptr;
while (p) { // 判断链表是否已经到结尾
ListNode* t = p->next; // 保留下一个即将翻转的表头
p->next = pre; // 将现在要翻转的节点的后继指向前一个节点 pre
pre = p; // 现在的 P 成为下一个前继节点
p = t; // p 成为下一个要翻转的节点
}
return pre;
}
};
本篇博客介绍了两种方式,一种是依赖数据结构本身,使用栈的特性。另一种是断链表,组新链表的方式,语言描述有点搞,具体还是参考代码为好。