线性表的链式存储结构

其实线性表的顺序存储结构最大的缺点就是插入和删除的时候需要移动大量的元素,这样的话会很耗费时间,为了解决这个问题,我们可以去是使用链式存储结构,但是链式存储结构就做不到随机访问了

线性表的链式存储结构的特点就是可以用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的,在链式的存储结构当中除了要存储数据元素信息之外,还要存储它的后继元素的存储地址

我们通常把存储数据元素信息的域称为是数据域,把存储直接后继位置的域称之为指针域,指针域中存储的信息称做指针域

链表当中的每个结点中只包含一个指针域,所以叫做单链表

线性表的链式存储结构_第1张图片

为了操作方便,我们一般会在单链表的第一个结点前面附加一个结点,我们称之为头结点,头结点的数据域可以不存储任何信息,也可以存储线性表的长度等信息,头结点的指针域指向第一个结点的指针

线性表的链式存储结构_第2张图片

下面再简单的看下头指针和头结点的区别

线性表的链式存储结构_第3张图片

关于单链表的删除、插入、获取其中的某一个值等操作的函数如下所示


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

//存储空间初始分配量
#define MAXSIZE 20

typedef int Status;
typedef int  ElemType;

typedef struct Node
{
    ElemType data;
    struct Node * next;
}Node;

//定义LinkList
typedef struct Node * LinkList;

//初始化顺序线性表
Status InitList(LinkList *L)
{
    *L = (LinkList)malloc(sizeof(Node));
    if(!(*L))
        return ERROR;
    (*L)->next = NULL;
    return OK;
}
//判断线性表是否为空,若L为空表,则返回TRUE,否则返回FALSE
Status ListEmpty(LinkList L)
{
    if(L->next)
        return FALSE;
    else
        return TRUE;
}
//顺序表已经存在,将L重置为空表,操作结果:返回L中数据元素个数
Status ClearList(LinkList *L)
{
    LinkList p,q;
    p = (*L)->next;
    while (p) {
        q = p->next;
        free(p);
        p=q;
    }
    (*L)->next = NULL;
    return OK;
}
//返回线性表的长度
int ListLength(LinkList L)
{
    int i=0;
    LinkList p = L->next;
    while (p) {
        i++;
        p=p->next;
    }
    return i;
}
//我们这里e返回L中第i个数据元素的值
Status GetElem(LinkList L,int i,ElemType *e)
{
    int j;
    //声明一结点P
    LinkList p;
    //让p指向链表L的第一个结点
    p=L->next;
    j=1;
    //p不为空或者计算机还没有等于i的时候,循环继续
    while (p&&j<i) {
        p = p->next;
        ++j;
    }
    //第i个元素不存在
    if(!p || j>i)
    {
        return ERROR;
    }
    //取第i个元素的数据
    *e = p->data;
    return OK;
}
//返回L中第1个与e相等的数据元素,若这样的数据元素不存在,则返回值0
int LocateElem(LinkList L,ElemType e)
{
    int i=0;
    LinkList p = L->next;
    while (p) {
        i++;
        if(p->data==e)
            return i;
        p=p->next;
    }
    return 0;
}
//在L中第i个位置之前插入新的数据元素e,L的长度加1
Status ListInsert(LinkList *L,int i,ElemType e)
{
    int j;
    LinkList p,s;
    p = *L;
    j=1;
    while (p && j<i) {
        p = p->next;
        ++j;
    }
    if(!p || j>i)
        return ERROR;

    s = (LinkList)malloc(sizeof(Node));
    //设置s结点的数值
    s->data = e;
    //将p的后继结点赋值给s的后继
    s->next = p->next;
    //将s赋值给p的后继
    p->next = s;
    return OK;
}
//删除L的第i个数据元素,用e返回其的值,L的长度减1
Status ListDelete(LinkList *L,int i,ElemType *e)
{
    int j;
    LinkList p,q;
    p = *L;
    j=1;
    /* 遍历寻找第i个元素 */
    while (p->next && j<i) {
        p = p->next;
        ++j;
    }
    //表示第i个元素不存在
    if (!(p->next) || j>i) {
        return ERROR;
    }
    q = p->next;
    //将q的后继结点赋值给p的后继
    p->next = q->next;
    //将q结点的数据给e
    *e = q->data;
    //让系统回收这个结点,释放内存
    free(q);
    return OK;
}
//依次输出L当中的每个数据元素
Status ListTraverse(LinkList L)
{
    LinkList p = L->next;
    while (p) {
        printf("%d ",p->data);
        p=p->next;
    }
    printf("\n");
    return OK;
}

//随机产生n个元素的值,建立带头结点的单链表,利用头插法
void CreateListHead(LinkList *L,int n)
{
    LinkList p;
    int i;
    //初始化随机种子
    /*
     时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数
     在C语言中可以通过time()函数获取到当前的秒数 参数为0则函数返回值即为结果,若参数不为0则结果保存在参数中
     */
    /*
     随机函数产生的是一种伪随机数,它实际是一种序列发生器,有固定的算法,只有当种子不同时,序列才不同,所以不应该把种子固定在程序中,应该用随机产生的数做种子,如程序运行时的时间等。
     以c++为例,应先用srand()设置不同种子,否则每次调用rand()得到的值是一样的。
     */
    srand(time(0));

    *L = (LinkList)malloc(sizeof(Node));
    (*L)->next = NULL;
    for (i=0; i<n; i++) {
        //生成新结点
        p = (LinkList)malloc(sizeof(Node));
        //随机生成100以内的数字
        p->data = rand()%100+1;
        //第一步的时候(*L)->next是为null的,然后我把null给了p->next,然后再把头结点的下一个结点给p,然后以此类推,不断的往下
        p->next = (*L)->next;
        //插入到表头当中
        (*L)->next = p;
    }
}
//利用尾插法去随机产生n个元素的值,建立带头结点的单链表
void CreateListTail(LinkList *L,int n)
{
    LinkList p,r;
    int i;
    //初始化随机种子
    srand(time(0));

    *L = (LinkList)malloc(sizeof(Node));
    //r为指向尾部的结点
    r = *L;
    for (i=0; i<n;i++) {
        p = (Node *)malloc(sizeof(Node));
        p->data = rand()%100+1;
        //将表尾终端结点的指针指向新结点
        r->next = p;
        //将当前的新结点定义为表尾终端结点
        r=p;
    }
    //表示当前链表结束
    r->next = NULL;
}

对于单链表结构和顺序存储结构的存储分配方式、时间性能、空间性能方面的对比如下所示
线性表的链式存储结构_第4张图片

如果线性表需要频繁的查找,很少进行插入和删除操作时,最好采用顺序存储结构,如果需要频繁的去插入和删除的时候,就最好去采用单链表结构

你可能感兴趣的:(数据结构)