线性表

Posted silentsharer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线性表相关的知识,希望对你有一定的参考价值。

1. 线性表综述:
线性表是具有相同特性数据元素的一个有限序列
该序列中所包含元素的个数叫做线性表的长度
当一个线性表有序时,成为有序表,属于逻辑结构
线性表的逻辑特征:
对于至少含有一个元素的线性表来说
除起始元素没有前驱元素外,其他元素都有一个唯一前驱
除终端元素没有后继元素外,其他元素都有一个唯一后继
在线性表中,每个元素最多只有一个前驱和后继元素
线性表存储结构:
线性表有顺序和链式两类存储结构,前者成为顺序表,后者成为链表
顺序表特点:
无需为元素间的逻辑关系儿增加额外的存储空间
可以随机存取表中的任何一个元素,但是要占用连续的空间
而且插入和删除元素时需要移动大量的元素,平均需要移动n/2个元素
链表特点:
不仅存储每个节点的信息,而且也存储节点之间的关系(通过指针)
链表的地址可以是不连续的,不支持随机访问,插入和删除比较快
链表中的第一个节点的存储位置通常被成为头指针
如果链表有头结点,头指针为头结点的地址
如果没有头结点,头指针为开始节点的地址
通常用头指针来标识一个链表
1、顺序表程序:

//线性表存储空间的初始分配量 
#define LIST_INIT_SIZE 10
//顺序存储结构的分配增量 
#define LIST_INCREMENT 5
typedef int ElemType;
//顺序存储结构体
struct SqList
{
    ElemType* elem;     //存储空间基址
    int length;         //数据元素当前长度 
    int listsize;       //当前分配的总容量(元素个数) 
};
/*******基础操作********/
//初始化顺序表
void InitList(SqList* L)
{
    L->elem = (ElemType*)malloc(sizeof(ElemType)*LIST_INIT_SIZE); 
    if (L->elem == NULL)
    {
        printf("空间申请失败!\n");
        exit(-1); 
    }
    L->length = 0;
    L->listsize = LIST_INIT_SIZE;
}
//销毁顺序表
void DestroyList(SqList* L)
{
    free(L->elem);
    L->elem = NULL;
    L->length = 0;
    L->listsize = 0; 
}
//清空顺序表
void ClearList(SqList* L)
{
    L->length = 0;
}
//判空
int EmptyList(SqList* L)
{
    if (L->length == 0)
        return 1;
    return 0; 
}
//顺序表长
int ListLength(SqList* L)
{
    return L->length;
}
//得到顺序表第index元素
int GetElem(SqList* L, int index, ElemType* e)
{
    //要找的元素不在顺序表范围内 
    if (index<1 || index>L->length) 
        return -1;
    *e = L->elem[index-1];
    return 1; 
}
//返回元素的位置
int LocateElem(SqList* L, ElemType* e)
{
    for (int i=0; i<L->length; ++i)
    {
        //元素相等 
        if (L->elem[i] == *e) 
            return i;
    }
    return -1;
}
//返回元素的前驱
int PriorElem(SqList* L, ElemType* e)
{
    int index = LocateElem(L, e);
    //第一个元素没有前驱 
    if (index == -1 || index == 0)
        return -1;
    return index-1; 
}
//返回元素的后继
int NextElem(SqList* L, ElemType* e)
{
    int index = LocateElem(L, e);
    //最后一个元素没有后继 
    if (index == -1 || index == L->length-1)
        return -1;
    return index+1; 
}
//顺序表插入
int ListInsert(SqList* L, int index, ElemType* e)
{
    //判断插入位置是否合法
    if (index<1 || index>L->listsize)
        return -1; 
    //判断空间是否满
    if (L->length == L->listsize)
    {
        L->elem = (ElemType*)realloc(L->elem, (L->listsize+LIST_INCREMENT)*sizeof(ElemType)); 
        if (L->elem == NULL)
            exit(-1); 
        L->listsize += LIST_INCREMENT;
    }
    //开始插入
    for (int i=L->length-1; i>=index-1; --i)
    {
        //循环后移 
        L->elem[i+1] = L->elem[i]; 
    }
    //插入 
    L->elem[index-1] = *e; 
    //长度++
    L->length++; 

    return 1;
}
//顺序表删除
int ListDelete(SqList* L, int index)
{
    //判断删除位置是否合法
    if (index<1 || index>L->length)
        return -1;
    //开始删除
    for (int i=index-1; i<L->length; ++i)
    {
        L->elem[i] = L->elem[i+1]; 
    }
    --L->length;

    return 1;
}
//顺序表遍历
void ListTraverse(SqList* L)
{
    for (int i=0; i<L->length; ++i)
    {
        cout << L->elem[i] << ‘ ‘; 
    }
    cout << endl;
}
/*******进阶操作********/
//合并两个顺序表
//将所有在顺序表Lb中,但不在La中的元素插入到La中 
void Union(SqList* La, SqList* Lb)
{
    int lena = ListLength(La);
    int lenb = ListLength(Lb); 
    for (int i=0; i<Lb->length; ++i)
    {
        //在La中没有出现就插入 
        if (LocateElem(La, &(Lb->elem[i])) == -1) 
            ListInsert(La, La->length+1, &(Lb->elem[i]));       
    }
}
//归并两个有序顺序表
void MergeList(SqList* La, SqList* Lb, SqList* Lc) 
{
    int i=0, j=0;
    int lena = La->length;
    int lenb = Lb->length;
    //初始化顺序表Lc 
    InitList(Lc); 

    //两个都没有完的时候 
    while (i<lena & j<lenb)
    {
        //La<Lb
        if (La->elem[i] < Lb->elem[j]) 
        {
            //尾插La里面的元素 
            ListInsert(Lc, Lc->length+1, &(La->elem[i])); 
            ++i;
        }
        else    //Lb<La
        {
            //尾插Lb里面的元素 
            ListInsert(Lc, Lc->length+1, &(Lb->elem[j])); 
            ++j;
        }
    }
    //其中有一个插完了
    while (i < lena)
    {
        //尾插La里面的元素 
        ListInsert(Lc, Lc->length+1, &(La->elem[i])); 
        ++i;
    }
    while (j < lenb)
    {
        //尾插Lb里面的元素 
        ListInsert(Lc, Lc->length+1, &(Lb->elem[j])); 
        ++j;
    }
}

int main()
{
    /*基础操作测试 
    SqList L;
    //初始化 
    InitList(&L);
    cout << "初始化后: 首地址:" << L.elem << "元素个数:" << L.length << "总容量:" << L.listsize << endl;
    //头插
    for (int i=1; i<=5; ++i)
         ListInsert(&L, 1, &i); 
    cout << "头插5次:" << endl; 
    ListTraverse(&L);
    //是否为空
    cout << "顺序表是否为空:" << EmptyList(&L) << endl; 
    //大小
    cout << "顺序表大小:" << ListLength(&L) << endl; 
    //清空顺序表
    cout << "清空顺序表:" << endl;
    ClearList(&L);
    cout << "清空后大小:" << ListLength(&L) << endl; 
    //尾插
    for (int i=6; i<=10; ++i)
        ListInsert(&L, L.length+1, &i);
    cout << "尾插5次:" << endl; 
    ListTraverse(&L);
    //删除第3个元素 
    cout << "删除第3个元素:" << endl;
    ListDelete(&L, 3);
    cout << "删除后:" << endl; 
    ListTraverse(&L); 
    //销毁顺序表
    DestroyList(&L); 
    cout << "首地址:" << L.elem << "元素个数:" << L.length << "总容量:" << L.listsize << endl;
    cout << "顺序表已成功销毁" << endl; 
    */

    SqList La, Lb, Lc;
    //初始化 
    InitList(&La);
    InitList(&Lb);
    InitList(&Lc); 
    //插入La 
    for (int i=1; i<=5; ++i)
         ListInsert(&La, La.length+1, &i);
    //插入Lb
    for (int i=2; i<=10; i+=2) 
        ListInsert(&Lb, Lb.length+1, &i);
    //合并两个顺序表
    //Union(&La, &Lb); 
    //遍历顺序表La
    //cout << "合并后为:" << endl; 
    //归并La和Lb
    MergeList(&La, &Lb, &Lc); 
    cout << "La和Lb归并后:" << endl; 
    ListTraverse(&Lc); 

    return 0;
}

2.链表:
和顺序表相比,链表在实现插入、删除操作时,不需要移动大量的元素
但是不容易实现随机存取,适用于经常进行插入和删除的线性表
一般用一个头指针来唯一的标识一个链表
链表的插入和删除:
插入和删除要先把后边的链表链起来,然后再链接上前面的链表,都要找到当前节点和前驱
链表的插入和删除主要考虑:第一个元素、中间元素、最后一个元素
其中,中间元素和最后一个元素的操作都相同,只剩下第一个元素和其余元素的区别
会因为带头结点和不带头结点而有所区别:比较重要:
不带头结点:
因为如果不带头结点插入表头或者删除表头的话,得修改链表的唯一标识指针即 L
带头结点的话,令头结点为第0个元素,表头结点为第1个元素
就可以不用修改L来插入或者删除表头了,这时修改的是头结点的指针域
链表指针始终指向的是头结点。所以就不用考虑插入是在表头还是在其他地方了
带头结点:
带头结点的链表的插入和删除,都是定义两个结点指针变量 p=L, q=NULL
p指向要插入或删除的结点,q指向p的前驱结点
因为不管是插入和删除,都要找到待操作结点和该结点的前驱
插入和删除:
插入:位置判断条件是:pos<1 || NULL==q 就退出
因为插入时,可以插在1–length+1之间,即 p==NULL是允许的
删除:位置判断条件是:pos<1 || NULL==p 就退出
因为在删除时,可以删除1–length之间,要删除的结点必须不能为空,即 p!=NULL

typedef int ElemType;
//链表结构体定义 
typedef struct Node
{
    ElemType data;      //数据域
    struct Node* next;  //指针域 
}Node, *LinkList;

//初始化
//也可以用Node** L来代替LinkList* L 
void InitList(LinkList* L)
{
    //产生头结点,并使L指向该头结点
    *L = (Node*)malloc(sizeof(Node)); 
    if (*L == NULL)
    {
        cout << "分配失败!" << endl; 
        exit(-1); 
    }
    (*L)->next = NULL; 
}
//销毁链表
//得传入一个链表的指针 
void DestroyList(LinkList* L)
{
    Node* q = NULL;

    while (NULL != *L)
    {
        q = (*L)->next;
        free(*L);
        *L = q; 
    }
    cout << "销毁成功!" << endl; 

}
//清空一个链表,不改变头结点 
void ClearList(LinkList L)
{
    Node* p = NULL;
    Node* q = NULL;
    p = L->next;        //p指向第一个结点 

    while (NULL != p)   //循环删除链表结点 
    {
        q = p->next;
        free(p);
        p = q; 
    }
    L->next = NULL;
}
//链表判空
int ListEmpty(LinkList L)
{
    if (L->next == NULL)
        return 1;
    return 0; 
}
//统计链表长度
int ListLength(LinkList L)
{
    Node* q = L->next;
    int length = 0;

    while (NULL != q)
    {
        ++length;
        q = q->next;
    }

    return length;
}
//取得链表第i个元素
int GetElem(LinkList L, int pos, ElemType* e)
{
    Node* p = L->next;
    int count = 1;

    while (NULL != p && count < pos)
    {
        ++count;
        p = p->next;
    }
    //没有找到 
    if (NULL==p || count>pos) 
        return 0;
    //找到 
    *e = p->data;
    return 1;
}
//获取元素在链表中的位置
int LocateElem(LinkList L, ElemType* e)
{
    Node* p = L->next;  //使p指向第一个结点
    int pos = 1;

    while (NULL!=p && p->data!=(*e))
    {
        ++pos;
        p = p->next;
    }
    if (NULL == p)
        return -1;
    return pos; 
}
//找元素的前驱
int PriorElem(LinkList L, ElemType* cur_e, ElemType* pre_e)
{
    Node* p = L->next;      //指向当前结点 
    Node* q = NULL;         //指向当前结点的前驱 

    while (NULL!= p && p->data!=(*cur_e))
    {
        q = p;
        p = p->next;
    }
    if (NULL == p)
        return -1;
    *pre_e = q->data;
    return 1; 
}
//找到元素的后继
int NextElem(LinkList L, ElemType* cur_e, ElemType* next_e) 
{
    Node* p = L->next;  

    while (p->next != NULL && p->data!=(*next_e)) 
    {
        p = p->next;
    }
    if (NULL == p->next)        //没有找到 
        return -1; 
    *next_e = p->next->data;
    return 1;
}
//在指定位置插入
//可以在1和length+1位置之间进行插入 
int ListInsert(LinkList L, int pos, ElemType* e)
{
    Node* p = L;            //指向要插入的位置
    Node* q = NULL;         //指向要插入的前驱
    int count = 0; 
    //寻找要插入结点的前驱q
    while (NULL!=p && count<pos)
    {
        q = p;
        p = p->next;
        ++count;
    }
    //插入位置不对 大于表长NULL==p或者小于1 
    if (pos<1 || NULL==q) 
    {
        cout << "插入位置不正确!" << endl; 
        return -1; 
    }
    p = (Node*)malloc(sizeof(Node));
    p->data = *e; 
    p->next = q->next;
    q->next = p;

    return 1;
}
//在指定位置删除元素
//在1和length之间可以删除 
int ListDelete(LinkList L, int pos, ElemType* e)
{
    Node* p = L;            //指向要删除的位置 
    Node* q = NULL;         //指向要删除位置的前驱 
    int count = 0;

    while (NULL!=p && count<pos)
    {
        q = p;
        p = p->next;
        ++count;
    }
    if (pos<1 || NULL==p)   //位置不在范围内
    { 
        cout << "删除位置不正确!" << endl; 
        return -1;
    }
    //删除结点 
    q->next = p->next;
    *e = p->data;
    free(p);
    p = NULL;

    return 1;
}
/***********不带头结点的链表的插入和删除***********/
int ListInsert(LinkList* L, int pos, ElemType* e)
{
    int count = 1;  
    Node* p = NULL;
    Node* q = NULL;
    p = (Node*)malloc(sizeof(Node));
    p->data = *e;
    p->next = NULL;
    //表头
    if (pos == 1)
    {
        p->next = (*L);
        *L = p; 
    }
    else        //其他位置 
    {
        while (NULL!=q && count<pos-1) 
        {
            q = q->next;
            ++count;
        }
        //插入位置大于表长 
        if (pos<1 || NULL == q) 
        {
            cout << "插入位置不对!" << endl;
            return -1; 
        }
        p->next = q->next;
        q->next = p;
    }

    return 1;
}
int ListDelete(LinkList* L, int pos, ElemType* e)
{
    Node* p = *L;
    Node* q = NULL;
    int count = 1;
    //头删 
    if (pos == 1)
    {
        p = (*L)->next;
        *L = p->next;
        free(p);
        p = NULL; 
    }
    else
    {
        while (NULL!=p && count<pos)
        {
            q = p;
            p = p->next;
            ++count;
        }
        if (pos<1 || NULL==p) 
        {
            cout << "插入位置不合法!" << endl; 
            return -1;
        }
        q->next = p->next;
        *e = p->data;
        free(p);
        p = NULL; 
    }
}

//链表遍历
void ListTraverse(LinkList L)
{
    Node* p = L->next;
    while (NULL != p)
    {
        cout << p->data << endl;
        p = p->next;
    }
}
//头插建立链表
void HeadCreateList(LinkList* L, int n) 
{
    //建立头结点
    *L = (Node*)malloc(sizeof(Node));
    (*L)->next = NULL; 
    Node* p = NULL;
    int num = 0;

    for (int i=0; i<n; ++i)
    {
        p = (Node*)malloc(sizeof(Node));
        cin >> num;
        p->data = num;
        p->next = (*L)->next;
        (*L)->next = p; 
    }
}
//尾插法建立链表
void TailCreateList(LinkList* L, int n)
{
    //建立头结点 
    (*L) = (Node*)malloc(sizeof(Node));
    (*L)->next = NULL;
    Node* p = NULL;
    Node* cur = *L;
    int num = 0;

    for (int i=0; i<n; ++i)
    {
        //找到尾结点
        while (NULL != cur->next)
        {
            cur = cur->next;
        }
        //开辟新结点
        p = (Node*)malloc(sizeof(Node)); 
        cin >> num;
        p->data = num;
        p->next = NULL;
        //插入尾部
        cur->next = p; 
    }
}

//合并两个链表(合并不同元素)
void Union(LinkList La, LinkList Lb)
{
    ElemType e;
    int len_a = ListLength(La);
    int len_b = ListLength(Lb);
    for (int i=1; i<=len_b; ++i)
    {
        GetElem(Lb, i, &e);
        if (LocateElem(La, &e) == -1) 
            ListInsert(La, ++len_a, &e); 
    }
}
//归并两个有序链表
void MergeList(LinkList La, LinkList Lb, LinkList Lc)
{
    int len_a = ListLength(La);
    int len_b = ListLength(Lb);
    int i=1, j=1;
    Node* pa = La->next;
    Node* pb = Lb->next;
    Node* pc = NULL;

    while (i<=len_a && j<=len_b)
    {
        //La < Lb
        if (pa->data < pb->data)
        {
            //开辟新结点 
            pc = (Node*)malloc(sizeof(Node));
            pc->data = pa->data;
            pc->next = NULL;
            //接入Lc
            Lc->next = pc;
            Lc = pc;
            //La后移一个
            pa = pa->next;
            ++i;
        }
        else
        {
            //开辟新结点
            pc = (Node*)malloc(sizeof(Node));
            pc->data = pb->data;
            pc->next = NULL;
            //接入Lc
            Lc->next = pc;
            Lc = pc;
            //Lb后移一个
            pb = pb->next;
            ++j; 
        }
    }
    while (i <= len_a)
    {
        //开辟新结点
        pc = (Node*)malloc(sizeof(Node));
        pc->data = pa->data;
        pc->next = NULL;
        //接入Lc
        Lc->next = pc;
        Lc = pc;
        //后移一个
        pa = pa->next;
        ++i;
    }
    while (j <= len_b)
    {
        //开辟新结点
        pc = (Node*)malloc(sizeof(Node)); 
        pc->data = pb->data;
        pc->next = NULL;
        //接入Lc
        Lc->next = pc;
        Lc = pc;
        //后移一个
        pb = pb->next;
        ++j; 
    }
}

int main()
{
    /****基础功能测试****/
    /*
    LinkList L;
    //初始化链表 
    InitList(&L);  
    //头插
    for (int i=0; i<5; ++i)
        ListInsert(L, 1, &i);
    //在第最后一个位置插入
    int num = 10;
    ListInsert(L, 6, &num);
    //遍历
    cout << "插入后的链表:" << endl; 
    ListTraverse(L); 
    //链表长度
    cout << "链表长度为:" << ListLength(L) << endl; 

    //在第1个位置删除
    cout << "删除后的链表:" << endl; 
    ListDelete(L, 1, &num);
    //遍历
    ListTraverse(L); 

    //清空链表
    cout << "清空链表:" << endl; 
    ClearList(L); 
    //遍历
    ListTraverse(L);
    //链表长度
    cout << "链表长度为:" << ListLength(L) << endl; 

    //销毁链表
    cout << "销毁链表:" << endl;
    DestroyList(&L); 
    */

    LinkList L;
    //头插创建链表 
    //HeadCreateList(&L, 3); 
    //尾插创建链表 
    TailCreateList(&L, 3); 
    //遍历
    ListTraverse(L);

    /****进阶功能测试****/
    LinkList La, Lb, Lc;
    //初始化链表 
    InitList(&La);  
    InitList(&Lb);  
    InitList(&Lc); 
    //头插
    for (int i=1; i<=5; ++i)
        ListInsert(La, i, &i);
    //尾插 
    int count = 1;
    for (int i=2; i<=10; i+=2)
        ListInsert(Lb, count++, &i);
    //遍历
    ListTraverse(La); 
    ListTraverse(Lb); 
    //Union(La, Lb); 
    cout << "归并后的链表为:" << endl; 
    MergeList(La, Lb, Lc); 
    ListTraverse(Lc);

    return 0;
}

以上是关于线性表的主要内容,如果未能解决你的问题,请参考以下文章

线性表—顺序表

线性表—顺序表

线性表的概念与实现

线性表

数据结构-线性表

数据结构-线性表