2019年专业408的算法题

15 篇文章 0 订阅

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) On
空间复杂度: O ( 1 ) O(1) O1

附录

408历年真题算法题解析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

繁星蓝雨

如果觉得文章不错,可以请喝咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值