408数据结构2019年41题代码实战

文章介绍了如何使用C/C++实现一个空间复杂度为O(1)的算法,对给定的带头结点的单链表进行重新排列,形成(a1,an,a2,an-1,a3,an-2,...)的顺序。主要涉及尾插法创建链表、查找链表中间结点、链表反转和合并两个链表的操作,所有操作的时间复杂度均为O(n)。
摘要由CSDN通过智能技术生成
408数据结构2019年第41题代码实战

【题目】设线性表L=(a1, a2, a3, … , an-2, an-1, an)采用带头结点的单链表保存,链表中的结点定义如下:

typedef struct node{
    int data;
    struct node* next;
}NODE;

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

解答:

(1)设计思想见代码中的注释

(2)代码实战

#include <stdio.h>
#include<stdlib.h>

typedef int ElemType;
typedef struct LNode{
    ElemType data;//数据域
    struct LNode *next;//指针域
}LNode,*LinkList;

//LNode*是结构体指针,和LinkList完全等价的
//尾插法新建链表
void list_tail_insert(LNode* &L)
{
    L= (LinkList)malloc(sizeof(LNode));//申请头节点空间,头指针指向头节点
    L->next=NULL;
    ElemType x;
    scanf("%d",&x);
    LNode *s,*r=L;//s是用来指向申请的新结点,r始终指向链表尾部
    while(x!=9999)
    {
        s=(LinkList)malloc(sizeof(LNode));//为新结点申请空间
        s->data=x;
        r->next=s;//新结点给尾结点的next指针
        r=s;//r要指向新的尾部
        scanf("%d",&x);
    }
    r->next=NULL;//让尾结点的next为null

}

//打印
void print_list(LinkList L)
{
    L=L->next;
    while(L !=NULL )
    {
        printf("%3d",L->data);
        L=L->next;
    }
    printf("\n");
}

//找链表中间结点,并设置好L2链表
void find_middle(LinkList L,LinkList &L2)
{
    L2=(LinkList) malloc(sizeof(LNode));//第二条链表的头结点
    LinkList pcur,ppre;//双指针遍历,考研初试常考
    // pcur每次走两步,ppre每次走一步,当pcur到尾部时,ppre到达中间位置
    ppre=pcur=L->next;
    //pcur指向最后一个为null,结束循环
    while (pcur)
    {
        pcur=pcur->next;
        if(pcur==NULL)//为了防止pcur为空
        {
            break;
        }
        pcur=pcur->next;
        if(pcur==NULL)//为了使得偶数个,ppre依然指向中a1-a6中的a3
        {
            break;
        }
        ppre=ppre->next;
    }
    L2->next=ppre->next;//由L2头结点指向后面一半链表
    ppre->next=NULL;//前一段链表尾结点的next要为空
}

//链表反转
void reverse(LinkList L2)
{
    LinkList r,s,t;//r,s,t分别指向链表前三个结点
    r=L2->next;
    if(r==NULL)
    {
        return;//链表为空
    }
    s=r->next;
    if(s==NULL)
    {
        return;//链表只有一个结点
    }
    t=s->next;
    while(t)
    {
        s->next=r;//原地逆置
        r=s;//以下三句是3个指针向后走一步
        s=t;
        t=t->next;
    }
    s->next=r;
    L2->next->next=NULL;//逆置后,链表的第一个结点要为null
    L2->next=s;//逆置后,s是链表的第一个结点,L2指向他
}

//合并两个链表
void merge(LinkList L,LinkList L2)
{
    LinkList pcur,p,q;
    pcur=L->next;//pcur始终指向组合后新链表的链表尾
    p=pcur->next;//p指向L待放入结点(p遍历L链表)
    q=L2->next;//q指向L2待放入结点(q遍历L2链表)
    while(p!=NULL && q!=NULL)
    {
        pcur->next=q;
        q=q->next;//指向下一个
        pcur=pcur->next;
        pcur->next=p;
        p=p->next;//指向下一个
        pcur=pcur->next;
    }
    //任何一个链表都可能剩余一个结点,放进来即可
    if(p!=NULL)
    {
        pcur->next=p;
    }
    if(q!=NULL)
    {
        pcur->next=q;
    }
}

int main() {
    LinkList L;//链表头,是结构体指针类型
    list_tail_insert(L);
    print_list(L);//打印
    printf("--------------------------\n");
    //寻找中间结点,并返回第二条链表
    LinkList  L2=NULL;
    find_middle(L,L2);//只有一个结点的时候,L2是没有结点的
    print_list(L);
    print_list(L2);
    printf("--------------------------\n");
    reverse(L2);
    print_list(L2);
    printf("--------------------------\n");
    merge(L,L2);
    free(L2);
    print_list(L);
    return 0;
}

测试结果

1 2 3 4 5 6 9999
  1  2  3  4  5  6
--------------------------
  1  2  3
  4  5  6
--------------------------
  6  5  4
--------------------------
  1  6  2  5  3  4

(3)

  1. find_middle函数有一个while循环,pcur每次移动两个结点,循环的次数是n/2,忽略系数,所以时间复杂度是O(n)。
  2. reverse函数只遍历了L2链表,遍历长度是n/2,忽略系数,所以时间复杂度是O(n)。
  3. merg函数while循环遍历次数是n/2,忽略系数,所以时间复杂度是O(n)。

综上,三个函数总运行次数是1.5n,忽略系数,所以时间复杂度是O(n)。

参考答案:
在这里插入图片描述

在这里插入图片描述

  • 参考王道视频课程
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值