顺序表C语言实现-教材版解读及C语言相关知识复习

  1. 前言:

    (1)文章以数据结构经典教程《数据结构(C语言版)》为蓝本进行顺序表的实现及解读。
    (2)文章的代码尽量与教材保持一致。

  2. 概述
    (1) 顺序表: 在存储结构上由一组地址连续的存储单元依次存储数据元素的数据结构称为顺序表。

  3. C语言相关知识的复习

    (1) typedef关键字:

    • typeof关键字主要作用是实现自定义类型

    • typedef关键字的两种用法:

      a. 为基本数据类型定义新的类型名:

      • typedef int newNametypedef char* str
      • 通过新的类型名规范数据类型,如教材顺序表的例子中通过将int类型变量重命名为ElemType对数据结构中的类型规范命名。

      b. 为自定义数据类型定义简洁的类型名称

      • 对于自定义的结构体,定义时需要添加struct关键字,使用typedeef定义后无需添加struct关键字,简化代码
      // 使用typedef定义
      typedef struct{
       int x;
       int y;
       }MyType;
      
      // 直接使用struct关键字定义
       struct MyType2 {
       int x;
       int y;
       };
      // 定义时的区别
      MyType myType = {1, 2};
      struct MyType2 myType2 = {1, 2};
      

      c.注意:避免在typedef自定义类型前使用conststatic等关键字,要在自定义时使用

      typedef int myInt;
      const myInt y = 1; // (×)
      
      typedef const int myInt;
      myInt y = 1;  // (v)
      

    (2) malloc的使用

    • 概述:

      • 头文件<stdlib.h>
      • malloc() 函数用来动态分配内存空间
    • 原型为``void* malloc(size_t size)```

      • size为需要分配的内存空间大小,以字节(bytes)为单位。
      • malloc()在堆区分配一块指定大小的内存空间,用来存放数据,这块内存空间不会被初始化
      • 分配成功返回该内存的地址,失败返回NULL。
      • 通常配合sizeof()函数一同使用。

    (3) realloc函数

    • 概述:

      • 头文件:<stdlib.h>
      • 修改内存指针对应的存储空间的大小。
    • realloc(void* ptr, size_t size)

      • ptr指针对应存储空间的大小修改为sizebytes
      • ptr: 需要修改的指针
      • size: 重新修改后的大小
    • 如果是扩大内存空间会出现以下两种情况:

      • ptr后又足够的内存空间,则会直接扩大内存空间,并返回原地址。
      • ptr后空间不足,会在堆区重新分配新的内存块,将数据复制到新的内存块,并返回新的地址。
    • 若调用成功,会释放原来地址,返回一个指针。

    (4) 函数指针

    • 函数指针是指向函数的指针。
    • 函数指针作为参数,例如
    int LocateElem_Sq(SqList L, int e,
                  int(*compare)(int, int)) {
    ...
    }
    

    上例中compare是一个函数指针,指向返回值为int类型,形参列表为(int, int)的函数,因此传参时传递的参数需要满足该函数签名。

  4. C语言实现及注释

#define LIST_INTI_SIZE 10  // 线性表存储空间的初始分配量;
#define LIST_INCREMENT 10  // 线性表存储空间的分配增量;

#define OVERFLOW 404  // 存储分配失败退出码
#define INDEX_ERROR 300 // 输入位置非法,导致该位置的相关操作失败的退出码;以索引范围为合法的输入范围
#define OK 200 // 操作成功的返回值

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

typedef int ElemType;  // 定义线性表的元素类型
typedef int Status;  // 通过int类型定义操作返回的状态码

typedef struct {
    ElemType *elem;  // 存储空间基址
    int length;  // 当前长度
    int list_size;  // 当前分配的存储容量(以sizeof(ElemType)为单位)
}SqList;

Status InitList_Sq(SqList *l) {
    // 构造一个空的线性表l,其长度为0,初始容量为LIST_INTI_SIZE(10)个元素。

    // 动态申请内存块
    l->elem = (ElemType *) malloc(LIST_INTI_SIZE * sizeof(ElemType));
    if(! l->elem) {
        exit(OVERFLOW);
    }
    l->length = 0;
    l->list_size = LIST_INTI_SIZE;
    return OK;
}

Status ListInsert_Sq(SqList *L, int i, ElemType e) {
    // 在顺序线性表L中第i的位置之前插入新的元素e。

    // i的合法值为 1 <= i <= ListLength_Sq(L) + 1;
    if(i < 1 || i > L->length + 1) {
        return INDEX_ERROR;
    }

    if(L->length >= L->list_size) {
        // 顺序表超出容量,扩容,元素容量增加LIST_INCREMENT。

        ElemType *newbase = (ElemType *) realloc(L->elem,
                                       (L->list_size + LIST_INCREMENT) * sizeof(ElemType));
        if(!newbase){
            exit(OVERFLOW);
        }
        L->elem = newbase;  // 重新指定新的基地址
        L->list_size += LIST_INCREMENT;
    }
    ElemType *q = &(L->elem[i-1]);  // 取插入目标位置的地址。
    ElemType *p;  // p取存储空间中最后一个元素的地址。
    for(p = &(L->elem[L->length-1]); p >= q; --p) {  // 地址值进行比较,地址q位置以后的元素后移一位。
        *(p+1) = *p;  // 地址运算,实现存储位置的后移一位。
    }
    *q = e;  // 目标位置赋值e
    ++L->length;  // 顺序表长度加1
    return OK;
}

Status ListDelete_Sq(SqList *L, int i, ElemType *e) {
    // 删除顺序表中第i的元素,并用e返回其值。

    if(i < 1 || i > L->length) {
        return INDEX_ERROR;
    }
    ElemType *p = &(L->elem[i-1]);  // 取删除的目标元素。
    *e = *p;
    ElemType *q = L->elem + L->length - 1;

    for(++p; p <= q; ++p) {
        *(p-1) = * p;  // 元素向前移动
    }
    -- L->length;
    return OK;
}

// 比较函数,用于LocateElem_Sq
Status compare(ElemType a, ElemType b) 
    return a == b ? 1 : 0;
}

int LocateElem_Sq(SqList L, ElemType e,
                  Status(*compare)(ElemType, ElemType)) {
    // 在顺序表L中查找第一个与e满足compare函数逻辑(如相等)的元素的位置。
    /* Status(*compare)(ElemType, ElemType)
     *  (1) compare是一个函数指针,指向一个返回值为Status, 形参列表为(ElemType, ElemType)的函数
     *  (2) 所以LocateElem_Sq的第三个参数为一个函数指针,函数的定义要满足函数签名的要求。
     *
     * */
    int i = 1;  // i的初始值为第1个元素的位序
    ElemType *p = L.elem; // p的初始值为第1个元素的存储位置
    while (i <= L.length && !(*compare)(*p++, e))  // 逐个比对
        ++ i;
    if(i <= L.length)
        return i;
    else
        return 0;
}

void MergeList_Sq(SqList La, SqList Lb, SqList *Lc) {
    // 将La和Lb中的元素合并到Lc中。
    // 已知顺序线性表La和Lb的元素按值非递减排序
    // 归并La和Lb得到新的顺序线性表Lc,Lc的元素也按值非递减排序
    ElemType *pa = La.elem;
    ElemType *pb = Lb.elem;
    Lc->list_size = Lc->length = La.length + Lb.length;
    ElemType *pc = Lc->elem = (ElemType*) malloc(Lc->length * sizeof(ElemType));
    if(!Lc->elem)
        exit(OVERFLOW);
    ElemType *pa_last = La.elem + La.length - 1;
    ElemType *pb_last = Lb.elem + Lb.length - 1;
    while(pa <= pa_last && pb <= pb_last) {  // 归并
        if(*pa <= *pb)
            * pc++ = * pa++;
        else
            * pc++ = * pb++;
    }
    // 向Lc中插入剩余的元素
    while(pa <= pa_last)
        * pc++ = * pa++;
    while(pb <= pb_last)
        * pc++ = * pb++;
}
void Print_List(SqList *L) {
    // 打印顺序表中的元素。
    int n = L->length;
    int i = 0;
    for(; i < n; i ++){
        printf("%d ", L->elem[i]);
    }
    printf("\n");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值