【2019统考真题】设线性表L= (a1,a2,a3,...,an-2,an-1,an)采用带头结点的单链表保存,链表中的结点定义如下:
c
typedef struct node{
int data;
struct node*next;
}NODE;
请设计一个空间复杂度为0(1)且时间上尽可能高效的算法,重新排列L中的各结点,得到线性表 L2 = (a1,an,a2,an-1,a3,an-1...)
要求:
-
给出算法的基本设计思想。
-
根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
-
说明你所设计的算法的时间复杂度
题目信息:
- 空间复杂度为O(1)且时间上尽可能高效-----尽量不适用栈,队列等辅助结构
- 带有头节点,不用单独处理第一个结点
方法一:
c
void ans(Node* L, int n){
int t=(n+1)/2; //即n/2向上取整
Node* pre=L, p, q, qq; //q为指向后半段链的指针
for (int i=0; i<t; i++)
pre=pre->next; //pre指向a⌈n/2⌉
q=pre->next; //q指向a⌈n/2⌉+1
pre->next=null; //a⌈n/2⌉的下一个结点为空
len=n-t; //后一半链长度
for (int i=len; i>0; i--){ //一个一个重新插入
pre=L;
for (int j=0; j<i; j++) //找到插入位置
pre=pre->next;
p=pre->next; //pre是插入位置
pre->next=q; //插入q
qq=q->next; //qq暂存q的下一个结点
q->next=p; //q下一个结点是插入位置后的点
q=qq; //q指向qq所指结点
}
}
可这么理解:
给定一个单链表 L:L0→L1→...→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→...
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
给定链表 1->2->3->4, 重新排列为 1->4->2->3.
示例 2:
给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.
算法的基本思想:
- 快慢指针找中点,将原链表切分成两段
- 将后面的链表翻转
- 将第二个链表的每个结点依次插在第一个链表的后面
\
比如:1->2->3->4->5->null
切分变成:1->2->3->null ; 4->5->null ;
将 4->5 翻转变成5->4;
插入变成:1->5->2->4->3->null;
c
class Solution {
public:
void reorderList(ListNode* head) {
if (head == NULL || head->next == NULL)
return;
//快慢指针分出两段
ListNode* slow = head, *fast = head;
while (fast->next && fast->next->next) {
slow = slow->next;
fast = fast->next->next;
}
//后端反转
ListNode* needReverser = slow->next;
slow->next = NULL;
needReverser = reverse(needReverser);
//插入前端缝隙
ListNode* cur = head;
while (cur && needReverser) {
ListNode* curSecond = needReverser;
needReverser = needReverser->next;
ListNode* nextCur = cur->next;
curSecond->next = cur->next;
cur->next = curSecond;
cur = nextCur;
}
}
ListNode* reverse(ListNode* head) {
ListNode* p1 = NULL;
ListNode* p2 = head;
ListNode* p3 = p2;
while (p2) {
p3 = p2->next;
p2->next = p1;
p1 = p2;
p2 = p3;
}
return p1;
}
};