day53 计算机考研408真题2019年41题(C++)

389 阅读3分钟

题目来源: 计算机考研408真题2019年41题

题目描述:

  • 描述: 41.(13分)设线性表L=(a1,a2,a3,...,an2,an1,an)L=(a_1,a_2,a_3,...,a_{n-2},a_{n-1},a_n)采用带头结点的单链表保存,链表中的结点定义如下:
typedef struct node
{ 
    int data;
    struct node*next;
} NODE;

请设计一个空间复杂度为O(1)且时间上尽可能高效的算法,重新排列L中的各结点,得到线性表L=(a1,an,a2,an1,a3,an2,...)L'=(a_1,a_n,a_2,a_{n-1},a_3,a_{n-2},...)。 要求:
1)给出算法的基本设计思想。
2)根据设计思想,采用C、C++或Java语言描述算法,关键之处给出注释.
3)说明你所设计算法的时间复杂度和空间复杂度。

思路:双指针

  • 1.先进行审题,找规律: L=(a1,a2,a3,...,an2,an1,an)L=(a_1,a_2,a_3,...,a_{n-2},a_{n-1},a_n)L=(a1,an,a2,an1,a3,an2,...)L'=(a_1 , a_n,a_2,a_{n-1},a_3,a_{n-2},...),发现L'是由L摘取第一个元素,再摘取倒数第一个元素……依次合并而成的
  • 2.对于"摘取倒数第一个元素"这个操作,会发现仅仅是与链表后半部分的元素有关,又因为要求空间复杂度O(1),所以,可以采用将后半段链表原地逆置
    • 先找出链表L的中间结点,为此设置两个指针p和q,指针p每次走一步,指针q每次走两步,当指针q到达链尾时,指针p正好在链表的中间结点
    • 将L的后半段结点原地逆置
    • 从单链表前后两段中依次各取一个结点,按要求重排

具体实现:

#include <iostream>

using namespace std;

typedef struct Node {
    int data; //数据域
    struct Node *next; //指针域
} Node,*Linklist;

void show(Linklist L){  //Node* 和 Linklist一样
    while(L->next != NULL){
        cout<<L->next->data;
        L = L->next;
    }

}

Linklist List_HeadInsert(Linklist &L){     //创建链表
    Node *s;
    int x;
    L = (Linklist)malloc(sizeof (Node));
    L->next = NULL;
    cin>>x;
    while(x!=9999){     //如果x的值是9999,那么就结束链表的创建
        s=(Node*) malloc(sizeof (Node));
        s->data = x;
        s->next = L->next;
        L->next = s;
        cin>>x;
    }
    return L;
}

void change_list(Linklist& h) {   //Node* 和 Linklist一样
    Node *p, *q, *r, *s;
    p = q = h;
    while (q->next != NULL) {    //寻找中间结点
        p = p->next;             //p走一步
        q = q->next;             //q走一步
        if(q->next != NULL)
            q = q->next;         //q再走一步,前后每次共走两步
    }
    q = p->next;             //p所指结点为中间结点,q为后半段链表的首结点
    p->next = NULL;
    while(q!=NULL){          //将链表后半段逆置
        r = q->next;
        q->next = p->next;
        p->next = q;
        q = r;
    }
    s = h->next;            //s指向前半段的第一个数据结点,即插入点
    q= p->next;             //q指向后半段的第一个数据结点
    p->next = NULL;
    while(q!=NULL){         //将链表后半段的结点插入到指定位置
        r = q->next;        //r指向后半段的下一个结点
        q->next = s->next;  //将q所指结点插入到s所指结点之后
        s->next = q;
        s = q->next;        //s指向前半段的下一个插入点
        q = r;
    }
}

int main() {     //测试
    Linklist head;
    Linklist linklist = List_HeadInsert(head);
    show(head); 
   
    change_list(linklist);
    cout<<"成果展示:"<<endl;
    show(head);

}

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 27 天,点击查看活动详情