博客园  :: 首页  :: 新随笔  :: 管理

3.线性表

Posted on 2020-06-10 18:02  wsg_blog  阅读(229)  评论(0编辑  收藏  举报

index 数据结构与算法

ADT(array data table)线性表

Data
线性表的数据对象集合为{\(a_1,a_2,a_3...a_n\)},每个元素的类型均为DataType。其中,除第一个元素\(a_1\)外,每一个元素有且只有一个直接前驱元素,除了最后一个元素\(a_n\)外,每一个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系。


线性表-顺序存储结构(数组)

.h

InitList(*L);        初始化操作,建立一个空的线性表L。
ListEmpty(L);         若线性表为空,返回true,否则返回false。
ClearList(*L);        将线性表清空。
GetElem(L,i,*e);       将线性表L中的第i个位置元素值返回给e。
LocateElem(L,e);       在线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中序号表示成功;否则,返回0表示失败。
ListInsert(*L,i,e);    在线性表L中的第i个位置插入新元素e。
ListDelete(*L,i,*e);   删除线性表L中第i个位置的元素,并用e返回其值。
ListLength(L);        返回线性表L的元素个数。

.c

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
//Status是函数的返回类型,其值是函数结果状态代码,如OK等

#define MAXSIZE 20      /*存储空间初始化分配量*/
typedef int ElemType;   /*ElemType 类型根据实际情况而定,这里假设为int */
typedef struct 
{
    ElemType data[MAXSIZE]; /*数组存储元素,最大值为MAXSIZE*/
    int length;             /*线性表当前长度*/
} SqList;


//GetElem(L,i,*e)   获取元素接口实现
//初始条件:顺序线性表L已存在,1<=i <=ListLength(L)
//操作结果:用e返回L中第i个数据元素的值
Status GetElem(SqList L, int i, ElemType *e)
{
    if(L.length==0 || i<1 || i>L.length)
        return ERROR;
    *e=L.data[i-1];
    return OK;
}

//ListInsert(*L,i,e)    在线性表L中的第i个位置插入新元素e
//初始条件:顺序线性表L已存在,1<=i <=ListLength(L)
//操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1
Status ListInsert(SqList *L,int i,ElemType e)
{
    int k;
    if(L->length==MAXSIZE)   /*顺序线性表已经满*/
        return ERROR;
    if(i<1 || i>L->length+1) /*当i不在范围内时*/
        return ERROR;
    if(i<=L->length) /*若插入的位置不在表尾*/
    {
        //重点1:数组后移的遍历方式
        for(k=L->length-1;k>=i-1;k--)   /*将要插入位置后数据元素向后移动一位*/
            L->data[k+1]=L->data[k];
    }
    L->data[i-1]=e;     /*将新元素插入*/
    L->length++;
    return OK;
}

//ListDelete(*L,i,*e) 删除线性表L中第i个位置的元素,并用e返回其值
//初始条件:顺序线性表L已存在,1<=i <=ListLength(L)
//操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1
Status ListDelete(SqList *L,int i,ElemType *e)
{
    int k;
    if(L->length==0)        /*线性表为空*/
        return ERROR;
    if(i<1 || i>L->length)  /*删除位置不正确*/
        return ERROR;
    *e=L->data[i-1];
    if(i<L->length)         /*如果删除不是最后位置*/
    {
        //重点2:从头到后遍历的方式一定不能有这种思想
        for(k=i; k<L->length;k++)   /*将删除位置后继元素前移*/
            L->data[k-1]=L->data[k];
    }
    L->length--;
    return OK;
}

数组优缺点

优点
无须为表示表中元素之间的逻辑关系儿增加额外的存储关系
可以快速地获取表中任一位置的元素
缺点
插入和删除操作需要移动大量元素
当线性表长度变化较大时,难以确定存储空间的容量
造成存储空间的“碎片


线性表-链式存储结构(单链表)

.h

CreateListHead(LinkList *L, int n);          //单链表整表创建-头插法
CreateListTail(LinkList *L, int n);          //单链表整表创建-尾插法
GetElem(LinkList L,int i,ElemType *e)
ListInsert(LinkList *L, int i, ElemType e)
ListDelete(LinkList *L, int i, ElemType *e)
ClearList(LinkList *L)

.c

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
//Status是函数的返回类型,其值是函数结果状态代码,如OK等

//3.6 线性表的链式存储结构
typedef struct Node
{
    ElemType data;
    struct Node *next;
} Node;
typedef struct Node* LinkList;

//3.7单链表的读取
//GetElem(L,i,*e);   将线性表L中的第i个位置元素值返回给e
//初始条件:顺序线性表L已存在,1<=i <=ListLength(L)
//操作结果:用e返回L中第i个数据元素的值
Status GetElem(LinkList L,int i, ElemType *e)
{
    int j;
    LinkList p;     //声明一结点p
    p = L->next;    //让p指向链表L的第一个结点
    j=1;            //j为计数器
    while(p && j<i) //p不为空或者计数器j还没有等于1时,循环继续
    {
        p=p->next;  //让p指向下一个结点
        ++j;
    }
    if(!p || j>i)
        return ERROR;   //第i个元素不存在
    *e=p->data;
    return OK;
}


//3.8单链表的插入与删除
//ListInsert(*L,i,e);   在线性表L中的第i个位置插入新元素e
//初始条件:顺序线性表L已存在,1<=i <=ListLength(L)
//操作结果:在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)     //寻找第i个结点
    {
        p = p->next;
        ++j;
    }
    if(!p || j>i)
        return ERROR;   //第i个元素不存在
    s=(LinkList)malloc(sizeof(Node));    //生成新结点
    s->data = e;
    s->next=p->next;        //将p的后继结点赋给s的后继结点
    p->next=s;              //将p的后继结点赋给s
    return OK;
}

//ListDelete(*L, i, e) 单链表的删除算法
//初始条件:顺序线性表L已存在,1<=i<=ListLength(L)
//操作结果:删除L的第i个结点,并用e返回其值,L的长度减1
Status ListDelete(LinkList *L, int i, ElemType *e)
{
    int j;
    LinkList p,q;
    p = *L;
    j = 1;
    //遍历寻找第i-1个结点
    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;
}

//3.9单链表的整表创建
//头插法,随机产生n个元素的值,建立带表头节点的单链线性表L
void CreateListHead(LinkList *L, int n)
{
    LinkList p;
    int i;
    //初始化随机种子
    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;
        p->next=(*L)->next;
        //插入到表头
        (*L)->next=p;
    }
}

//尾插法,随机产生n个元素的值,建立带表头结点的单链线性表L
void CreateListTail(LinkList *L, int n)
{
    LinkList p,r;
    int i;
    //初始化随机种子
    srand(time(0));
    *L=(LinkList)malloc(sizeof(Node));
    //r为指向尾部的结点
    //注意L与r的关系,L是指整个单链表,而r是指指向尾结点的变量,r会随着循环不断地变化结点
    //而L则是随着循环增长为一个多节点的链表。
    r=*L;
    for(i=0; i<n; i++)
    {
        //生成新结点
        p=(Node *)malloc(sizeof(Node));
        //随机生成100以内的数字
        p->data=rand() % 100 + 1;
        //将表尾终结点的指针指向新节点
        r->next = p;
        //将当前的新结点定义为表尾终端结点
        r=p;
    }
    //表示当前链表结束
    r->next=NULL;
}

//3.10单链表的整表删除
//初始条件:顺序线性表L已存在,操作结果将L重置为空表
Status ClearList(LinkList *L)
{
    LinkList p,q;
    //p指向第一个结点
    p=(*L)->next;
    //每到表尾
    while(p)
    {
        q=p->next;
        free(p);
        p=q;
    }
    //头结点指针域为空
    (*L)->next=NULL;
    return OK;
}

单链表优缺点

存储分配方式
顺序存储结构用一段连续的存储单元依次存储线性表的数据元素
单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素
时间性能
查找:顺序存储结构O(1)、单链表O(n)
增删:顺序存储结构需要平均移动表长一半的元素,时间为O(n)、单链表在找出某位置的指针后,插入和删除时间仅为O(1)
空间:顺序存储结构需要预分配存储空间,分大了,浪费,分小了易发生上溢、单链表不需要分配存储空间,只要有就可以分配,元素个数也不受限制

线性表-双向链表(list)

我们在单链表中,有了next指针,这就是我们要查找下一结点的时间复杂度为O(1),可我们要查找上一借点的话,那最坏的时间复杂度就是O(n)了,因为我们每次都要从头开始遍历查找。为了克服单向性这一缺点,我们的老科学家们设计出可双向链表。双向链表中的结点都有两个指针域,一个指向直接后继,另一个指向直接前驱。

typedef struct DulNode{
    ElemType data;
	struct DulNode *prior;		//直接前驱指针
	struct DulNode *next;		//直接后继指针
} DulNode, *DuLinkList;