线性表

无畏惧十年磨一剑,因为那是溶在我血液里面的纯粹。
化作尘泥碾作尘,只有香如故。——————摘自知乎


数序表的基本操作有12个,通过对基本操作有机的组合,可以对线性表进行较复杂的处理。List是抽象的线性表类型,并不是稍后将要介绍的具体的线性表存储结构。SqList是顺序存储结构的线性表,LinkList是链式存储结构的线性表。

数序存储结构的线性表SqList(也称作顺序表)

线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素。
顺序表的存储结构是一种随机存取的存储结构,而高级程序设计语言中的数组类型也有随即存储的特性,因此通常用数组来描述数据结构中的顺序存储结构。


由于线性表的长度可变,且所需最大存储空间随问题不同而不同,因此在C语言中可用动态分配的一维数组。

首先介绍顺序表的存储结构

//c2-1.h
 #define LIST_INIT_SIZE 10//线性表存储空间的初始分配量
 #define LIST_INCREMENT 2//线性表存储空间的分配量
 typedef struct
 {
   ElemType *elem; /* 存储空间基址 */
   int length; /* 当前长度 */
   int listsize; /* 当前分配的存储容量(以sizeof(ElemType)为单位) */
 }SqList;

顺序表的原理很简单:通过一个指针*elem指向申请的连续的数组(如果数据元素的数据项有多个可以使用结构体数组)空间的首地址,记录顺序表的结构体中记录表的信息,但是表的数据元素是记录在数组空间里的。通过对数组的操作,实现顺序表的一些操作。

顺序表的12个基本操作

//bo2-1.cpp
//线性表的12个基本操作
void InitList(SqList *L)
{
    //操作结果:构造一个空的顺序表L
    (*L).elem = (ElemType*) malloc(LIST_INIT_SIZE * sizeof(ElemType));
    //基址指向最初申请的一段数组的首地址
    if(!(*L).elem)
        exit(OVERFLOW);    //内存申请失败
    (*L).length = 0; //空表长度为0
    (*L).listsize = LIST_INIT_SIZE; //初始存储容量
}

Status  DestroyList(SqList *L)
{
    //初始条件:顺序表L存在,操作结果:释放数据地址;将顺序表销毁,这里和ClearList不同
    free((*L).elem);
    (*L).elem = NULL;
    (*L).length = 0;
    (*L).listsize = 0;//注意这点
    return OK;
}

void ClearList(SqList *L)
{
    //初始条件:顺序表L存在。操作结果:将L重置为空表
    (*L).length = 0;
}

Status ListEmpty(SqList *L)
{
    //初始条件:顺序表存在L。操作结果:L为空表反回TRUE;否则返回FALSE
    if((*L).length == 0)
        return TRUE;
    else
        return FALSE;
}

int ListLength(SqList *L)
{
    //操作结果:返回顺序表的长度
    return (*L).length;
}

Status GetElem(SqList L, int i, ElemType *e)
{
    //初始条件:1<=i<=Listllength。操作结果:用e返回L中第i个元素的值
    if(i < 1 || i > L.length)
        return ERROR;
    *e = *(L.elem + i - 1);   //这里说明数组的下标是从0开始的
    return OK;
}

int LocaleElem(SqList L, ElemType e, Status(*compare)(ElemType, ElemType))
{
    //compare()是元素判定函数(满足为1,否则为0)
    //返回L中第1个与e满足compare()的数据元素的位序,若不存在返回0;
    ElemType*p;
    int i = 1; //i的初值为第一个元素的位序
    p = L.elem; //p的初值为第一个元素的存储位置
    //因为L.elem是基址不能改变因此用p来进行搜索
    while(i <= L.length && !compare(*p++, e))
        ++i;//未找到符合的元素,位序后移;最后也没找到i=L.length+1;
    if(i <= L.length)
        return i;
    else
        return 0;
}

Status PriorElem(SqList L, ElemType cur_e, ElemType *pre_e)
{
    //操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱;
    // 否则操作失败,pre_e无定义
    //直接从第二个元素查找
    int i = 2;
    ElemType *p = L.elem + 1;
    while(i <= L.length && *p != cur_e)
    {
        //未找到.指针、位序后移
        //退出循环有两个可能,找到、或最后也没有找到
        p++;
        i++;
    }
    if(i > L.length)
        return INFEASIBLE;//操作失败
    else//找到符合元素
        *pre_e = *(--p);
    return OK;
}

Status NextElem(SqList L, ElemType cur_e, ElemType *next_e)
{
    //操作结果:若cur_e是L的数据元素,且不是第一个,则用next_e返回它的前驱;
    // 否则操作失败,next_e无定义
    int i = 1;
    ElemType*p = L.elem;
    while(i < L.length && *p != cur_e)
    {
        i++;
        p++;
    }
    if(i == L.length)
        return INFEASIBLE;
    else
        *next_e = *++p;
    return OK;
}

Status ListInsert(SqList *L, int i, ElemType e)
{
    //1<=i<=L.length+1
    //操作结果:在L中第i个位置之前插入新的数据元素e(插入元素的位序为i),L的长度+1
    ElemType *newbase, *p, *q;
    if(i < 1 || i > (*L).length + 1)
        return ERROR;
    if((*L).length >= (*L).listsize)   //一般长度不会超过容量
    {
        //如果L.length=L.listsize说明当前空间用完
        newbase = (ElemType*)realloc((*L).elem, ((*L).listsize + LIST_INCREMENT) * sizeof(ElemType));
        //讲解realloc函数
        if(!newbase)   //申请失败
            exit(OVERFLOW);
        (*L).elem = newbase; //更新新的基址
        (*L).listsize += LIST_INCREMENT;
    }
    q = (*L).elem + i - 1; //q为插入位置从这也能看出插入后e的位序为i
    for(p = (*L).elem + (*L).length - 1; p >= q; --p)   //插入位置及以后的元素右移
        *(p + 1) = *p;
    *q = e;
    ++(*L).length;
    return OK;
}

Status ListDelete(SqList *L, int i, ElemType *e)
{
    //1<=i<=L.length
    //操作结果:删除第i个元素,用e返回其结果,L长度-1
    ElemType *p, *q;
    if(i < 1 || i > (*L).length)
        return ERROR;
    p = (*L).elem + i - 1;
    *e = *p;
    q = ((*L).elem + (*L).length - 1);
    for(++p; p <= q; ++p)   //被删除位置后的元素左移
        *(p-1)=*p;
    (*L).length--;
    return OK;
}

Status ListTraverse(SqList L,void(*vi)(ElemType*))
 { // 初始条件:顺序线性表L已存在
   // 操作结果:依次对L的每个数据元素调用函数vi()。一旦vi()失败,则操作失败
   //           vi()的形参加'*',表明可通过调用vi()改变元素的值
   ElemType *p;
   int i;
   p=L.elem;
   for(i=1;i<=L.length;i++)
     vi(p++);
   printf("\n");
   return OK;
 }

来一个主函数检查一下自己的基本操作是否正确

#include"c1.h"
typedef int ElemType;
#include"c2-1.h"
#include"bo2-1.c"//顺序表的基本操作
Status sq(ElemType c1, ElemType c2)
{
    //数据元素判定函数(平方关系)
    if(c1 == c2 * c2)
        return TRUE;
    else
        return FALSE;
}

void dbl(ElemType *c)
{
    //元素值加倍
    *c *= 2;
}

int main()
{
    SqList L;
    ElemType e, e0;
    Status  i;
    int j, k;
    //初始化一个顺序表
    InitList(&L);//void InitList(SqList *L)实参应该是地址
    printf("初始化:L.elem=%u  L.length=%d  L.listsize=%d\n", L.elem, L.length, L.listsize);
    printf("当前是否为空表\n");
    if((i = ListEmpty(&L)) == TRUE)
        printf("空表\n");
    else
        printf("不是空表\n");
    //在表容量范围内插入数据
    for(j = 1; j <= 5; j++)
        ListInsert(&L, 1, j); //每次在头部插入一个数字
    printf("插入数据后:L.elem=%u  L.length=%d  L.listsize=%d\n", L.elem, L.length, L.listsize);
    printf("表的内容为:");
    for(j = 1; j <= 5; j++)
        printf("%d ", *(L.elem + j - 1));
    printf("\n");
 //超过顺序表容量插入数据
    printf("再次插入后\n");
    getch();
    for(j = 6; j <= 30; j++)
        ListInsert(&L, 1, j);
    for(j = 1; j <= 30; j++)
        printf("%d ", *(L.elem + j - 1));
    printf("\n");
    printf("表的容量发生改变\n");
    printf("L.length=%d  L.listsize=%d\n", L.length, L.listsize);
   //查找一个数据的前驱后继
    printf("输入一个数查找它的前驱\n");
    scanf("%d", &e);
    if(PriorElem(L, e, &e0) == OK)
        printf("%d的前驱是%d\n", e, e0);
    else
        printf("操作失败\n");
    printf("输入一个数查找它的后继\n");
    scanf("%d", &e);
    if(NextElem(L, e, &e0) == OK)
        printf("%d的后继是%d\n", e, e0);
    else
        printf("操作失败\n");
      printf("输入你想查找的位序\n");
      scanf("%d",&j);
      if(GetElem(L,j,&e0))
      printf("位序为%d的元素为%d\n",j,e0);
      else
        printf("操作失败没有此位序\n");
    //感受一下函数作为参数的功能
    printf("LocateElem()函数调用sq()函数\n");
    printf("输出表中为4的平方数的位序\n");
    getch();
        k=LocateElem(L,4,sq);
        if(k)
            printf("第%d个元素:%d的值为%d的平方\n",k,*(L.elem+k-1),j);
    printf("使表中每一个元素都加倍  也就是对表中的每一个元素调用dbl函数\n");
            ListTraverse(L,dbl);
    for(j=1;j<=30;j++)
        printf("%d  ",*(L.elem+j-1));
    printf("\n");
    //顺序表的删除
    printf("将表中的数据全部删除\n");
    for(j = 1; j <= 30; j++)  //删除操作执行后表中数据的位置就会改变
    {
        ListDelete(&L, 1, &e0);
        printf("被删除元素为%d \n", e0);
    }
    printf("\n");
    if(ListEmpty(&L))
        printf("现在是空表\n");
    else
        printf("现在不是空表\n");
    return 0;
}

顺序表的算法

  • 无序合并
//algo2-1.c
#include"c1.h"
typedef int ElemType;
#include"c2-1.h"
#include"bo2-1.c"
#include"func2-1.c"
//注意形参与实参类型
void UnionList(SqList *La, SqList Lb)
{
    //注意这里的La是指针类型
    int Lb_len;
    Lb_len = ListLength(Lb);
    int e, i;
    for(i = 1; i <= Lb_len; i++)
    {
        GetElem(Lb, i, &e);
        if(LocateElem(*La, e, equal) == 0) //未在表中找到e
            //这里的参数不是指针类型
            ListInsert(La, (*La).length + 1, e); //在表尾插入e
    }
}
int main()
{
    SqList La, Lb;
    InitList(&La);
    InitList(&Lb);
    printf("La中数据为:\n");
    ListInsert(&La, 1, 4);
    ListInsert(&La, 1, 1);
    ListInsert(&La, 1, 3);
    ListInsert(&La, 1, 6);
    ListInsert(&La, 1, 8);
    ListInsert(&La, 1, 5);
    int i;
    for(i = 1; i <= La.length; i++)
        printf("%d ", *(La.elem + i - 1));
    printf("\n");
    printf("Lb中数据为:\n");
    ListInsert(&Lb, 1, 4);
    ListInsert(&Lb, 1, 2);
    ListInsert(&Lb, 1, 8);
    ListInsert(&Lb, 1, 9);
    ListInsert(&Lb, 1, 6);
    ListInsert(&Lb, 1, 0);
    ListInsert(&Lb, 1, 10);
    ListInsert(&Lb, 1, 7);
    for(i = 1; i <= Lb.length; i++)
        printf("%d ", *(Lb.elem + i - 1));
    printf("\n");
    printf("无序合并后的La:\n");
    UnionList(&La, Lb);
    for(i = 1; i <= La.length; i++)
        printf("%d ", *(La.elem + i - 1));
    printf("\n");
    getchar();
    return 0;
}

数序表的有序合并

//algo2-2.c
#include"c1.h"
typedef int ElemType;
#include"c2-1.h"
#include"bo2-1.c"
//#include"func2-1.c"
void MergeList(SqList La, SqList Lb, SqList *Lc)
{
    //初始条件:La,Lb两表原数据非递减排列
    //操作结果:归并La和Lb得到新的线性表Lc,且Lc的数据元素非递减排列
    int La_len, Lb_len;
    La_len = ListLength(La);
    Lb_len = ListLength(Lb);
    InitList(Lc);
    int i = 1, j = 1, k = 0;
    ElemType ai, bj;
    while(i <= La_len && j <= Lb_len)
    {
        GetElem(La, i, &ai);
        GetElem(Lb, j, &bj);
        if(ai <= bj)
        {
            ListInsert(Lc, ++k, ai);
            ++i;
        }
        else
        {
            ListInsert(Lc, ++k, bj);
            ++j;
        }
    }
    while(i <= La_len)
    {
        GetElem(La, i++, &ai);
        ListInsert(Lc, ++k, ai);
    }
    while(j <= Lb_len)
    {
        GetElem(Lb, j++, &bj);
        ListInsert(Lc, ++k, bj);
    }
}
void SortList(SqList *L, int left, int right)
{
    if(left >= right)
        return;
    int i = left;
    int j = right;
    int key = *((*L).elem + left - 1);
    while(i < j) //一趟排序
    {
        while(i < j && key <= *((*L).elem + j - 1)) //先从右向左查找比key小的值
            j--;
        //找到了开始交换位置
        *((*L).elem + i - 1) = *((*L).elem + j - 1); //由于key=*(L.elem+i-1),故不许担心*(L.elem+i-1)值的丢失
        while(i < j && key >= *((*L).elem + i - 1)) //先从左向右查找比key小的值
            i++;
        *((*L).elem + j - 1) = *((*L).elem + i - 1);
    }
    *((*L).elem + i - 1) = key;
    SortList(L, left, i - 1);
    SortList(L, i + 1, right);
}
void Nonrep_MergeList(SqList La, SqList Lb, SqList *Lc)
{
    //无重复合并
    int i = 1, j = 1, k = 0;
    int La_len, Lb_len;
    ElemType ai, bj;
    InitList(Lc);
    La_len = ListLength(La);
    Lb_len = ListLength(Lb);
    while(i <= La_len && j <= Lb_len)
    {
        GetElem(La, i, &ai);
        GetElem(Lb, j, &bj);
        if(ai <= bj)
        {
            if(ai != *((*Lc).elem + k - 1))
            //如果当前元素已经出现在表中就不进行插入
                ListInsert(Lc, ++k, ai);
            ++i;
        }
        else
        {
            if(bj != *((*Lc).elem + k - 1))
                ListInsert(Lc, ++k, bj);
            ++j;
        }
    }
    while(i <= La_len)
    {
        GetElem(La, i++, &ai);
        if(ai != *((*Lc).elem + k - 1))
            ListInsert(Lc, ++k, ai);
    }
    while(j <= Lb_len)
    {
        GetElem(Lb, j++, &bj);
        if(bj != *((*Lc).elem + k - 1))
            ListInsert(Lc, ++k, bj);
    }
}
void print(ElemType *c)
{
    printf("%d ", *c);
}
int main()
{
    SqList La, Lb, Lc;
    InitList(&La);
    InitList(&Lb);
    printf("La中数据为:\n");
    ListInsert(&La, 1, 4);
    ListInsert(&La, 1, 1);
    ListInsert(&La, 1, 3);
    ListInsert(&La, 1, 6);
    ListInsert(&La, 1, 8);
    ListInsert(&La, 1, 5);
    int i;
    for(i = 1; i <= La.length; i++)
        printf("%d ", *(La.elem + i - 1));
    printf("\n");
    printf("Lb中数据为:\n");
    ListInsert(&Lb, 1, 4);
    ListInsert(&Lb, 1, 2);
    ListInsert(&Lb, 1, 8);
    ListInsert(&Lb, 1, 9);
    ListInsert(&Lb, 1, 6);
    ListInsert(&Lb, 1, 0);
    ListInsert(&Lb, 1, 10);
    ListInsert(&Lb, 1, 7);
    for(i = 1; i <= Lb.length; i++)
        printf("%d ", *(Lb.elem + i - 1));
    printf("\n");
    SortList(&La, 1, La.length);
    SortList(&Lb, 1, Lb.length);
    printf("排序后La的数据:\n");
    printf("La= ");
    ListTraverse(La, print);
    printf("排序后Lb的数据:\n");
    printf("Lb= ");
    ListTraverse(Lb, print);
    MergeList(La, Lb, &Lc);
    printf("Lc= ");
    ListTraverse(Lc, print);
    Nonrep_MergeList(La, Lb, &Lc);
    printf("无重复元素的Lc= ");
    ListTraverse(Lc, print);
    return 0;
}

SortList()使用的是快速排序的方法
书上教材里的MergeList();无法去除重复元素,做一些小的判断就可以去除重复元素生成了Nonrep_MergeList();

你可能感兴趣的:(c语言,数据结构,线性表)