问题描述
最初,面试官问这个问题,以扭转我很容易解决的链表。
现在他说要按K个节点的组来反转列表。
例如,如果列表为[1,2,3,4,5,6]
和K=4
,则为o/p = [4,1,6].
。因此,我已经修改了现有解决方案以实现此目的,但是仍然可以使整个列表的输出颠倒(i.e [6,1]
)。参见下面的程序。可能需要进行一些细微的更改,但我无法弄清楚。有人可以帮忙解决问题吗?
ListNode *reverseKGroup(ListNode *head,int k)
{
if (k == 0 || k == 1)
return head;
if (head == NULL)
return NULL;
int counter = 0;
ListNode *fastPtr = head;
ListNode *currentPtr = head;
ListNode *nextPtr = NULL;
ListNode *prevPtr = NULL;
while (fastPtr)
{
counter++;
//one chain formed from list,reverse it
if (counter == k)
{
fastPtr = fastPtr->next;
while (counter)
{
nextPtr = currentPtr->next;
currentPtr->next = prevPtr;
prevPtr = currentPtr;
currentPtr = nextPtr;
counter--;
}
//last node
if (currentPtr->next == NULL)
{
currentPtr->next = prevPtr;
prevPtr = currentPtr;
break;
}
}
else
{
fastPtr = fastPtr->next;
}
}
return prevPtr;
}
解决方法
>它给出了整个列表的输出反转....
这是因为下次控件进入此if
块
if(counter == k)
在操作列表时,指针prevPtr
仍指向列表中与上一次迭代所指向的节点相同的节点。反转一组prevPtr
节点时,需要将NULL
设置为k
。与此同时,您需要明确地处理列表的head
和tail
。列表的head
将是颠倒的k
节点的第一组的k
th 节点,列表的tail
将是第一个节点当列表中的节点是k
的倍数时,最后一组k
节点的;如果列表中的节点不在{{1}的倍数中,它将指向其余列表的第一个节点}。反转每个k
节点组时,需要注意列表中的tail
。
在处理上述细节的同时粗略地修改了代码:
k
您可以使用递归来反转列表中ListNode* reverseKGroup(ListNode* head,int k)
{
if(k==0 || k==1)
return head;
if(head == NULL)
return NULL;
int counter = 0;
ListNode* fastPtr = head;
ListNode* currentPtr = head;
ListNode* nextPtr = NULL;
ListNode* prevPtr = NULL;
ListNode* tail = NULL;
ListNode* currLast = NULL;
int set_head = 0;
int set_currLast = 0;
while(fastPtr)
{
counter++;
//one chain formed from list,reveser it
if(counter == k)
{
fastPtr = fastPtr->next;
prevPtr = NULL;
set_currLast = 0;
while(counter)
{
nextPtr = currentPtr->next;
currentPtr->next = prevPtr;
// when reversing group of k nodes,the first node will be
// the last when whole group reversed
if (!set_currLast)
{
currLast = currentPtr;
set_currLast = 1;
}
prevPtr = currentPtr;
currentPtr = nextPtr;
counter--;
}
// Need to set head just once only
if (!set_head) {
tail = head;
head = prevPtr;
set_head = 1;
} else {
// the tail need to set after reversing every k nodes group
tail->next = prevPtr;
tail = currLast;
}
// For the last group which will be of size less than k
tail->next = nextPtr;
}
else
{
fastPtr = fastPtr->next;
}
}
return head;
}
个节点组中的节点。该代码看起来很干净,更容易理解:
k
,
您的方法还可以,但是您没有正确链接反向列表片段:
-
您可以将
fastPtr = fastPtr->next;
分解为if
语句的两个分支。 -
最后一个节点的测试是虚假的:如果列表长度是
if (currentPtr->next == NULL)
的倍数,k
将取消引用空指针。 -
您应该保留一个指向反向列表片段头部存储位置的指针。在循环开始时,该指针指向变量
head
,在每个片段反转之后,它应指向反转之前片段第一个节点的next
成员,即CurrentNode
在反转循环的开始。
通过这些小的修改,您的代码可以正常运行。
请注意,这个问题很棘手。如果面试官要求您以交互方式解决问题,则他们可能会对您解决问题的方法感兴趣。在不到半小时的时间内构造出有效而优雅的解决方案将是非常好的。
这是带有测试床的修改版:
#include <stdio.h>
typedef struct ListNode {
struct ListNode *next;
int data;
} ListNode;
ListNode *reverseKGroup(ListNode *head,int k) {
if (k > 1) { // no need to test for `head != NULL`
int counter = 0;
ListNode **start = &head; // place where to store the head of the reversed fragment
ListNode *currentPtr = head; // pointer to the first node of the fragment
ListNode *fastPtr = head; // pointer to the node after the end of the fragment
while (fastPtr) {
fastPtr = fastPtr->next;
counter++;
if (counter == k) {
// k nodes between CurrentPtr and fastPtr: reverse the fragment
ListNode *lastPtr = currentPtr;
ListNode *prevPtr = fastPtr;
while (counter) {
ListNode *nextPtr = currentPtr->next;
currentPtr->next = prevPtr;
prevPtr = currentPtr;
currentPtr = nextPtr;
counter--;
}
*start = prevPtr;
start = &lastPtr->next;
}
}
}
return head;
}
void printList(const char *prefix,const ListNode *p,const char *suffix) {
while (p) {
printf("%s%d",prefix,p->data);
prefix = ",";
p = p->next;
}
printf("%s",suffix);
}
ListNode *test(ListNode *list,int k) {
printf("reverseKGroup(%d): ",k);
list = reverseKGroup(list,k);
printList("",list,"\n");
return reverseKGroup(list,k); // undo k-reversal
}
int main() {
ListNode a[10],*list = a;
for (int i = 0; i < 10; i++) {
a[i].next = i + 1 < 10 ? &a[i + 1] : NULL;
a[i].data = i + 1;
}
for (int k = 0; k <= 11; k++) {
list = test(list,k);
}
return 0;
}
输出:
reverseKGroup(0): 1,2,3,4,5,6,7,8,9,10 reverseKGroup(1): 1,10 reverseKGroup(2): 2,1,10,9 reverseKGroup(3): 3,10 reverseKGroup(4): 4,10 reverseKGroup(5): 5,6 reverseKGroup(6): 6,10 reverseKGroup(7): 7,10 reverseKGroup(8): 8,10 reverseKGroup(9): 9,10 reverseKGroup(10): 10,1 reverseKGroup(11): 1,10,
最初,面试官问这个问题来扭转链接 清单,我很容易解决。现在他说要扭转小组名单 K个节点。
对于初学者,切勿在面试中做任何测试作业。禁止他人操纵您和您的时间。忽略提供测试任务的公司。
例如,对于任务而言,使用递归函数不是一个好主意,因为对于大列表,您可能会出现堆栈溢出。
另外,引入一种用于声明列表本身的结构会更好。
我可以提出以下解决方案。
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *next;
};
struct List
{
struct Node *head;
};
int push_front( struct List *list,int data )
{
struct Node *new_node = malloc( sizeof( struct Node ) );
int success = new_node != NULL;
if ( success )
{
new_node->data = data;
new_node->next = list->head;
list->head = new_node;
}
return success;
}
void clear( struct List *list )
{
while ( list->head != NULL )
{
struct Node *tmp = list->head;
list->head = list->head->next;
free( tmp );
}
}
FILE * display( const struct List *list,FILE *fp )
{
for ( const struct Node *current = list->head; current != NULL; current = current->next )
{
fprintf( fp,"%d -> ",current->data );
}
fputs( "null",fp );
return fp;
}
void reverse_n( struct List *list,size_t n )
{
if ( !( n < 2 ) )
{
for( struct Node **first = &list->head,*last = list->head; last != NULL; )
{
for ( size_t i = n; --i != 0 && last; )
{
last = last->next;
}
if ( last != NULL )
{
struct Node *next = ( *first )->next;
( *first )->next = last->next;
last = *first;
for ( size_t i = n; --i != 0; )
{
struct Node *tmp = next;
next = next->next;
tmp->next = *first;
*first = tmp;
}
first = &last->next;
last = last->next;
}
}
}
}
int main(void)
{
struct List list = { .head = NULL };
const int N = 10;
for ( size_t i = 2; i < N + 1; i++ )
{
for ( int j = N; j != 0; --j )
{
push_front( &list,j - 1 );
}
putc( '\n',display( &list,stdout ) );
reverse_n( &list,i );
putc( '\n',stdout ) );
putc( '\n',stdout );
clear( &list );
}
return 0;
}
程序输出为
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
1 -> 0 -> 3 -> 2 -> 5 -> 4 -> 7 -> 6 -> 9 -> 8 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
2 -> 1 -> 0 -> 5 -> 4 -> 3 -> 8 -> 7 -> 6 -> 9 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
3 -> 2 -> 1 -> 0 -> 7 -> 6 -> 5 -> 4 -> 8 -> 9 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
4 -> 3 -> 2 -> 1 -> 0 -> 9 -> 8 -> 7 -> 6 -> 5 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
5 -> 4 -> 3 -> 2 -> 1 -> 0 -> 6 -> 7 -> 8 -> 9 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> 7 -> 8 -> 9 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> 8 -> 9 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> 9 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> null