【题目】
有一个单链表,请设计一个算法,使得每K个节点之间逆序,如果最后不够K个节点一组,则不调整最后几个节点。例如链表1->2->3->4->5->6->7->8->null,K=3这个例子。调整后为,3->2->1->6->5->4->7->8->null。因为K==3,所以每三个节点之间逆序,但其中的7,8不调整,因为只有两个节点不够一组。
给定一个单链表的头指针head,同时给定K值,返回逆序后的链表的头指针。
方法一:不使用额外空间,纯手动,时间复杂度O(N)
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class KInverse {
public:
ListNode* inverse(ListNode* head, int K) {
if(head != NULL && head->next != NULL&& K > 1){
ListNode* cur = head;
ListNode* pre = NULL;
ListNode* next = NULL;
ListNode* start = NULL;
int counter = 1; //计数器初值为1,计到k
while(cur != NULL){ //只判断是否为空程序结束,通过k来判断是否需要逆序
next = cur->next;
if(counter == K){ //满足k个,逆序它
start = pre == NULL ? head : pre->next;//第一组的第k个作为链表头部
head = pre == NULL ? cur : head;
reverse_local(pre, start, cur, next);
pre = start; //pre是用来每次保存上一组k个结点尾节点的
counter = 0; //计数器为0,因为接下来会++
}
++counter;
cur = next;//千万注意是cur=next,而不是cur=cur->next因为cur->next指向被逆序改变了
}
}
return head;
}
private:
void reverse_local(ListNode* left, ListNode* start, ListNode* end, ListNode* right){
ListNode* behind = start;
ListNode* current = start->next;;
ListNode* front = current->next;
while(current != right){
front = current->next;
current->next = behind;
behind = current;
current = front;
}
if(left != NULL)//如果是第一组,不必更新,因为pre是用来替除了第一组保存前一组尾节点的
left->next = end; //由之前的pre->next=right改为pre->next=end
start->next = right;//start实际上先连接
}
};
方法一注意的点都在注释中了,下面给出它的图解:
这种方法超级考验代码实现能力,很容易漏掉情况,我觉得谁要是没练过这类题,面试中基本不可能在有限时间做出来。
方法二:使用栈,时间复杂度为O(2N),空间复杂度O(N)。比较容易,直接利用栈将链表从每K个节点倒序赋值给从1开始的节点。
有一种做法是使用栈但是修改指针,但是那样太麻烦了,时间复杂度是相同的。所以直接赋值即可。
class KInverse {
public:
ListNode* inverse(ListNode* head, int k) {
if(head != NULL && head->next != NULL && k > 1){
ListNode* wait = head;
ListNode* act = head;
stack<int> st;
int counter = 1;
while(act != NULL){ //此类问题使用NULL控制循环,使用循环外变量counter控制是否逆序
st.push(act->val);
if(counter == k){
while(!st.empty()){
wait->val = st.top();
st.pop();
wait = wait->next;
}
counter = 0;
}
++counter;
act = act->next;
}
}
return head;
}
};