线性表–n个数据元素的有限序列

  • 一个数据元素可以由若干个数据项组成。

  • 同一线性表中的元素必定具有相同特性,即属同一数据对象

  • i称为ai在线性表中的位序

  • ai-1称为ai的直接前驱元素

    ai称为ai-1的直接后继元素

  • n=0时称为空表

  • 线性表的存储结构有顺序存储结构和链式存储结构

线性表的顺序表示

  • 用一组地址连续的存储单元依次存储线性表的数据元素
  • 通常称这种存储结构的线性表为顺序表
  • 随机访问特性:通过序号可以找到任意位置的数据
  • 占用连续的存储空间
#define maxSize 100
typedef struct
{
    int data[maxSize];  //存放顺序表元素的数组
    int length;  //顺序表的长度
}Sqlist;   //顺序表类型的定义

####顺序表的增删改查

  • 顺序表在做插入删除操作时需要移动多个元素
int findElem(Sqlist L, int x)//查找x
{
    int i;
    for(i=0;i<L.length;i++){
        if(x == L.data[i]){
			return i;
        }
    }
    return -1;
}
//在顺序表L的第p个位置插入元素e
int insertElem(Sqlist &L, int p, int e)  //引用型
{
    int i;
    if(p<0||p>L.length||L.length==maxSize)  //位置错误或表长已经达到最大值
        return 0;
    for(i=length-1;i>=p;--i){
        L.data[i+1] = L.data[i];
    }
    L.data[p] = e;
    ++(L.length);  //表长加1
    return 1;  //插入成功,返回1
}
//删除顺序表L中下标为p的元素,并把删除的元素的值赋给e
int deleteElem(Sqlist &L, int p, int &e)
{
    int i;
    if(p<0||p>L.length-1)
        return 0;
    e=L.data[p];
    for(i=p;i<L.length-1;++i){
        L.data[i] = L.data[i+1];  //p位置开始,后边的元素逐个前移一个位置
    }
    --(L.length);
    return 1;
}

线性表的链式表示

  • 任意的存储单元
  • 结点:由数据域和指针域构成
  • 数据域:存储数据元素信息的域
  • 指针域:存储直接后继存储位置的域,指针(链)
  • 链表:线性表的链式存储结构
  • 线性链表(单链表):结点中只包含一个指针域
  • 头结点:有时在单链表第一个结点之前附设的结点,头结点的数据域可以不存储任何信息,也可存储线性表的长度等类的附加信息
  • 带头结点的单链表中,头指针head指向头结点,head->next为NULL时,链表为空;不带头结点的单链表中的头指针head直接指向开始结点,当head等于NULL时,链表为空。
  • 不支持随机访问,结点的存储空间利用率较顺序表稍低,支持存储空间的动态分配

单链表的定义

typedef struct LNode
{
    int data;  //data中存放结点数据域
    struct LNode *next;  //指向后继结点的指针
}LNode; //定义单链表结构类型

单链表的增删改查

  • 链表的增删操作不需要移动元素,只需要修改指针
//完整的删除操作
q = p->next;	//要删除的结点
p->next = p->next->next;
free(q);	//调用free函数释放q所指结点的空间

尾插法

//有n个元素已经存储在数组a中,用尾插法建立链表C
void createlistR(LNode *&C, int a[], int n)
{
    LNode *s, *r;	//s用来指向新申请的结点,r始终指向C的终端结点
    int i;
    C = (LNode *)malloc(sizeof(LNode));		//申请C的头结点空间
    C->next = NULL;
    r = C;
    for(i=0;i<n;++i)
    {
        s = (LNode*)malloc(sizeof(LNode));
        s->data = a[i];		//用新申请的结点来接收a中的一个元素
        r->next = s;		//用r来接纳新结点
       	r = r->next;		//r指向终端结点,以便接纳下一个到来的结点
    }
    r->next = NULL;			//将C的终端结点指针域置为NULL,C建立完成
}

头插法

void createlistF(LNode *&C, int a[], int n)
{
    LNode *s;
    int i;
    C = (LNode*)malloc(sizeof(LNode));
    C->next = NULL;
    for(i=0;i<n;i++)
    {
        s = (LNode*)malloc(sizeof(LNode));
        s->data = a[i];
        s->next = C->next;	//s所指新结点的指针域next指向C中的开始结点
        C->next = s;		//头结点的指针域next指向s结点,使得s成为新的开始结点
    }
}

单链表的合并

//将A,B两个元素递增有序的单链表(含头结点)合并成一个按元素值非递减有序的链表C
void merge(LNode *A, LNode *B, LNode *&C)
{
    LNode *p = A->next;		//p跟踪A的最小值结点
    LNode *q = B->next;		//q跟踪B的最小值结点
    LNdoe *r;	//r指向C的终端结点
    C = A;		
    C->next = NULL;		
    free(B);	//B的头结点已无用,释放
    r = C;		//r指向C,此时头结点也是终端结点
    while(p!=NULL&&q!=NULL)
    {
        if(p->data <= q->data)
        {
            r->next = p;
            p = p->next;
            r = r->next;
        }
        else
        {
            r->next = q;
            q = q->next;
            r = r->next;
        }
    }
    //r->next = NULL;
    if(p != NULL) r->next = p;
    if(q != NULL) r->next = q;
}

静态链表

  • 借助一维数组实现
  • 数组中的每个结点含有两个分量:一个数据元素分量,一个指针分量,指示当前结点的直接后继结点在数组中的位置
  • 指针分量不是通常的存储内存地址的指针型变量,而是一个存储数组下标的整型变量

循环链表

  • 表中最后一个结点的指针域指向头结点,整个链表形成一个环

双向链表

  • 结点有两个指针域,一个指向直接后继,一个指向直接前驱
typedef struct DLNode
{
    int data;
    struct DLNode *prior; //指向前驱结点的指针
    struct DLNode *next;  //指向后继结点的指针
}DLNode;  //定义双链表结点类型

循环双向链表

//以下4句任意一句为真,即可判断循环双链表为空
head->next == head;
head->prior == head;
head->next == head && head->prior == head;
head->next == head || head->prior == head;

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

Canvas 上一篇
伪类与伪元素 下一篇