线性表

本文介绍了线性表的概念,包括前驱元素和后驱元素,并讨论了线性表的两种存储结构——顺序存储和链式存储。顺序存储适用于查找,而链式存储适合于插入与删除。文章重点讲述了单循环链表和双向链表的实现,强调了使用尾指针的优势,并提供了创建双向链表的方法。此外,还提及了双向链表与循环链表的结合。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线性表:零个或多个数据元素的有限序列。
关于线性表,我们有两个概念需要知道,分别是前驱元素和后驱元素。
在这里插入图片描述
这两个概念应用十分广泛,无论是单链表,双向链表,循环链表,还是模拟链表都有用到其中的概念。

线性表的存储形式:
1.顺序存储结构。
2.链式存储结构。
其实第一种就是按照数组存,第二种就是链表存罢了。
第一种更适合于查找而不是插入与删除,在查找中他的时间复杂度为O(1),而在插入或者删除时他的时间复杂度为O(n)。
第二种则相反,更适合于对链表进行操作,但一般情况下它查找的时间复杂度为O(1),删除与插入的时间复杂度为O(n)。

关于单链表的各种操作以及静态链表(就是用数组模拟的链表)在我其他的博客中有,此篇中不再赘述。此篇主要说双向链表以及加入循环链表。

循环链表:将单链表中终端结点的指针由空指针改为指向头节点,这种头尾相接的单链表就被称为单循环链表,简称循环链表。
单循环链表与单链表十分相似,代码内容也很相近,只是将最后的指针指回头结点了而已。在这里我们一般不使用头指针,因为如果要找到最后一个元素的话,需要从头遍历到尾,时间复杂度为O(n)。我们使用尾指针,使用尾指针的好处是可以从尾开始,也可以很快从头开始,此时要遍历到尾部只用一次赋值即可。
在这里插入图片描述
从这里可以看出使用尾指针的好处,可是在我们创建链表时,我们只能返回一个指针,但如果我们即想要头指针也想要尾指针该怎么办呢。
这里解释两种办法:1.可以将所定义的指针定义为全局变量。2.可以定义一个结构体,里面存放两种指针。

双向链表:双向链表是在单链表的每个结点中,在设置一个指向前驱指针的指针域。
结构为:

struct node
{
    int num;
    struct node *last;
    struct node *next;
};

我们现在来从单链表开始创建一个普通的双向链表

#include <iostream>
#include<cstdio>
#include<cstdlib>
#include<malloc.h>
#define N sizeof(struct node)
using namespace std;

struct node
{
    int num;
    struct node *last;
    struct node *next;
};
struct node *creat()
{
    struct node *p1,*p2,*head;
     head=p2=(struct node *)malloc(N);
    p1=(struct node *)malloc(N);
    scanf("%d",&p1->num);


    //p1->last=head;
    while(p1->num!=0)
    {
        p2->next=p1;
        p1->last=p2;
        p2=p1;
        p1=(struct node *)malloc(N);
        scanf("%d",&p1->num);
    }
    p2->next=NULL;
    free(p1);
    return head;
};
void print(struct node *p0)
{
    struct node *p1;
    p1=p0->next;
    while(p1!=NULL)
    {
        printf("%d ",p1->num);
        p1=p1->next;
    }
}
int main()
{
    struct node *head;
    head=creat();
    print(head);
    return 0;
}

这样的双向链表是没有什么用的,他和单链表和接近,距离我们成熟的双向链表还有一段距离,我们不妨优化他一下,顺便用刚才说的同时需要两个指针的方法。

struct node
{
    int num;
    struct node *last;
    struct node *next;
};
struct stud
{
    struct node *head;
    struct node *tail;
};
struct stud q1,q2;    //为了存头指针与尾指针(定义成全局变量)
void creat()
{
    struct node *p1,*p2,*head,*tail;

     head=p2=(struct node *)malloc(N);
    p1=(struct node *)malloc(N);
    scanf("%d",&p1->num);
    //p1->last=head;
    while(p1->num!=0)
    {
        p2->next=p1;
        p1->last=p2;
        p2=p1;
        p1=(struct node *)malloc(N);
        scanf("%d",&p1->num);
    }
    p2->next=NULL;
    tail=p2;
    free(p1);
    q1.head=head;
    q1.tail=tail;
};
void print(struct node *p0)
{
    struct node *p1;
    p1=p0->next;
    while(p1!=NULL)
    {
        printf("%d ",p1->num);
        p1=p1->next;
    }
}
void print1(struct node *p0)
{
    struct node *p1;
    p1=p0;
    while(p1!=q1.head)
    {
        printf("%d ",p1->num);
        p1=p1->last;
    }
}
int main()
{
    creat();
    //print(q1.head);
    print1(q1.tail);
    return 0;
}

现在的这个链表就比刚才的完善许多了。
他的插入和删除和单链表很接近,只不过麻烦一些,在写是要注意顺序。
插入:
在这里插入图片描述
删除:
在这里插入图片描述

双向链表是可以和循环链表相结合的,只要改很少就可以做到。
本篇文章部分图摘取于 “程杰《大话数据结构》清华大学出版社”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值