每日练习-2022-2-2-牛客NC2-重排链表

描述

来源:牛客-名企高频面试题高频TOP200

将给定的单链表L:L0->L1->…->Ln-1->Ln

重新排序为:L0->Ln->L1->Ln-1->L2->Ln-2->…

要求使用原地算法,不能只改变节点内部的值,需要对实际的节点进行交换。

数据范围:链表长度 0 <= n <= 20000 ,链表中每个节点的值满足 0 <= val <= 1000

要求:空间复杂度 O(n)并在链表上进行操作而不新建链表,时间复杂度 O(n)

进阶:空间复杂度 O(1) , 时间复杂度 O(n)

答案及解析

1. 数组法

将链表存储于数组当中,通过下标重建链表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void reorderList(ListNode* head)
{
if ( !head )
return;
vector<ListNode*>vec;
while ( head )
{
vec.push_back(head);
head = head->next;
}
auto i = 0;
auto j = vec.size() - 1;
while ( i < j )
{
vec[i]->next = vec[j];
++i;
if ( i == j )
break;
vec[j]->next = vec[i];
--j;
}
vec[i]->next = nullptr;
return;
}

时间复杂度O(N):N是链表的结点数量,遍历链表、数组构建新链表

空间复杂度O(N):使用额外的数组空间

2. 寻找链表中点 + 链表逆序 + 合并链表

1、找中点的话,可以使用快慢指针。快指针一次走两步,慢指针一次走一步,当快指针走到终点的话,慢指针会刚好到中点。如果节点个数是偶数的话,slow 走到的是左端点,利用这一点,我们可以把奇数和偶数的情况合并,不需要分开考虑。

2、链表逆序的话,参考反转链表

3、合并链表,两个指针分别向后移动就可以。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
ListNode* ReverseList(ListNode* pHead)
{//反转链表
ListNode* nHead = nullptr;
ListNode* node = nullptr;
while ( pHead )
{
node = pHead;
pHead = pHead->next;
node->next = nHead;
nHead = node;
}
return nHead;
}
void reorderList(ListNode* head)
{
if ( !head || !head->next || !head->next->next )
return;
ListNode* slow = head;
ListNode* fast = head;
while ( fast->next && fast->next->next )
{
slow = slow->next;
fast = fast->next->next;
}
ListNode* nHead = slow->next;
slow->next = nullptr;
nHead = ReverseList(nHead);
while ( nHead )
{
ListNode* tmp = nHead->next;
nHead->next = head->next;
head->next = nHead;
head = nHead->next;
nHead = tmp;
}
}

时间复杂度O(N):N是链表的结点数量,遍历链表、重组链表

空间复杂度O(1):使用常数级空间指针