考研408 2019年 第41题 链表代码及其详解(数据结构)

本文详细介绍了链表的尾插法、打印、找到中间节点并创建新链表、逆转链表和合并两个链表的C++实现,并分析了find_middle、reverse和merge函数的时间复杂度均为O(n/2)。
摘要由CSDN通过智能技术生成
#include <stdio.h>
#include <stdlib.h>

typedef int ElemType;
typedef struct Lnode
{
    ElemType data;// int型数据域
    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;
        r=s;//r要指向新的尾部
        scanf("%d",&x);
    }
    r->next=NULL;//尾节点的next为NULL

}


//------链表打印-----------
void print_list(LinkLIst L) //只读不改动 不需要C++引用
{
    L=L->next;
    while(L!=NULL)
    {
        printf("%3d",L->data);
        L = L->next;
    }
    printf("\n");
}

//----注意!408初试并不考察以上插入以及打印结构体 是对关键函数的考察-----------

//断链
void find_middle(LinkLIst L,LinkLIst &L2)
{
    L2=(LinkLIst)malloc(sizeof(LNode));//(第二条链表的头节点) 为新结点申请头节点空间,头指针指向头节点
    LinkLIst pcur,ppre;//双指针遍历,考研初始常考
    ppre=pcur=L->next;
    while (pcur)
    {
        pcur=pcur->next;
        if(NULL==pcur)//为了防止pcur为NULL
        {
            break;
        }
        pcur=pcur->next;
        if(NULL==pcur)//为了使得偶数个,ppre依然指向a1,a2,到a6中的a3结点
        {
            break;
        }
        ppre=ppre->next;
    }
    L2->next=ppre->next;//由L2头节点指向后面一半链表
    ppre->next=NULL;//前一半链表的最后一个结点,next要为NULL
}

//逆转
void reverse(LinkLIst L2)//逆转L2这个结构体 头指针并不发生改变 故不需要引用
{
    LinkLIst r,s,t;
    r=L2->next;
    if(NULL==r)
    {
        return; //链表为空
    }
    s=r->next;
    if(NULL==s)
    {
        return; //链表只有一个结点
    }
    t=s->next;
    while (t)//如果t不为空
    {
        s->next=r;//t往后移的前提
        r=s;//以下三句是三个指针同时往后走一步
        s=t;
        t=t->next;

        L2->next->next=NULL;//逆置后,链表第一个结点的next要为NULL
        L2->next=s;//s才是新链表的表头
    }
    s->next=r;//t为NULL后倒数第二个还需要指向前一个完成最后的逆置
}

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


}
int main() {
    LinkLIst L;//L是链表头指针,是结构体指针类型  //考试可写L已初始化好 备注a1-an
    list_tail_insert(L);                //考试可省略
    print_list(L);                          //考试可省略
//-------以下为必要内容--------------

//寻找中间结点,并返回第二条链表
  LinkLIst L2=NULL;
    find_middle(L,L2);//只有一个结点时,L2中是没有结点的
    printf("-----------------------------\n");
    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;
}


判断其时间复杂度:

真题代码的三个函数find_middle()、reverse()、merge()的时间复杂度都为O(n/2),主要原因在于它们都是对链表进行遍历的操作,且每次遍历都跳过一些节点,使得实际遍历的节点数量为链表总节点数的一半。下面分别解释三个函数的时间复杂度:

当分析这三个函数的时间复杂度时,我们可以从每个函数的核心遍历部分入手:

  1. find_middle函数

    • 在find_middle中,使用了两个指针pcur和ppre,其中pcur每次移动两步,ppre每次移动一步。这样,当pcur到达链表末尾时,ppre正好到达链表的中间位置。由于每一步都在遍历节点,因此遍历的总次数约为链表长度的一半。因此,时间复杂度为O(n/2)。
  2. reverse函数

    • reverse函数采用了三个指针r、s、t来逆转链表。在while循环中,每次都将s的next指向r,然后三个指针依次往后移动。因为每次循环都处理两个节点,所以逆转整个链表的总次数约为链表长度的一半。因此,时间复杂度为O(n/2)。
  3. merge函数

    • merge函数通过两个指针p和q同时遍历两个链表,将节点逐个合并到一个新链表中。在每个循环中,都是将一个节点从链表中取出,并加入到新链表中。由于每次循环都处理两个节点,整个合并过程的总次数约为链表长度的一半。因此,时间复杂度为O(n/2)。

综上所述,这三个函数的核心思想是通过巧妙的指针移动和逻辑设计,在遍历链表时跳过一些节点,从而实现对链表的操作,使得时间复杂度都保持在O(n/2)的水平。这种设计有效地减少了遍历的总次数,提高了算法的效率。

上面三个函数总的运行次数是1.5n,忽略首项系数,因此时间复杂度是O(n)

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

陆小果不会写代码

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值