class Solution {
public:
ListNode* popstack(stack<ListNode*>& st, ListNode* left, ListNode* right) {
if (left != NULL) {
left->next = st.top();
}
ListNode* ccc = st.top();
st.pop();
ListNode* next = NULL;
while (!st.empty()) {
next = st.top();
st.pop();
ccc->next = next;
ccc = next;
}
ccc->next = right;
return ccc;
}
ListNode* reverseKGroup(ListNode* head, int k) {
if (k < 2) {
return head;
}
stack<ListNode*>st;
ListNode* newhead = head;
ListNode* cur = head;
ListNode* pre = NULL;
ListNode* next = NULL;
while (cur != NULL) {
next = cur->next;
st.push(cur);
if (st.size() == k) {
pre = popstack(st, pre, next);
newhead = newhead == head ? cur : newhead;
}
cur = next;
//cur = cur->next;此处没想明白为什么会出错,先跳过
}
return newhead;
}
};
做题思路
- 将k个元素压入栈中
- 将k个元素弹出完成逆序
- 连接,将弹出的与之前原链表中的元素相连,应该如何记录两个前后节点呢
- k小于2,直接返回头指针
代码细节实现
- 主函数中先定义新的头节点,用于返回完成后的链表;当前节点,用于遍历整个链表,完成入栈这些操作;pre节点,保存当前栈中这组元素的前驱节点;next节点,保存当前栈中这组元素的后继节点。
- 弹栈函数应该做什么工作,判断前驱节点left是否为空,若为空说明是整个链表的第一组元素,若不为空,则将栈顶的元素作为前驱节点left的后继。再取一个变量获取栈中弹出的元素,将弹出的元素与之前弹出的元素相连。最终返回栈底的元素,将这个元素赋值给pre,当做是下一组的前驱元素。
- 主函数中,当前元素不断向后遍历,当栈中的数量达到k时,next指向的是cur的下一个元素,也就是后继节点。这样,前驱和后继节点就可以确定了。
- 那第一次的时候,前驱是null,如何将反转后的链表的首元素变为新链表的头newhead呢?在完成弹栈反转操作以后,判断newhead还是不是原链表的头head,如果还是head,说明这是第一次操作,就该把cur,也就是栈顶的这个元素赋值给newhead;如果不是head,说明不是第一次弹栈反转操作了,newhead保持原始值就可以了。
- 还有一个向后遍历的操作是用cur=next而不是cur=cur->next。这是因为cur是栈顶的元素,在弹栈反转的过程中cur指向的不再是下一个元素,而是上一个元素。所以会出错,使用之前保存好的next,next没有被污染,所以可以放心使用
- 若要将不足k个的剩余元素也反转,只需在循环以后,再次调用弹栈反转操作,将栈、上次返回的pre和本次的结尾null 即可完成要求
- 还有一点就是在遍历链表的过程中,有可能存在以cur为标准,那么循环中存在next的时候,next必然是null的,程序就会报错,冲出了链表的限制,一定要注意这一点
方法二
class Solution {
public:
void revergroup(ListNode* left, ListNode* start, ListNode* end, ListNode* right) {
ListNode* pre = start;
ListNode* cur = start->next;
ListNode* next = NULL;
while (cur != right) {
next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
if (left != NULL) {
left->next = end;
}
start->next = right;
}
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* pre = NULL;
ListNode* start = NULL;
ListNode* next = NULL;
ListNode* cur = head;
int count = 1;
while (cur) {
next = cur->next;
if (count == k) {
start = pre == NULL ? head : pre->next;
head = pre == NULL ? cur : head;
revergroup(pre, start, cur, next);
pre = start;
count = 0;
}
count++;
cur = next;
}
return head;
}
};
解题思路
方法二不采用额外的容器,反转链表部分没有使用栈结构,而是采用了最常用的方法,不会占用额外的空间,空间复杂度为O(1),方法1的空间复杂度为O(k)
方法二采用一个变量count记录当前数量是否到达k个元素,不断用cur向后遍历链表,若计数达到k,则继续反转链表和连接前后节点操作,若不满足计数k,则不断向后遍历,计数也不断增加
当计数达到k的时候,先确定链表的首节点,若是第一组则前驱pre应该是null,所以首节点就应该是head;若不是第一组,说明pre不为null,则链表的首节点就是pre的后继
返回的最终结果的头节点应该是第一组链表的最后一个,还是根据pre来判断是不是第一组,若pre为空说明还是第一组,那么head就应该是第一组的末节点;若pre不为null,说明不是第一组,则head保持之前的head即可。
反转链表函数完成后,将前驱pre变为上一组链表反转后的尾结点,也就是函数调用中这组链表的首节点start,再将计数清零即可