数据结构01 —— 线性表(C)

一、线性表

线性表的分类:
线性表的分类
线性表的抽象数据类型(ADT)定义如下:

ADT 线性表(List)
Data
    数据对象为{a1, a2, ... , an}的集合,每个元素的类型相同。
Operation
    InitList(*L)
        操作结果:构造一个空的线性表 L。
    DestroyList(*L)
        初始条件:线性表 L 已经存在。
        操作结果:销毁线性表 L。
    ClearList(*L)
        初始条件:线性表 L 已经存在。
        操作结果:将 L 重置为空表。
    ListEmpty(L)
        初始条件:线性表 L 已经存在。
        操作结果:若 L 为空,则返回 true,否则返回 false。
    ListLength(L)
        初始条件:线性表 L 已经存在。
        操作结果:返回 L 中数据元素的个数。
    GetElem(L, i, *e)
        初始条件:线性表 L 已经存在,且 1 ≤ i ≤ ListLength(L)。
        操作结果:返回 L 中第 i 个数据元素。
    LocateElem(L, e)
        初始条件:线性表 L 已经存在。
        操作结果:返回 L 中第一个出现数据元素 e 的位置,若数据元素 e 不存在,则返回 0。
    PriorElem(L, cur_e, *pre_e)
        初始条件:线性表 L 已经存在。
        操作结果:若 cur_e 不是 L 的第一个元素,则返回 true 和 cur_e 的前驱(赋值给 pre_e),否则返回 false,这时 pre_e 为空。
    NextElem(L, cur_e, *next_e)
        初始条件:线性表 L 已经存在。
        操作结果:若 cur_e 不是 L 的最后一个元素,则返回 true 和 cur_e 的后继(赋值给 next_e),否则返回 false,这时 pre_e 为空。
    ListInsert(*L, i, e)
        初始条件:线性表 L 已经存在,且 1 ≤ i ≤ ListLength(L)+1。
        操作结果:在 L 中第 i 个位置之前插入新的数据元素 e,L 的长度加 1。
    ListDelete(*L, i, *e)
        初始条件:线性表 L 已经存在,且 1 ≤ i ≤ ListLength(L)。
        操作结果:删除 L 中第 i 个位置的数据元素,并用 e 返回其值,L 的长度减 1。
endADT

二、线性表的顺序存储结构

线性表的顺序存储结构在C语言中可以使用数组来实现,代码如下:

#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 20       // 存储空间的初始分配量,根据实际情况而定

/* Data1:线性表的元素类型定义 */
typedef int ElemType;    // 元素的类型,根据实际情况而定,这里假设为int

/* Data2:线性表定义 */
typedef struct
{
    ElemType data[MAXSIZE];    // 使用数组存储数据元素,其长度为MAXSIZE
    unsigned int length;       // 存储线性表当前数据的的个数
}SqList;

/* Operation1:创建空表 */
// 在 C 语言中,因为使用了 typedef 定义线性表,所以可以直接使用 SqList 关键字创建线性表表
void InitList(SqList **list_p_p)
{
    // 给线性表分配地址
    *list_p_p = (SqList *)calloc(1, sizeof(SqList));    // calloc 会将每个字节初始化为 0,而 malloc 不会
}

/* Operation2:销毁线性表 */
// 不能销毁用 SqList 关键字创建的线性表
void DestroyList(SqList **list_p_p)
{
    free(*list_p_p);    // 由于 C 语言的特性,销毁了依然可以访问,很危险
}

/* Operation3:清空线性表 */
void ClearList(SqList *const list_p)
{
    // 将数组 data 中已使用的空间循环赋值为 0,若没有必要,可以直接改变 length
    for (int i = 0; (i < list_p->length)||(i < MAXSIZE); i++)
    {
        list_p->data[i] = 0;
    }
    // 数据个数 length 设为 0
    list_p->length = 0;
}

/* Operation4:判断性表是否为空 */
int ListEmpty(const SqList *const list_p)
{
    return (list_p->length == 0);
}

/* Operation5:返回线性表长度 */
int ListLength(const SqList *const list_p)
{
    return list_p->length;
}

/* Operation6:返回线性表中第 i 个元素 */
int GetElem(const SqList *const list_p, const int i, ElemType *const elem)
{
    // 检查 i 是否超出范围
    if((i < 1)||(i > list_p->length)||(i > MAXSIZE))
    {
        return 0;    // i超过了范围,查找失败
    }
    else
    {
        *elem = list_p->data[i-1];
        return 1;
    }
}

/* Operation7:返回线性表中第 start 后(不包括 start)第一个出现 elem 的位置,范围:start+1 ~ length */
int LocateElem(const SqList *const list_p, const int start, const ElemType elem)
{
    // 循环查找
    for (int i = start; (i < list_p->length)||(i < MAXSIZE); i++)
    {
        if (list_p->data[i] == elem)
        {
            return i+1;
        }
    }
    return 0;    // 不存在返回 0
}

/* Operation8:返回 cur_elem 的前驱 */
int PriorElem(const SqList *const list_p, const ElemType cur_elem, ElemType *const pre_elem)
{
    // 先获取 cur_elem 的位置
    int cur_elem_locate = LocateElem(list_p, 0, cur_elem);

    // 判断位置信息
    if ((cur_elem_locate <= 1)||(cur_elem_locate > list_p->length)||(cur_elem_locate > MAXSIZE))
    {
        return 0;    // cur_elem 的位置信息错误,或者 cur_elem 为第一个元素
    }
    else
    {
        GetElem(list_p, cur_elem_locate - 1, pre_elem);
        return 1;
    }
}

/* Operation9:返回 cur_elem 的后继 */
int NextElem(const SqList *const list_p, const ElemType cur_elem, ElemType *const next_elem)
{
    // 先获取 cur_elem 的位置
    int cur_elem_locate = LocateElem(list_p, 0, cur_elem);

    // 判断位置信息
    if ((cur_elem_locate < 1)||(cur_elem_locate >= list_p->length)||(cur_elem_locate > MAXSIZE))
    {
        return 0;    // cur_elem 的位置信息错误,或者 cur_elem 为最后一个元素
    }
    else
    {
        GetElem(list_p, cur_elem_locate + 1, next_elem);
        return 1;
    }
    
}

/* Operation10:在线性表的第 i 个位置之前插入元素 elem */
int ListInsert(SqList *const list_p, int i, const ElemType elem)
{
    // 检查线性表是否已满
    if (list_p->length >= MAXSIZE)
    {
        return 0;    // 线性表已满,无法插入
    }
    
    // 检查 i 是否超出范围
    if((i < 1)||(i  > list_p->length+1)||(i > MAXSIZE))
    {
        return 0;    // i 信息有误,无法插入
    }

    // 第 i 个位置的数据以及后面的数据向后移一位
    for (int k = list_p->length; k >= i; k--)
    {
        list_p->data[k] = list_p->data[k-1];
    }

    // 插入数据
    list_p->data[i-1] = elem;    // 重新给第 i 个位置赋值
    list_p->length++;            // 长度加一
    return 1;
}

/* Operation11:删除线性表中第 i 个元素,并返回给 elem */
int ListDelete(SqList *const list_p, int i, ElemType *const elem)
{
    // 检查 i 是否超出范围
    if((i < 1)||(i > list_p->length)||(i > MAXSIZE))
    {
        return 0;    // i 信息有误,或者线性表为空表,无法删除
    }

    // 将第 i 个元素赋值给 elem
    *elem = list_p->data[i-1];

    // 将第 i 个位置后的元素向前移一位
    for (int k = i-1; k < list_p->length; k++)
    {
        list_p->data[k] = list_p->data[k+1];
    }

    // 长度减一
    list_p->length--;
    return 1;
}

/* 主函数:测试及示例 */
int main(void)
{
    SqList my_list;                  // 直接使用关键字生成空表,注意内部未初始化,数值不确定,需初始化为 0
    SqList *my_list_p = &my_list;    // 定义线性表指针,可以指向 my_list 空表
    ElemType my_elem = 666;

    // Test:创建空表
    InitList(&my_list_p);    // 通过 InitList() 函数分配内存,内部所有字节已初始化为 0

    // Test:插入元素
    printf("\n-------- Test:插入元素 --------\n");
    printf("Insert debug code: %d\n", ListInsert(my_list_p, 0, my_elem));          // 查看 i 超范围
    printf("Insert debug code: %d\n", ListInsert(my_list_p, MAXSIZE, my_elem));    // 查看 i 超范围
    printf("Insert debug code: %d\n", ListInsert(my_list_p, 1, my_elem));          // 查看 i 正常
    printf("-------- End --------\n");

    // Test: 删除元素
    printf("\n-------- Test:删除元素 --------\n");
    printf("Delete debug code: %d\n", ListDelete(my_list_p, 0, &my_elem));          // 查看 i 超范围
    printf("Delete debug code: %d\n", ListDelete(my_list_p, MAXSIZE, &my_elem));    // 查看 i 超范围
    printf("Delete debug code: %d\n", ListDelete(my_list_p, 1, &my_elem));          // 查看 i 正常
    printf("-------- End --------\n");

    // Test:获取线性表长度
    printf("\n-------- Test:获取线性表长度 --------\n");
    for (int i = 1; i <= MAXSIZE+5; i++)
    {
        // 循环添加元素
        ListInsert(my_list_p, i, (ElemType)(i+10));
        printf("Length debug code: %d\n", ListLength(my_list_p));
    }
    printf("-------- End --------\n");

    // Test:获取第 i 个元素
    printf("\n-------- Test:获取第 i 个元素 --------\n");
    my_elem = (ElemType) 666;
    printf("Get debug code: %d\n", GetElem(my_list_p, 0, &my_elem));            // 查看 i 超范围
    printf("my_elem = %d\n", my_elem);
    printf("Get debug code: %d\n", GetElem(my_list_p, MAXSIZE+5, &my_elem));    // 查看 i 超范围
    printf("my_elem = %d\n", my_elem);
    printf("Get debug code: %d\n", GetElem(my_list_p, 5, &my_elem));            // 查看 i 正常
    printf("my_elem = %d\n", my_elem);
    printf("-------- End --------\n");

    // Test:获取在第 start 位置之后,线性表中出现 my_elem 的位置
    printf("\n-------- Test:获取在第 start 位置之后,线性表中出现 my_elem 的位置 --------\n");
    int start = 4;
    my_elem = (ElemType)15;
    printf("Locate debug code: %d\n", LocateElem(my_list_p, start, my_elem));    // 存在 my_elem
    my_elem = (ElemType)666;
    printf("Locate debug code: %d\n", LocateElem(my_list_p, start, my_elem));    // 不存在 my_elem
    printf("-------- End --------\n");
    
    // Test:获取 cur_elem 的前驱 pre_elem 和后继 next_elem
    printf("\n-------- Test:获取 cur_elem 的前驱 pre_elem 和后继 next_elem --------\n");
    ElemType cur_elem = (ElemType)15;
    ElemType pre_elem = (ElemType)0;
    ElemType next_elem = (ElemType)0;
    printf("Prior debug code: %d\n", PriorElem(my_list_p, cur_elem, &pre_elem));
    printf("Pre_elem = %d\n", pre_elem);
    printf("Next debug code: %d\n", NextElem(my_list_p, cur_elem, &next_elem));
    printf("Pre_elem = %d\n", next_elem);
    printf("-------- End --------\n");

    // Test:测试判断是否为空、清空线性表
    printf("\n-------- Test:判断是否为空、清空线性表 --------\n");
    printf("Empty debug code: %d\n", ListEmpty(my_list_p));
    ClearList(my_list_p);
    printf("Empty debug code: %d\n", ListEmpty(my_list_p));
    printf("-------- End --------\n");

    return 0;
}

参考内容:
《大话数据结构》P42
《数据结构(C语言版)》P18

posted @ 2021-03-28 14:39  TechRice  阅读(501)  评论(0)    收藏  举报