顺序表的定义与操作实现

#include <stdio.h>
#include <stdlib.h>//malloc()
#define OK 1
#define ERROR 0
#define TURE 1          
#define FALSE 0
#define INFEASIBLE -1   //不可行的
#define OVERFLOW -2     //溢出
#define LIST_INIT_SIZE 100//初始分配的元素个数(这些数据都是自己定义的,可以根据实际情况来分配)
#define LIST_ADD_SiZE 10//分配增量

typedef char ElemType;//表明ElemType是 char 类型

//========================线性表的动态分配存储结构====================
typedef struct
{
    ElemType *elem;//指针型变量,存储空间的起始地址
    int length;//当前的长度(存储的数据个数)
    int listsize;//当前分配的存储容量
}SqList;

//基本操作定义
//==========================InitList()初始化函数======================
int InitList(SqList &L)
{
    L.elem=(ElemType *)malloc(LIST_INIT_SIZE*sizeof(LIST_INIT_SIZE));//用指针elem存储分配的线性表的起始地址
    if(!L.elem)
        exit(OVERFLOW);//由于某种原因,内存分配失败
    L.length=0;//初始化时候最初存储的元素个数为0
    L.listsize=LIST_INIT_SIZE;
    return OK;
}

//==========================ClearList()重新设置为空表==================
int ClearList(SqList &L)
{
    L.length=0;
    return OK;
}

//==========================ListEmpty()判断是否为空================================
int ListEmpty(SqList L)
{
    if(L.length==0)
        return TURE;//为空,返回TURE
    else 
        return FALSE;//不为空,返回FALSE
}

//==========================ListLength()返回线性表的长度=====================================
int ListLength(SqList L)
{
    if(!L.elem)
        return ERROR;
    return L.length;
}

//==========================GetElem()用e返回第i个数据元素的值================================
ElemType GetElem(SqList L,int i,ElemType &e)
{
    if(i<1 || i>L.length || !L.elem)    //查找的位置越界或L表不存在,返回错误
        return ERROR;
    e=*(L.elem+i-1);//蛋蛋,这里要明白的是:L.elem表示的是
    return e;   
}

//==========================LocateElem()返回所找元素在表中的位置================================
int LocateElem(SqList L,ElemType e,int(*compare)(ElemType,ElemType))
{
    int i=1;//这里的i=1,和下面的while语句相呼应,表明的是检测【1......L.length】共length个元素
    ElemType *p;
    p=L.elem;//指针p存储表L的首元地址,如果用指针p做相应操作(比较),相当于直接用L.elem指针一样
    while(i<=L.length && !(*compare)(*p++,e))//表中的元素和e想等时,用i记下那个位置
        ++i;
    if(i<=L.length)     //注意:上面一句,是++i;因此,当最后一个元素和e比较不等时,i仍然+1,此时i>L.length【不应该输出这个i】,所以要限制为i<=L.length
        return i;
    else 
        return FALSE;
}

//==========================GetElem()用e返回第i个数据元素的值================================
int compare(ElemType e1,ElemType e2)
{
    if(e1==e2)
        return TURE;
    else
        return FALSE;
}

//==========================PriorElem()返回cur_e的前驱元素================================
//若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,否则操作失败
int PriorElem(SqList L,ElemType cur_e,ElemType &pre_e)
{
    //关键是找到值为cur_e的元素位置
    int i=2;
    ElemType *p=L.elem+1;//p存储第二个元素的地址
    while(i<=L.length && *p!=cur_e)//没找到,执行......
    {
        p++;    //p指向下一个元素
        i++;    
    }
    if(i>L.length)      //如果i>length时还没找到,则返回“不可行”
        return INFEASIBLE;
    else
    {
        //*--p表示先让指针减1,再取其值,所以下面用()把--p扩住,括号的运算优先级大于*运算符
        //在这里强烈建议一定要看C prime Pluse 第十章{数组和指针}
        pre_e=*(--p);       //当找到(while循环结束),赋给pre_e
        return OK;
    }
}
//==========================NextElem()用next_e返回cur_e的后继元素================================
int NextElem(SqList L,ElemType cur_e,ElemType &next_e)
{
    int i=1;
    ElemType *p=L.elem;
    while(i<L.length && *p!=cur_e)//比较【1......length-1】共length-1个元素
    {
        i++;
        p++;
    }
    if(i==L.length)
        return INFEASIBLE;//注意:已经找到了最后一个元素(最后一个元素没有后继),还没找到,返回“不可行”
    else
    {
        next_e=*(++p);
        return OK;
    }
}
//==========================ListInsert()在第i个位置之前插入元素e================================
/*顺序表是从0开始的一种存储单位连续的数据结构,它如果有i个元素最后一个元素下标就为i
用数组来存储顺序表的元素数据,数组从【0】开始【i-1】结束,顺序表的第i个元素的数组下标是【i-1】*/

//操作结果:成功插入,length+1
int ListInsert(SqList &L,int i,ElemType e)
{
    ElemType *newbase,*q,*p;
    if(i<1 || i>L.length+1)//从表的第一个元素开始可以插入,在表的最后一个元素的后面也可插入元素,符合则执行
        return ERROR;
    //在已经有数据的基础上,表已经满了,则考虑再分配存储空间
    if(L.length>=L.listsize)
    {
        newbase=(ElemType *)realloc(L.elem,(L.listsize+LIST_ADD_SiZE));//插入元素要考虑内存空间空间是否足够
        if(!newbase)//和分配L.elem时类似(某种原因导致分配错误)
            exit(ERROR);
        L.elem=newbase;//用elem存储新基址
        L.listsize+=LIST_ADD_SiZE;//当前分配空间增加
    }

    q=&(L.elem[i-1]);//用q记[i]位置的前一个位置[i-1]的元素
    for(p=&(L.elem[L.length-1]);p>=q;--p)//逆序开始,把表的最后一个元素[length-1]到第[i-1]个元素处(注意范围,想一想为什么?)
        *(p+1)=*p;//元素统一往后移一个位置,可以画图表示出要移动的元素起始到结束的位置!
    *q=e;//表示e值放到指针所指的位置上
    L.length++;//记得要让长度加1
    return OK;
}
//==========================ListDelete()删除L中第i个数据,用e返回其值================================
ElemType ListDelete(SqList &L,int i,ElemType &e)
{
    ElemType *p,*q;
    if(i<1 || i>L.length)
        return ERROR;
    p=L.elem+i-1;//p为被删除元素的位置   L.elem+0相当于是L.elem[0] L.elem+i就相当于是L.elem[i]
    e=*p;
    q=&(L.elem[L.length-1]);//沿用插入操作的赋值方式,记录下表尾元素的位置
    //++p 分析:p是指针,所以++p就是指针p指向下一个元素的位置    //遇到++i 和i++ 不要怕,弄清原则就很好理解!举个例子:C到C++ 先有C再有C++,++i表示先+1再使用。//如果这块有问题,还是看书彻底搞明白。
    for(++p;p<=q;++p)//注:第一个++p表示for循环的起始位置(表示从第i个元素的后一个元素[i-1]开始向后移动一个位置),第二个++p表示进行的动作操作(自增)
        *(p-1)=*p;//这里的赋值其实是后面的值覆盖了前面的元素值。
    L.length--;
    return OK;
}
//==========================ListTraverse()对每个数据元素调用vi()函数================================
void ListTraverse(SqList L,void(*vi)(ElemType ))
{
    ElemType *p;
    int i;
    p=L.elem;//指针p指向L的首元地址
    for(i=1;i<=L.length;i++)
        vi(*p++);//依次对表中的每个元素执行Vi()函数
    printf("\n");
}
//==========================vi()函数只是一个任意的你想对数据处理的函数功能,比如对int 型数据每个元素乘以2,对char型数据每个元素执行输出操作,可以参考那个资料================================
//这里的vi仅实现对元素输出的操作
void vi(ElemType e)
{
    printf("%c ",e);
}
//==========================main()===========================================================
int main()
{
    SqList L;
    ElemType e,e1;
    int i;
    int k;
    //执行初始化函数
    InitList(L);
//=====================这步是帮助理解的==============

    //执行完初始化函数后可以输出给L分配的空间基址和当前分配的空间大小
    //其实这步可以帮助理解指针和存储相关的存储空间的知识
    printf("%u    %d     当前的元素个数:%d\n",L.elem,L.listsize,L.length);//无符号十进制输出
    printf("%u \n",L.elem+1);//elem是指针,elem+1 就是指针指向下一个元素,这里测试的是char类型,char类型 占一个字节。通过输出可以一看出。地址+1
                                //同样的道理,如果这里存储的是int类型,int 类型占4个字节,下一个int元素的地址是上一个int元素地址+4。
                                //每个变量都占有一定数目的字节(通过sizeof运算符获得)其中取第一个字节的地址做该变量的地址。
//=========================又啰嗦了,进入正题!============================================
    //判断是否为空
    if(ListEmpty(L)==TURE)
        printf("由于线性表的长度存储的元素个数是0,所以为空。\n");
    else if(ListEmpty(L)==FALSE)//判断不为空,同时输出元素个数。
        printf("不空。存储的元素个数是=>:%d\n",L.length);
    //接下来向表内手动插入元素

    printf("请输入你要插入的元素个数=>:");
    scanf("%d",&k);
    printf("请输入数据=>:");
    for(i=1;i<=k;i++)
    {
        scanf(" %c",&e);//输入一个数据,把它插入到依次第i个元素之前
        ListInsert(L,i,e);
    }
    //测试输出L里面的数据
    ListTraverse(L,vi);
    printf("当前表的长度是:%d\n",ListLength(L));
    printf("查找元素的位置(接下来请输入你要查找的元素)=>:");
        scanf(" %c",&e);
        i=LocateElem(L,e,compare);
    if( i==FALSE)
        printf("表中没有该元素!\n");
    else
        printf("你所找的元素%c在表中的位置是=>%d\n",e,i);
    //========================================================
    //插入元素
    printf("请输入你要插入的元素的位置和值=>:");
    scanf(" %d %c",&i,&e);
    if(ListInsert(L,i,e)==ERROR)
        printf("错误!");
    else
    {
        printf("成功插入%c后的表L=>:",e);
        //测试输出L里面的数据
        ListTraverse(L,vi);
    }   
    //============================================================
    //删除元素
    printf("请输入你要删除的元素在表中的位置=>:");
    scanf(" %d",&i);
    ListDelete(L,i,e);
    printf("删除第%d个元素%c后表L=>:",i,e);
    ListTraverse(L,vi);

    //查找前驱元素的值
    printf("请输入元素(查找该元素的前驱)=>:");
    scanf(" %c",&e);
    PriorElem(L,e,e1);
    printf("元素%c的前驱是=>: %c\n",e,e1);

    //查找后继
    printf("请输入元素(查找该元素的后继)=>:");
    scanf(" %c",&e);
    NextElem(L,e,e1);
    printf("元素%c的后继是=>: %c\n",e,e1);

    ClearList(L);
    printf("执行清空表操作后,表长为=>:%d\n",ListLength(L));
    return 0;
}
发布了8 篇原创文章 · 获赞 4 · 访问量 1411

猜你喜欢

转载自blog.csdn.net/sinat_28995767/article/details/51494827