0 结果
对于奇数:
对于偶数:
1 题目
2 思路
因为空间复杂度要求为O(1),因此不能使用数组保存链表结点。
2.1 思路1(较优解:一半尾插法)
直接将后半段链表取下来每个结点使用尾插法插入。
例子1(奇数个元素):
1,2,3,4,5,6,7
-
分段
前半段:t = (n+1)/2,即4个元素
后半段:7- 4 = 3个元素 -
插入
把后半段的元素依次使用头插法插入到前半段元素中,
把5插入到movNum = t - j(j从0,即第4个元素的后面)前半段的位置后面;
把6插入到movNum–(即第三个元素的后面)前半段的位置后面;
把7插入到movNum–(即第二个元素的后面)前半段的位置后面。
例子2(偶数个元素):
1,2,3,4,5,6
- 分段
前半段(t = (n+1)/2 = 3):1,2,3
后半段:4,5,6 - 插入
4插入到3(movNum = t - j = 3)的后面,
5插入到2(t - j = 3)的后面,
6插入到1(t - j = 3)的后面。
实现:
#include<iostream>
const int MAXN = 7;
typedef struct node{
int data;
struct node* next;
}NODE;
void ans(NODE* &head, int n){
int t = (n + 1)/2;
node *p, * q, *pre;
pre = head;
for(int i = 0;i < t;i++){//指向前半段第一个结点
pre = pre->next;
}
q = pre->next;//指向后半段的第一个结点
pre->next = nullptr;//前半段的尾结点指向空,以便后半段元素插入
int movNum = n - t;//移动步数
for(int i= 0;i < n - t;i++){//这里n-t指后半段的长度(固定的)
pre = head;//每次都从前半段的头结点开始寻找插入的位置
for(int j = 0; j < movNum;j++){//找到前半段中插入后半段结点的位置
pre = pre->next;
}
p = q->next;//暂存后半段插入结点的后一个结点(防止后半段断链)
q->next = pre->next;
pre->next = q;
q = p;//更新q
movNum--;
}
}
//使用尾插法创建结点
void create(NODE* &L){
NODE* r = L;
for(int i = 1;i <= MAXN;i++){
NODE *s = (NODE*)malloc(sizeof(NODE));
s->data = i;
r->next = s;
r = s;
}
r->next = NULL;
}
//打印数据
void print(NODE* L){
NODE* p = L->next;
while(p!=NULL){
printf("%d ", p->data);
p = p->next;
}
}
int main(){
NODE* L;
L = (NODE*)malloc(sizeof(NODE));
L->next = nullptr;
create(L);
ans(L,MAXN);
print(L);
return 0;
}
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
空间复杂度:
O
(
1
)
O(1)
O(1)
2.2 思路2(最优解:多次逆置)
- 1 首先找到链表的中间结点(使用双指针法(一个指针走一步,另一个指针走两步)找到中间节点位置(即后半段第一个结点的前驱));
- 例如
- 偶数个数据对于1,2,3,4,5,6,中间结点为3,第一段数据为1,2,3,第二段数据为4,5,6;
- 奇数个数据对于1,2,3,4,5,中间结点为3,第一段数据为1,2,3,第二段数据为4,5;
- 2 把后半段数据逆置,使用头插法或者原地逆置法;
- 3 把逆置后的后半段数据一次插入到前半段数据中。
⚠️备注:
原地逆置法:将结点的next指向其原结点的前驱。特殊处理第一个结点时,把其next域置空,因为它最后变成了尾结点;处理完最后一个结点后,把头结点的指针指向它。
例子1:
1,2,3,4,5,6,7
- 使用双指针法找到后半段第一个元素的前驱,这里找到的是4;
- 将后半段元素原地逆置,原元素序列变为1,2,3,4,7,6,5
- 将后半段元素7,6,5依次插入到1,2,3元素的后面
#include <cstdio>
#include <cstdlib>
typedef struct node{
int data;
struct node* next;
}NODE;
//使用头插法逆置
void func(NODE* &L){
NODE *p = L, *q = L, *r;
while(q->next != NULL){//让q指向中间结点
p = p->next;
q = q->next;
if(q->next != NULL){
q = q->next;
}
}
q = p->next;//让q指向中间结点,第二段的第一个结点
p->next = NULL;//前半段,断链
while(q != NULL){//对第二段使用头插法插入到第一段的最后一个结点后
r = q->next;
q->next = p->next;
p->next = q;
q = r;
}
q = p->next;//让q指向倒置后的第二段第一个结点
p->next = NULL;//让第一段和第二段断开,使得插入结点后尾结点指向空
p = L->next;
while (q != NULL){//把第二段数据一次插入到第一段中
r = q->next;//防止第二段断链,提前保存第二段数据
q->next = p->next;//插入结点
p->next = q;
p = q->next;//让p指向第一段插入的下一个位置
q = r;//q指向第二段插入的下一个位置
}
}
//使用原地逆置法逆置
void func2(NODE* &L){
NODE *p = L, *q = L, *r, *s;
while(q->next != NULL){//让q指向中间结点
p = p->next;
q = q->next;
if(q->next != NULL){
q = q->next;
}
}
q = p->next;//让q指向中间结点,第二段的第一个结点
p->next = NULL;//前半段,断链
r = q->next;
q->next = NULL;//让第二段倒置后的尾指针为空
while(r != NULL){//三个指针实现原地逆置,结束时,p指向倒置后第二段第一个结点
s = q;
q = r;
r = r->next;
q->next = s;
}
p->next = NULL;//让第一段和第二段断开,使得插入结点后尾结点指向空
p = L->next;
while (q != NULL){//把第二段数据一次插入到第一段中
r = q->next;//防止第二段断链,提前保存第二段数据
q->next = p->next;//插入结点
p->next = q;
p = q->next;//让p指向第一段插入的下一个位置
q = r;//q指向第二段插入的下一个位置
}
}
//使用尾插法创建结点
void create(NODE* &L){
NODE* r = L;
for(int i = 1;i < 7;i++){
NODE *s = (NODE*)malloc(sizeof(NODE));
s->data = i;
r->next = s;
r = s;
}
r->next = NULL;
}
//打印数据
void print(NODE* L){
NODE* p = L->next;
while(p!=NULL){
printf("%d ", p->data);
p = p->next;
}
}
int main(){
NODE* L;
L = (NODE*)malloc(sizeof(NODE));
L->next = NULL;
create(L);
func2(L);
print(L);
return 0;
}
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)