线性表(一)顺序表

一、线性表(linear list)的定义

一个线性表是n个数据元素的有限序列。

注:

1) 数据元素是数据结构的基本单位,可以是一个数或一个符号,也可以是一本书甚至更复杂的信息。在稍复杂线性表中,一个数据元素可以由若干数据项组成。

2) 同一线性表中的元素必定具有相同特性,即属于同一数据对象(类型相同)

二、顺序表C语言实现

 这里我们使用库函数malloc创建一个动态数组,指定元素类型和分配空间大小后,malloc会从空闲链表中找到一块合适大小的内存块。这里分配的内存块(连续内存区域)就是我们的数组存放的地方。然后我们把数组的首地址和容量存到一个struct结构体里,并给这个结构体一个别名,就叫SqList

注意顺序表结构的定义,和基本操作(共计12个)的方法定义,统统放进h头文件中,具体方法的实现放到c文件中。

大部分基本操作还是很简单,其中要注意的是destroy方法中释放数组空间后,需要将首地址的指针置空

最复杂的是ListInsert方法,因为在插入新元素前需要进行容量判断,如果不够需要进行扩容,主要是通过realloc进行内存重新分配。

SqList.h

#define LIST_INIT_SIZE 100  // 顺序表存储空间的初始分配量
#define LISTINCREMENT  10   // 顺序表存储空间的分配增量

/* 顺序表元素类型定义 */
typedef int ElemType;

/*
 * 顺序表结构
 *
 */
typedef struct {
    ElemType* elem;     // 指向顺序表所占内存的起始位置
    int length;
    int listsize;
} SqList;

Status InitList(SqList* L);

Status DestroyList(SqList* L);

Status ClearList(SqList* L);

Status ListEmpty(SqList L);

int ListLength(SqList L);
Status GetElem(SqList L, int i, ElemType* e); int LocateElem(SqList L, ElemType e, Status(Compare)(ElemType, ElemType)); Status PriorElem(SqList L, ElemType cur_e, ElemType* pre_e); Status NextElem(SqList L, ElemType cur_e, ElemType* next_e); Status ListInsert(SqList* L, int i, ElemType e); Status ListDelete(SqList* L, int i, ElemType* e); void ListTraverse(SqList L, void (Visit)(ElemType));

SqList.c

#include "SqList.h"
Status InitList(SqList* L) { // 分配指定容量的内存,如果分配失败,则返回NULL (*L).elem = (ElemType*) malloc(LIST_INIT_SIZE * sizeof(ElemType)); if((*L).elem == NULL) { // 存储内存失败 exit(OVERFLOW); } (*L).length = 0; // 初始化顺序表长度为0 (*L).listsize = LIST_INIT_SIZE; // 顺序表初始内存分配量 return OK; // 初始化成功 } Status DestroyList(SqList* L) { // 确保顺序表结构存在 if(L == NULL || (*L).elem == NULL) { return ERROR; } // 释放顺序表内存 free((*L).elem); // 释放内存后置空指针 (*L).elem = NULL; // 顺序表长度跟容量都归零 (*L).length = 0; (*L).listsize = 0; return OK; } Status ClearList(SqList* L) { // 确保顺序表结构存在 if(L == NULL || (*L).elem == NULL) { return ERROR; } (*L).length = 0; return OK; } Status ListEmpty(SqList L) { return L.length == 0 ? TRUE : FALSE; } int ListLength(SqList L) { return L.length; } Status GetElem(SqList L, int i, ElemType* e) { // 因为i的含义是位置,所以其合法范围是:[1, length] if(i < 1 || i > L.length) { return ERROR; //i值不合法 } *e = L.elem[i - 1]; return OK; } int LocateElem(SqList L, ElemType e, Status(Compare)(ElemType, ElemType)) { int i; ElemType* p; // 确保顺序表结构存在 if(L.elem == NULL) { return ERROR; } /* * i的初值为第1个元素的位序 */ i = 1; // p的初值为第1个元素的存储位置 p = L.elem; // 遍历顺序表 while(i <= L.length && !Compare(*p++, e)) { ++i; } if(i <= L.length) { return i; } else { return 0; } } Status PriorElem(SqList L, ElemType cur_e, ElemType* pre_e) { int i; // 确保顺序表结构存在,且最少包含两个元素 if(L.elem == NULL || L.length < 2) { return ERROR; } // 这里的i初始化为第1个元素的【索引】 i = 0; // 从第1个元素开始,查找cur_e的位置 while(i < L.length && L.elem[i] != cur_e) { ++i; } // 如果cur_e是首个元素(没有前驱),或者没找到元素cur_e,返回ERROR if(i==0 || i >= L.length) { return ERROR; } // 存储cur_e的前驱 *pre_e = L.elem[i - 1]; return OK; }
Status NextElem(SqList L, ElemType cur_e, ElemType* next_e) { int i; // 确保顺序表结构存在,且最少包含两个元素 if(L.elem == NULL || L.length < 2) { return ERROR; } // 这里的i初始化为第1个元素的【索引】 i = 0; // 从第1个元素开始,查找cur_e的位置 while(i < L.length-1 && L.elem[i] != cur_e) { ++i; } // 如果cur_e是最后1个元素(没有前驱),或者没找到元素cur_e,返回ERROR if(i >= L.length-1) { return ERROR; } // 存储cur_e的前驱 *next_e = L.elem[i + 1]; return OK; } Status ListInsert(SqList* L, int i, ElemType e) { ElemType* newbase; ElemType* p, * q; // 确保顺序表结构存在 if(L == NULL || (*L).elem == NULL) { return ERROR; } // i值越界 if(i < 1 || i > (*L).length + 1) { return ERROR; } // 若存储空间已满,则增加新空间 if((*L).length >= (*L).listsize) { // 基于现有空间扩容 newbase = (ElemType*) realloc((*L).elem, ((*L).listsize + LISTINCREMENT) * sizeof(ElemType)); if(newbase == NULL) { // 存储内存失败 exit(OVERFLOW); } // 新基址 (*L).elem = newbase; // 存的存储空间 (*L).listsize += LISTINCREMENT; } // q为插入位置 q = &(*L).elem[i - 1]; // 1.右移元素,腾出位置 for(p = &(*L).elem[(*L).length - 1]; p >= q; --p) { *(p + 1) = *p; } // 2.插入e *q = e; // 3.表长增1 (*L).length++; return OK; } Status ListDelete(SqList* L, int i, ElemType* e) { ElemType* p, * q; // 确保顺序表结构存在 if(L == NULL || (*L).elem == NULL) { return ERROR; } // i值越界 if(i < 1 || i > (*L).length) { return ERROR; } // p为被删除元素的位置 p = &(*L).elem[i - 1]; // 1.获取被删除元素 *e = *p; // 表尾元素位置 q = (*L).elem + (*L).length - 1; // 2.左移元素,被删除元素的位置上会有新元素进来 for(++p; p <= q; ++p) { *(p - 1) = *p; } // 3.表长减1 (*L).length--; return OK; }
void ListTraverse(SqList L, void(Visit)(ElemType)) { int i; for(i = 0; i < L.length; i++) { Visit(L.elem[i]); } printf("\n"); }

三、A=A∪B

/*
 *
 * A=A∪B
 *
 * 计算La与Lb的并集并返回。
 * 由于生成的并集会拼接在La上,所以La的入参为指针类型。
 */
void Union(SqList* La, SqList Lb) {
    int La_len, Lb_len;
    int i;
    ElemType e;

    // 求顺序表长度
    La_len = ListLength(*La);
    Lb_len = ListLength(Lb);

    for(i = 1; i <= Lb_len; i++) {
        // 取Lb中第i个元素赋给e
        GetElem(Lb, i, &e);

        // 若e不在La中则插入
        if(!LocateElem(*La, e, equal)) {
            ListInsert(La, ++La_len, e);
        }
    }
}

/*
 * 判等
 *
 * 判断两元素是否相等。
 * 如果相等,则返回TRUE,否则,返回FALSE。
 */
Status equal(ElemType e1, ElemType e2) {
    return e1 == e2 ? TRUE : FALSE;
}

 四、C=A∪B(A、B有非递减,求C仍保持非递减)

用整形变量 i 和 j 分别代表A和B在遍历中的当前位置,将两个表同时从第一个位置开始向后遍历,每次获取到A和B的元素进行比较,较小的元素插入表C

遍历的元素被选中插入C,则这个表继续往下遍历,否则仍停留在这个位置

这样两个表是同时开始,但是以不同的速度往后推进遍历,其中有一个表会先遍历完。比如表A先遍历完了,那么此时的 i 必然已经大于La_len,而 j 必然还小于等于Lb_len,此时单独用一个循环取出B中所有元素插入C

因为GetElem的时间复杂度是O(1),而ListInsert因为每次都是在最后一个位置插入,所以时间复杂度也是O(1)

所以整体时间复杂度是O(La_len + Lb_len)

void MergeSqList(SqList La, SqList Lb, SqList* Lc) {
    int La_len, Lb_len;
    int i, j, k;
    ElemType ai, bj;
    
    i = j = 1;
    k = 0;
    
    // 初始化Lc
    InitList(Lc);
    
    // 获取La、Lb的长度
    La_len = ListLength(La);
    Lb_len = ListLength(Lb);
    
    // 如果La及Lb均未遍历完
    while(i <= La_len && j <= Lb_len) {
        GetElem(La, i, &ai);
        GetElem(Lb, j, &bj);
        
        // 比较遍历到的元素,先将比较小的元素加入顺序表Lc
        if(ai <= bj) {
            ListInsert(Lc, ++k, ai);
            i++;
        } else {
            ListInsert(Lc, ++k, bj);
            j++;
        }
    }
    
    // 如果Lb已遍历完,但La还未遍历完,将La中剩余元素加入Lc
    while(i <= La_len) {
        GetElem(La, i++, &ai);
        ListInsert(Lc, ++k, ai);
    }
    
    // 如果La已遍历完,但Lb还未遍历完,将Lb中剩余元素加入Lc
    while(j <= Lb_len) {
        GetElem(Lb, j++, &bj);
        ListInsert(Lc, ++k, bj);
    }

 

posted @ 2022-09-03 00:31  方山客  阅读(83)  评论(0编辑  收藏  举报