Round3—链表(1)(单链表)

声明:本篇博客,仅仅是记录平常的习题,不提倡从这里抄袭或者是贴代码,如果被查重,后果自负。

前置知识点:点我

判断:

1-1在具有N个结点的单链表中,访问结点和增加结点的时间复杂度分别对应为O(1)和O(N)。(T) (1分)

解析:在单链表当中访问结点需要依次访问每一个结点才能找到想要的结点,因为单链表并不是顺序表那样随机存储,不能通过基地址加上偏移量来直接访问,因此访问结点是O(n)。

在单链表当中插入元素,如果我们假设已经找到了插入位置,那么插入就是O(1),但是如果我们不知道插入的位置,需要查找的话,那么时间复杂度就是O(n)。

同时这也说明了对于链表来说,访问困难而插入简单

1-2若用链表来表示一个线性表,则表中元素的地址一定是连续的。 (F)(1分)

解析:顺序表的逻辑结构与物理结构是一致的,即逻辑上连续并且存储上也是连续的,而链表的逻辑结构是连续的,物理结构上不一定是连续的。

1-3将长度分别为m,n的两个单链表合并为一个单链表的时间复杂度为O(m+n)。 (F)(1分)

解析:我们来考虑两种情况。

第一种,将长度链表为m的链表连接到长度为n的链表的后面,那么我们可以一次遍历长度为n的链表找到链表的尾结点,然后直接将长度为m的链表连接在长度为n的链表的后面,因此时间复杂度就是O(n),反之时间复杂度就是O(m)。

第二种,两个链表的元素是有序的,并且要求合并之后的链表的元素也是有序的,那么我们定义两个指针来分别指向两个链表的当前元素,谁小选择谁(假设是从小到大),这样的话遍历的时间复杂度就是O(m + n),最后将剩余的部分直接接到后面就可以了,因此整个合并的操作的时间复杂度就是O(m + n)。

1-4单链表不是一种随机存取的存储结构。 (T)(1分)

解析:顺序表才是一种随机存储结构,因为可以通过基地址加上偏移量来直接访问表当中的元素。

选择:

2-1设h为不带头结点的单向链表。在h的头上插入一个新结点t的语句是:(2分)

  • h=t; t->next=h->next;
  • t->next=h->next; h=t;
  • h=t; t->next=h;
  • t->next=h; h=t;

解析:因为h是不带头结点的单链表,那么我们每一次动态申请新的节点之后,只需让当前结点变成首元节点即可,因此

t->next = h,这个操作的含义就是,将新的节点t接到整个链表的前面。h=t,是为了让单链表的指针永远指向第一个元素。

2-2在单链表中,若p所指的结点不是最后结点,在p之后插入s所指结点,则执行 (2分)

  • s->next=p; p->next=s;
  • s->next=p->next; p=s;
  • s->next=p->next; p->next=s;
  • p->next=s; s->next=p;

解析:为了在结点p的后面插入结点,我们需要将p后面的全部结点,接到s后面,然后再将s接到p的后面就行了。

因此,第一步就是s->next = p->next,这一步就是将p后面的全部结点都接到s的后面。

第二步,将s接到p后面,p->next = s。

2-3带头结点的单链表h为空的判定条件是: (2分)

  1. h == NULL;
  2. h->next == NULL;
  3. h->next == h;
  4. h != NULL;

解析:带头结点的单链表我们需要搞清楚三个概念,头指针,头结点,首元结点。

头指针指向的永远都是第一个元素,如果这个单链表有头结点,那么就是指向头结点,头结点的next指针指向首元结点。如果没有头结点,那么就是指向首元结点。

其中首元结点,才是真正的第一个存储数据元素的节点。

因此带头结点的单链表判断为空的条件就是,头结点的next指针指向空,也就是没有首元结点。

2-4将两个结点数都为N且都从小到大有序的单向链表合并成一个从小到大有序的单向链表,那么可能的最少比较次数是: (2分)

  • 1
  • N
  • 2N
  • NlogN

解析:举两个例子就行了,第一个单链表就是1,2,3。第二个单链表是6,7,8。

由此可以发现最少的比较次数就是N。

2-5线性表若采用链式存储结构时,要求内存中可用存储单元的地址 (1分)

  • 必须是连续的
  • 连续或不连续都可以
  • 部分地址必须是连续的
  • 一定是不连续的

解析:现在应该都知道了吧。

2-6在具有N个结点的单链表中,实现下列哪个操作,其算法的时间复杂度是O(N)? (2分)

  • 在地址为p的结点之后插入一个结点
  • 删除开始结点
  • 遍历链表和求链表的第i个结点
  • 删除地址为p的结点的后继结点

解析:第一个跟第四个为什么不是O(n),因为现在我们知道的不是他是第几个结点而是知道这个结点的地址,因此我们可以O(1)的进行操作,删除开始结点其实也是,我们根据链表的指针可以直接删除开始结点。

2-7对于一个具有N个结点的单链表,在给定值为x的结点后插入一个新结点的时间复杂度为 (2分)

  • O(1)
  • O(N/2)
  • O(N)
  • O(N​2​​)

解析:要先遍历整个链表。

2-8链表不具有的特点是: (1分)

  • 插入、删除不需要移动元素
  • 方便随机访问任一元素
  • 不必事先估计存储空间
  • 所需空间与线性长度成正比

解析:第二个是顺序表的特点。

2-9在一个含有n个结点的有序单链表中插入一个新结点,使单链表仍然保持有序的算法的时间复杂度是( )。 (2分)

  • O(1)
  • O(log​2​​n)
  • O(n)
  • O(n​2​​)

解析:仍然保持有序是关键,因此要首先遍历整个链表,找到插入位置。

2-10将长度为n的单链表连接在长度为m的单链表之后的算法的时间复杂度为( )。 (2分)

  • O(1)
  • O(m)
  • O(n)
  • O(n+m)

解析:要先遍历链表找到链表的尾结点。

2-11在单链表中,增加一个头结点的最终目的是为了( )。 (2分)

  • 使单链表至少有一个结点
  • 方便运算的实现
  • 标识表结点中首结点的位置
  • 说明单链表是线性表的链式存储

解析:其实我们可以发现,加入了头结点,我们整个运算都统一了起来,就算是没有首元结点。

2-12在单链表中,要删除某一指定结点,必须先找到该结点的()。 (2分)

  • 直接前驱
  • 自身位置
  • 直接后继
  • 直接后继的后继

解析:删除这个结点,就是将这个结点的前一个结点的next指针指向这个结点的下一个结点,但是单链表是单向的,因此我们必须要知道这个节点的直接前驱。

2-13以下关于链式存储结构的叙述中,()是不正确的。 (2分)

  • 结点除自身信息外还包括指针域,因此存储密度小于顺序存储结构
  • 逻辑上相邻的结点物理上不必邻接
  • 可以通过计算直接确定第i个结点的存储地址
  • 插入、删除运算操作方便,不必移动结点

解析:这个特点是顺序表的。

2-14线性链表不具有的特点是()。 (2分)

  • 随机访问
  • 不必事先估计所需存储空间大小
  • 插入与删除时不必移动元素
  • 所需空间与线性长度成正比

解析:同上。

2-15线性表若采用链式存储结构时,要求内存中可用存储单元的地址()。 (2分)

  • 必须是连续的
  • 部分地址必须是连续的
  • 一定是不连续的
  • 连续或不连续都可以

2-16对线性表,在下列情况下应当采用链表表示的是()。 (2分)

  • 经常需要随机地存取元素
  • 经常需要进行插入和删除操作
  • 表中元素需要占据一片连续的存储空间
  • 表中的元素个数不变

解析:因为链表的删除操作是o(1)的,并且在运行时不容易出现错误。

2-17不带表头附加结点的单链表为空的判断条件是头指针head满足条件()。 (2分)

  • head==NULL
  • head->next==NULL
  • head->next== head
  • head!=NULL

解析:带头结点是头结点的next指向空,那么不带头结点的就是链表的指针指向空。

2-18可以用带表头附加结点的链表表示线性表,也可以用不带头结点的链表表示线性表,前者最主要的好处是()。

  • 可以加快对表的遍历
  • 使空表和非空表的处理统一
  • 节省存储空间
  • 可以提高存取表元素的速度

函数题目:

题目大意:

本题要求编写函数实现带头结点的单链线性表的就地逆置操作函数。L是一个带头结点的单链表,函数ListReverse_L(LinkList &L)要求在不新开辟节点的前提下将单链表中的元素进行逆置,如原单链表元素依次为1,2,3,4,则逆置后为4,3,2,1。

裁判测试程序样例:

//库函数头文件包含
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>

//函数状态码定义
#define TRUE        1
#define FALSE       0
#define OK          1
#define ERROR       0
#define INFEASIBLE -1
#define OVERFLOW   -2

typedef int  Status;
typedef int  ElemType; //假设线性表中的元素均为整型

typedef struct LNode
{
    ElemType data;
    struct LNode *next;
}LNode,*LinkList;

Status ListCreate_L(LinkList &L,int n)
{
    LNode *rearPtr,*curPtr;   //一个尾指针,一个指向新节点的指针
    L=(LNode*)malloc(sizeof (LNode));
    if(!L)exit(OVERFLOW);
    L->next=NULL;               //先建立一个带头结点的单链表
    rearPtr=L;  //初始时头结点为尾节点,rearPtr指向尾巴节点
    for (int i=1;i<=n;i++){  //每次循环都开辟一个新节点,并把新节点拼到尾节点后
        curPtr=(LNode*)malloc(sizeof(LNode));//生成新结点
        if(!curPtr)exit(OVERFLOW);
        scanf("%d",&curPtr->data);//输入元素值
        curPtr->next=NULL;  //最后一个节点的next赋空
        rearPtr->next=curPtr;
        rearPtr=curPtr;
    }
    return OK;
}
void ListReverse_L(LinkList &L);
void ListPrint_L(LinkList &L){
//输出单链表
    LNode *p=L->next;  //p指向第一个元素结点
    while(p!=NULL)
    {
          if(p->next!=NULL)
               printf("%d ",p->data);
          else
               printf("%d",p->data);
          p=p->next;
    }
}
int main()
{
    LinkList L;
    int n;
    scanf("%d",&n);
    if(ListCreate_L(L,n)!= OK) {
          printf("表创建失败!!!\n");
          return -1;
    }
    ListReverse_L(L);
    ListPrint_L(L);
    return 0;
}
/* 请在这里填写答案 */

输入样例:

4
1 2 3 4

输出样例:

4 3 2 1

AC代码:


void ListReverse_L(LinkList &L)
{
    LNode *res, *temp;
    res = L->next;
    temp = L->next;
    L->next = NULL;
    while(temp != NULL)
    {
        temp = res->next;
        res->next = L->next;
        L->next = res;
        res = temp;
    }
}

解析:其实就是遍历,每一次将最前面的用前插法插入到头结点的后面即可。

编程题:

7-1 两个有序链表序列的合并 (20 分)

题目大意:

已知两个非降序链表序列S1与S2,设计函数构造出S1与S2合并后的新的非降序链表S3。

输入格式:

输入分两行,分别在每行给出由若干个正整数构成的非降序序列,用−1表示序列的结尾(−1不属于这个序列)。数字用空格间隔。

输出格式:

在一行中输出合并后新的非降序链表,数字间用空格分开,结尾不能有多余空格;若新链表为空,输出NULL

输入样例:

1 3 5 -1
2 4 6 8 10 -1

输出样例:

1 2 3 4 5 6 8 10

AC代码:

#include <bits/stdc++.h>
using namespace std;

typedef int ElemType;
struct LNode
{
    ElemType data;
    LNode *next;
    LNode(int data = 0, LNode *next = NULL) : data(data), next(next) {}
};
typedef LNode *LinkList;

void InitList(LinkList &link)
{
    link = new LNode();
    return ;
}

void UnionList(LinkList a, LinkList b, LinkList un)
{
    while(a && b)
    {
        if(a->data > b->data)
        {
            un->next = b;
            b = b->next;
            un = un->next;
        }
        else if(a->data <= b->data)
        {
            un->next = a;
            a = a->next;
            un = un->next;
        }
    }

    while(a)
    {
        un->next = a;
        a = a->next;
        un = un->next;
    }

    while(b)
    {
        un->next = b;
        b = b->next;
        un = un->next;
    }

    return ;
}

int main()
{
    ios::sync_with_stdio(false);
    LinkList Link_a, Link_b, Link_union;
    InitList(Link_a);
    InitList(Link_b);
    InitList(Link_union);
    LNode *a = Link_a, *b = Link_b, *un = Link_union;
    int x;
    while(cin >> x && x != -1)
    {
        LNode *p = new LNode(x);
        a->next = p;
        a = a->next;
    }

    while(cin >> x && x != -1)
    {
        LNode *p = new LNode(x);
        b->next = p;
        b = b->next;
    }

    a = Link_a->next;
    b = Link_b->next;
    UnionList(a, b, un);

    LNode *temp = Link_union->next;
    Link_union = Link_union->next;
    if(Link_union)
    {
        while(Link_union)
        {
            if(Link_union->next != NULL)
                cout << Link_union->data << " ";
            else
                cout << Link_union->data << endl;
            Link_union = Link_union->next;
        }
    }
    else
        cout << "NULL" << endl;
    while(temp)
    {
       LNode *t = temp;
       temp = temp->next;
       delete t;
    }
    return 0;
}

7-2 两个有序链表序列的交集 (20 分)

题目大意:

已知两个非降序链表序列S1与S2,设计函数构造出S1与S2的交集新链表S3。

输入格式:

输入分两行,分别在每行给出由若干个正整数构成的非降序序列,用−1表示序列的结尾(−1不属于这个序列)。数字用空格间隔。

输出格式:

在一行中输出两个输入序列的交集序列,数字间用空格分开,结尾不能有多余空格;若新链表为空,输出NULL

输入样例:

1 2 5 -1
2 4 5 8 10 -1

输出样例:

2 5

AC代码:

#include <bits/stdc++.h>
using namespace std;

typedef int ElemType;
struct LNode
{
    ElemType data;
    LNode *next;
    LNode(int data = 0, LNode *next = NULL) : data(data), next(next) {}
};
typedef LNode *LinkList;

void InitList(LinkList &link)
{
    link = new LNode();
    return ;
}

void InsertList(LinkList a, LinkList b, LinkList un)
{
    LinkList t = un;
    while(a && b)
    {
        if(a->data == b->data)
        {
            un->next = a;
            b = b->next;
            a = a->next;
            un = un->next;
            un->next = NULL;
        }
        else if(a->data < b->data)
            a = a->next;
        else if(b->data < a->data)
            b = b->next;
    }
    return ;
}

void Delete(LinkList a)
{
    LNode *temp = a->next;
    while(temp)
    {
       LNode *t = temp;
       temp = temp->next;
       delete t;
    }
}

int main()
{
    ios::sync_with_stdio(false);
    LinkList Link_a, Link_b, Link_insert;
    InitList(Link_a);
    InitList(Link_b);
    InitList(Link_insert);
    LNode *a = Link_a, *b = Link_b, *un = Link_insert;
    int x;
    while(cin >> x && x != -1)
    {
        LNode *p = new LNode(x);
        a->next = p;
        a = a->next;
    }

    while(cin >> x && x != -1)
    {
        LNode *p = new LNode(x);
        b->next = p;
        b = b->next;
    }

    a = Link_a->next;
    b = Link_b->next;
    InsertList(a, b, un);

    LNode *temp = Link_insert;
    Link_insert = Link_insert->next;
    if(Link_insert)
    {
        while(Link_insert)
        {
            if(Link_insert->next != NULL)
                cout << Link_insert->data << " ";
            else
                cout << Link_insert->data << endl;
            Link_insert = Link_insert->next;
        }
    }
    else
        cout << "NULL" << endl;

    Delete(Link_a);
    Delete(Link_b);
    //Delete(temp);
    return 0;
}

 

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值