线性表(list)

线性表的逻辑特性

只有一个表头元素,只有一个表尾元素,表头元素没有前驱,表尾元素没有后继,除表头和表尾元素之外,其他元素只有一个直接前驱,也只有一个直接后继。

线性表的存储结构

顺序存储结构(称为顺序表)和链式存储结构(称为链表)

顺序表——1)随机访问特性;2)要求占用连续的存储空间。

链表——1)不支持随机访问;2)链表中结点的存储空间利用率较顺序表稍低一些;3)支持存储空间的动态分配。

顺序表做插入(或删除操作)的时候要移动多个元素,而在料表中进行插入操作无需移动元素。

链表的五种形式

  • 单链表
  • 双链表
  • 循环单链表
  • 循环双链表
  • 静态链表

静态链表来自于一个结构体数组,数组中的每个结点含有两个分量:一个是数据元素分量data;另一个是指针分量,知识了当前结点的直接后继结点在数组中的位置。

顺序表和链表的比较

  1. 基于空间的比较:1)存储分配的方式:  顺序表的存储空间是一次性分配的,链表的存储空间是多次分配的。2)存储密度(=结点值域所占的存储量/ 结点结构所占的存储总量):顺序表的存储密度=1,链表的存储密度<1
  2. 基于时间的比较:1)存取方式:顺序表可以随机存取,链表只能顺序存取。2)插入/删除时移动元素的个数:顺序表平均需要移动近一半元素,而链表不需要移动元素,只需要修改指针。
/*顺序表的结构体定义*/
typedef struct
{
    int data[maxSize];  //存放顺序表元素的数组
    int length;  //存放顺序表的长度
}Sqlist;
/*考试中常用*/
int A[maxSize];
int n;
/*单链表的结点定义*/
typedef struct LNode
{
    int data;
    struct LNode *next;
}LNode;
/*双链表的结点定义*/
typedef struct DLNode
{
    int data;
    struct DLNode *prior;
    struct DLNode *next;
}DLNode;
LNode *A=(LNode*)malloc(sizeof(LNode));
/*按元素值查找算法*/
int findElem(Sqlist L,int e)
{
    int i;
    for (i=0;i<L.length;i++)
        if (e==L.data[i])
        {
            return i;  //若找到,则返回下标值
            break;
        }
    return -1;  //没找到,则返回-1作为失败标记
}
/*插入数据元素的算法*/
int insertElem(Sqlist &L,int p, int e)  //在顺序表的第P个位置上插入新的元素e
{
    int i;
    if (p<0||p>L.length-1||L.length==maxSize)  //位置错误或者表长已经达到顺序表的最大允许值
    return 0;                                  //此时插入不成功,返回值为0
    for (i=L.length-1;i>=p;i--)  //从后往前
        L.data[i+1]=L.data[i];   //逐个将元素往后移动一个位置
    L.data[p]=e;  //将e放在插入位置p上
    ++(L.length);  //表长自增1
    return 1;  //插入成功,返回1
}
/*删除顺序表L中下标为p的元素*/
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];
    --(L.length);
    renturn 1;
}
/*初始化顺序表的算法*/
void initList(Sqlist &L)
{
    L.length=0;
}
/*求指定位置元素的算法*/
int getElem(Sqlist L;int p;int &e)
{
    if (p<0||p>L.length-1)
        return 0;
    e=L.data[p];
    return 1;
}
/*尾插法归并*/
void merge(LNode *A,LNode *B,LNode *&C)
{
    LNode *p=A->next;  //p来跟踪A的最小结点值
    LNode *q=B->next;  //q来跟踪B的最小结点值
    LNode *r;  //r始终指向C的终端结点
    C=A;  //用A的头结点来做C的头结点
    C->next=NULL;  //将C置空
    free(B);  //B的头结点已无用,释放空间
    r=C;  //r此时指向C的头结点,因为C此时为空,其头结点也是终端结点
    while (p!=NULL&&q!=NULL)
    {
        if (p->data<=q->data)
        {
            r->next=p;
            p=p->next;
        }
        else
        if (q->data<=p->data)
        {
            r->next=q;
            q=q->next;
        }
    }
    r->next=NULL;
    if (p!=NULL) r->next=p;
    if (q!=NULL) r->next=q;
}
/*尾插法建立单链表*/
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;  //r指向头结点,因为此时头结点就是终端结点
    for (i=0;i<n;i++)  //循环申请n个结点来接收数组a中的元素
    {
        s=(LNode *)malloc(sizeof(LNode));  //s指向新申请的结点
        s->data=a[i];  //用新申请的结点接收a中的一个元素
        r->next=s;  //用r来接纳新结点
        r=r->next;  //r指向新的终端结点,以便接纳下一个到来的结点
    }
    r->next=NULL;  //数组a中的元素都已经装入链表C中,C的终端结点指针置为NULL,C建立成功
}
/*头插法建立单链表*/
void createlistF(LNode *&C,int a[],int n)  //要改变的变量用引用型
{
    LNode *s;  //s用来指向新申请的结点
    int i;
    C=(LNode *)malloc(sizeof(LNode));  //申请C的头结点空间
    C->next=NULL;
    for (i=0;i<n;i++)  //循环申请n个结点来接收数组a中的元素
    {
        s=(LNode *)malloc(sizeof(LNode));  //s指向新申请的结点
        s->data=a[i];  //用新申请的结点接收a中的一个元素
        /*以下两歩是头插法关键步骤*/
        s->next=C->next;
        c->next=s;
    }
    r->next=NULL;  //数组a中的元素都已经装入链表C中,C的终端结点指针置为NULL,C建立成功
}
/*头插法归并(逆原序列顺序)*/
void merge(LNode *A,LNode *B,LNode *&C)
{
    LNode *p=A->next;  //p来跟踪A的最小结点值
    LNode *q=B->next;  //q来跟踪B的最小结点值
    LNode *s;
    C=A;  //用A的头结点来做C的头结点
    C->next=NULL;  //将C置空
    free(B);  //B的头结点已无用,释放空间
    r=C;  //r此时指向C的头结点,因为C此时为空,其头结点也是终端结点
    while (p!=NULL&&q!=NULL)
    {  
      /*下边的if else语句体现了链表的头插法*/
        if (p->data<=q->data)
        {
            s=p;
            p=p->next;
            s->next=C->next;
            C->next=s;
        }
        else
        if (q->data<=p->data)
        {
            s=q;
            q=q->next;
            s->next=C->next;
            C->next=s;
        }
    }
    r->next=NULL;
  /*头插法必须将剩余元素逐个插入C的头部才能得到最终的递减序列*/
    if (p!=NULL)
    {
        s=p;
        p=p->next;
        s->next=C->next;
        C->next=s;
    }
    if (q!=NULL)
    {
        s=q;
        q=q->next;
        s->next=C->next;
        C->next=s;
    }
}
int findAndDelete(LNode *C,int x)
{
    LNode *p,*q;
    p=C;
    /*查找部分开始*/
    while(p->next!=NULL)
    {
        if (p->next->data==x)
            break;
        p=p->next;
    }
    /*查找部分结束*/
    if(p->next==NULL)
        return 0;
    else
    {
      /*删除部分开始*/
        q=p->next;
        p->next==p->next->next;
        free(q);
      /*删除部分结束*/
        return 1;
    }
}

要删除一个结点必须知道其前驱结点的位置

双链表的操作

/*尾插法建立双链表*/
void createDlistR(DLNode *&L,int a[],int n)  //要改变的变量用引用型
{
    DLNode *s,*r;  //s用来指向新申请的结点,r始终指向L的终端结点
    int i;
    L=(DLNode *)malloc(sizeof(DLNode));  //申请L的头结点空间
    L->prior=NULL;
    L->next=NULL;
    r=L;  //r指向头结点,因为此时头结点就是终端结点
    for (i=0;i<n;i++)  //循环申请n个结点来接收数组a中的元素
    {
        s=(DLNode *)malloc(sizeof(DLNode));  //s指向新申请的结点
        s->data=a[i];  //用新申请的结点接收a中的一个元素
        r->next=s;  //用r来接纳新结点
        s->prior=r;
        r=r->next;  //r指向新的终端结点,以便接纳下一个到来的结点
    }
    r->next=NULL;  //数组a中的元素都已经装入双链表L中,L的终端结点指针置为NULL,L建立成功
}
/*在双链表中查找第一个值为x的结点*/
DLNode* findNode(DLNode *C,int x)
{
    DLNode *p=C->next;
    while (p!=NULL)
    {
        if (p->data==x)
            break;
        p=p->next;
    }   
    return p;  //如果找到,则p中内容是结点地址;
}              //如果没找到,则p中内容是NULL。
/*在双链表中插入结点的语句(p所指的结点之后插入一个结点s)*/
s->next=p->next;
s->prior=p;
p->next=s;
s->next->prior=s;  //加入p指向最后一个结点,则本行可删去
/*在双链表中删除结点的算法(要删除p结点的后继结点)*/
q=p->next;
p->next=q->next;
q->next->prior=p;
free(q);

猜你喜欢

转载自blog.csdn.net/qq_41661831/article/details/81262220