线性表

线性表

线性表是具有相同特性的数据元素的一个有限序列; 是一种典型的线性结构


线性表的两种存储结构:

  • 顺序存储结构
  • 链式存储结构

抽象数据类型线性表

抽象数据类型线性表的定义:

ADT List{

​ 数据对象:D=ai|ai属于Elemset,(i=0,1,2,3,...,n,n>0)}

​ 数据关系:R=<ai-1,ai>|ai-1,ai属于D,(i=2,3,...,n)}

​ 基本操作:

​ InitList(&L);

​ 操作结果:创建一个空的线性表L

​ DestroyList(&L);

​ 初始条件:线性表L已存在

​ 操作结果:销毁线性表L

​ ClearList(&L);

​ 初始条件:线性表L已存在

​ 操作结果:将线性表L重置为空表

​ ListEmpty(L);

​ 初始条件:线性表L已存在

​ 操作结果:若线性表L为空表返回TRUE;否则返回FALSE

​ ListLength(L);

​ 初始条件:线性表L已存在

​ 操作结果:返回线性表L中的数据元素个数

​ GetElem(L,i,&e);

​ 初始条件:线性表L已存在; 1<=i<=ListLength(L)

​ 操作结果:用e返回线性表L中第i个数据元素的值

​ LocateElem(L,e,compare());

​ 初始条件:线性表L已存在; compare()是数据元素判定函数

​ 操作结果:返回L中第1个与e满足compare()数据元素的位序,若不存在则返回0

​ PriorElem(L,cur_e,&pre_e);

​ 初始条件:线性表L已存在

​ 操作结果:若,cur_e是L的数据元素且不是第一个,则用pre_e返回其前驱;否则操作失败pre_e无意义

​ NextElem(L,cur_e,&next_e);

​ 初始条件:线性表L已存在

​ 操作结果:若,cur_e是L的数据元素且不是最后一个,则用next_e返回其后继;否则操作失败next_e无意义

​ ListInsert(&L,i,e);

​ 初始条件:线性表L已存在; 1<=i<=ListLength(L)+1

​ 操作结果:在L的第i个位置之插入新的元素e,L长度加一

​ ListDelete(&L,i,&e);

​ 初始条件:线性表L已存在; 1<=i<=ListLength(L)

​ 操作结果:删除L的第i个数据元素,并用e返回其值,L长度减一

​ ListTraverse(&L,visited());

​ 初始条件:线性表L已存在

​ 操作结果:依次对线性表中每个元素调用visited()

}ADT List


顺序表的定义

struct Book{
  char id[20];	//ISBN
  char name[50];
  int price;
};

typedef struct{
  Book *elem;
  int length;
}SqList;

链表的定义

struct Book{
  char id[20];	//ISBN
  char name[50];
  int price;
};

typedef struct Lnode{
  Book data;
  struct Lnode *next;
}LNode,*LinkList;

线性表的合并

将Lb表中无重复的元素依次插入La的表尾

#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100

typedef struct{
    int elem[MAXSIZE];
    int length;
}List;


int GetElem(List L, int i ,int e){
    if(i<1||i>L.length) return 0;
    e=L.elem[i-1];
    return 1;
}

int LocateElem(List L,int e){
    for (int i = 0; i <L.length ; i++) {
        if(L.elem[i]==e) return i+1;
    }
    return 0;
}

int ListInsert_L(List *L,int i,int e){
    if(i<1||i>L->length+1||L->length==MAXSIZE) return 0;
    for (int j = L->length; j >=i-1 ; j--) {
        L->elem[j+1]=L->elem[j];
    }
    L->elem[i-1]=e;
    L->length++;
    return 1;
}

void Union(List *La, List Lb){
    int  La_length=La->length;
    int  Lb_length=Lb.length;
    int e=0;
    for (int i = 1; i <=Lb_length ; i++) {
        GetElem(Lb,i,e);
        if (!LocateElem(Lb,e)){
            ListInsert_L(La,++La_length,e);
        }
    }
}   //时间复杂度为O(La.length)*(Lb.length)

int main(void)
{

}


单链表、循环链表、双向链表的时间效率比较

查找表头结点(首元结点) 查找表尾结点 查找结点*p的前驱结点
带头结点的单链表L O(1) O(n) 无法找到其前驱
带头结点仅设头指针L的循环单链表 O(1) O(n) O(n)
带头结点仅设尾指针R的循环单链表 O(1) O(1) O(n)
带头结点的双向循环链表L O(1) O(1) O(1)

顺序表、链表的比较

链式存储

  • 优:
    • 结点空间可以动态申请和释放
    • 插入和删除时不需要移动大量元素,只需要修改指针
  • 缺:
    • 存储密度(结点数据本身占用的空间/结点占用的空间总量)小:每个结点的指针域需要额外占用存储空间;一般存储密度越大,存储空间利用率越高,显然顺序表的存储密度为1(100%)
    • 链式存储结构是非随机存取结构,增加了算法的复杂度

比较项目 存储结构 顺序表 链表
存储空间 预先分配,会导致空间闲置或溢出 动态分配,不会出现空间闲置或溢出
存储密度 存储密度=1 存储密度<1
存取元素 随机存取,按位置访问元素,时间复杂度为O(1) 顺序存取,按位置访问元素,时间复杂度为O(n)
插入删除 时间复杂度为O(n) 不需要移动元素,时间复杂度为O(1)
使用 情况 表长变化不大,且能事先确定变化范围;很少进行插入、删除操作;经常按元素位置序号访问数据元素 长度变化较大;频繁进行插入、删除
posted @ 2023-01-30 15:03  原语  阅读(73)  评论(0编辑  收藏  举报