数据结构与算法系列(线性表篇)

线性表篇之顺序表

一、顺序表简单介绍

顺序表是线性表的物理存储结构之一,顺序表采用连续的地址空间存放数据元素,在高级语言中最常见的就是使用数组表示

二、顺序表相关的时间复杂度

2.1 查询的时间复杂度

由于顺序表常常是使用数组进行实现的,而在数组中定位一个元素的时间开销是常量级别的,因为通过数组的首元素地址+偏移量就可以计算出目标元素的地址,因此顺表的查询的时间复杂度是O(1)

2.2 修改元素的时间复杂度

修改一个数据元素和查询一个数据元素的时间复杂度相同,也是O(1)

2.3 插入的时间复杂度

在顺序表中插入一个新的元素,等价于在数组中插入一个新的数据元素,由于数组中的数据是连续存放的,因此如果要在数组的某个位置插入一个新的元素需要进行数组元素之间的移动,最坏的移动次数就是整个数组的长度(在数组第一个位置插入新的数据时),因此顺序表的插入操作的时间复杂度是O(N),N是数组中元素的个数

2.4 删除的时间复杂度

删除数据元素的时间复杂度和删除数据元素的时间复杂度相同,也是O(N)

小贴士:

我们在讨论时间复杂度的时候往往指的是最坏的时间复杂度

三、顺序表实现(C语言)

3.1 头文件定义

#ifndef _SEQLIST_H
#define _SEQLIST_H

// 定义顺序表的元素类型,默认为int类型
typedef int elementType;

// 声明顺序表结构类型
struct seqlist;

// 定义顺序表指针类型
typedef struct seqlist *seqlistPtr;

// 定义元素指针类型
typedef elementType *elementTypePtr;

// 定义顺序表元素比较函数
typedef int (*cmpFunc)(elementTypePtr, elementTypePtr);

// 定义元素访问函数
typedef void (*visitFunc)(elementTypePtr e);

// 根据指定的初始容量创建顺序表
// 返回顺序表结构指针
seqlistPtr create(int cap);

// 清空顺序表
void makeEmpty(seqlistPtr list);

// 判断顺序表是否为空
// 如果为空,返回true,否则返回false
int isEmpty(seqlistPtr list);

// 判断指定的元素是否是顺序表的最后一个元素
// 如果是返回1,否则返回0
int isLast(elementTypePtr e, seqlistPtr list, cmpFunc cmp);

// 返回指定元素在顺序表中的位置
// 如果不存在返回-1
int locate(elementTypePtr e, seqlistPtr list, cmpFunc cmp);

// 寻找指定元素e在顺序表中的直接前驱
// 如果不存在,返回0,否则返回1
// e的直接前驱使用pre指针接收
int findPrevious(elementTypePtr e, elementTypePtr pre, seqlistPtr list, cmpFunc cmp);

// 返回指定元素e在顺序表中的直接后继
// 如果不存在,那么返回0,否则返回1
// e的直接后继使用next指针接收
int findNext(elementTypePtr e, elementTypePtr next, seqlistPtr list, cmpFunc cmp);

// 返回顺序表中指定位置处的数据元素
// 如果不存在,那么返回0,否则返回1
// 元素使用指针e接收
int findByIndex(int index, elementTypePtr e, seqlistPtr list);

// 删除顺序表中指定的元素
// 删除成功,返回1,否则返回0
int deleteElement(elementTypePtr e, seqlistPtr list, cmpFunc cmp);

// 删除顺序表指定位置上的元素
// 删除成功,返回1,否则返回0
// 被删除的元素使用del指针来接收,该参数可以为NULL
int deleteElementByIndex(int index, elementTypePtr del, seqlistPtr list);

// 将e插入在顺序表指定的index位置上,index从0算起
// 如果插入成功,返回1,否则返回0
int insertByIndex(int index, elementTypePtr e, seqlistPtr list);

// 将元素e追加到顺序表的最后一个位置
void addLast(elementTypePtr e, seqlistPtr list);

// 将元素追加到顺序表的最后一个位置
void addFirst(elementTypePtr e, seqlistPtr list);

// 打印顺序表中的数据元素
void printList(seqlistPtr list, visitFunc visit);

// 回收顺序表的空间
void freeSeqList(seqlistPtr list);

#endif

3.2 c源文件定义

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

#include "seqlist.h"

// 声明顺序表结构类型
struct seqlist {
    // 顺序表元素基地址
    elementTypePtr base;
    // 顺序表的底层数组空间的容量大小
    int cap;
    // 顺序表的长度
    int len;
};

// 错误信息打印函数
static void fatalOf(const char *msg) {
    fprintf(stderr, "file: %s, func: %s, line: %s, info: %s\n", 
            __FILE__, __func__, __LINE__, msg);
}

// 确保list的容量
// 如果当前list的实际元素的数量小于list底层数组容量的1/4,那么将list的底层数组容量
// 缩小为当前的1/2
// 如果当前list的实际元素的数量刚好等于了list底层数组的容量,那么将list的底层数组
// 容量扩容为当前的2倍
static void ensureCap(seqlistPtr list) {
    if (!list) {
        fatalOf("非法的空参数");    
    }

    elementTypePtr tmp = NULL;
    if (list->len <= (list->cap >> 2)) {
        tmp = (elementTypePtr) realloc(list->base, 
            sizeof(elementType) * (list->cap >> 1));
        if (!tmp) {
            fatalOf("顺序表缩容失败");
        }
        list->cap >>= 1;
        list->base = tmp;
    } else if (list->len == list->cap) {
        tmp = (elementTypePtr) realloc(list->base, 
            sizeof(elementType) * (list->cap << 1));
        if (!tmp) {
            fatalOf("顺序表扩容失败");
        }
        list->cap <<= 1;
        list->base = tmp;
    }
}   

// 根据指定的初始容量创建顺序表
// 返回顺序表结构指针
seqlistPtr create(int cap) {
    seqlistPtr list = (seqlistPtr) malloc(sizeof(struct seqlist));
    if (!list) {
        fatalOf("创建顺序表失败");
    }
    
    list->base = (elementTypePtr) malloc(sizeof(elementType) * cap);
    if (!list->base) {
        fatalOf("分配顺序表元素空间失败");
    }
    
    list->len = 0;
    list->cap = cap;
    
    return list;
}

// 清空顺序表
void makeEmpty(seqlistPtr list) {
    if (!list) {
        fatalOf("list 不能为NULL");
    }

    list->len = 0;
}

// 判断顺序表是否为空
// 如果为空,返回true,否则返回false
int isEmpty(seqlistPtr list) {
    if (!list) {
        fatalOf("list 不能为NULL");
    }

    return 0 == list->len;
}

// 判断指定的元素是否是顺序表的最后一个元素
// 如果是返回1,否则返回0
int isLast(elementTypePtr e, seqlistPtr list, cmpFunc cmp) {
    if (!e || !list || !cmp) {
        fatalOf("非法的NULL形参");
    }

    int pos = locate(e, list, cmp);
    if (-1 == pos) {
        return 0;
    }

    return pos == list->len - 1;
}

// 返回指定元素在顺序表中的位置
// 如果不存在返回-1
int locate(elementTypePtr e, seqlistPtr list, cmpFunc cmp) {
    if (!e || !list || !cmp) {
        fatalOf("非法的NULL形参");
    }

    for (int i = 0; i < list->len; i++) {
        if (cmp(e, list->base + i) == 0) {
            return i;
        }
    }
    return -1;
}

// 寻找指定元素e在顺序表中的直接前驱
// 如果不存在,返回0,否则返回1
// e的直接前驱使用pre指针接收
int findPrevious(elementTypePtr e, elementTypePtr pre, seqlistPtr list, cmpFunc cmp) {
    int pos = locate(e, list, cmp);
    if (-1 == pos || 0 == pos) {
        return 0;
    }

    *pre = *(list->base + pos - 1);
    return 1;
}

// 返回指定元素e在顺序表中的直接后继
// 如果不存在,那么返回0,否则返回1
// e的直接后继使用next指针接收
int findNext(elementTypePtr e, elementTypePtr next, seqlistPtr list, cmpFunc cmp) {
    int pos = locate(e, list, cmp);
    if (-1 == pos || list->len - 1 == pos) {
        return 0;
    }

    *next = *(list->base + pos + 1);
    return 1;
}

// 返回顺序表中指定位置处的数据元素
// 如果不存在,那么返回0,否则返回1
// 元素使用指针e接收
int findByIndex(int index, elementTypePtr e, seqlistPtr list) {
    if (!e || !list) {
        fatalOf("非法的NULL形参");
    }

    if (index < 0 || index >= list->len) {
        return 0;
    }

    *e = *(list->base + index);
    return 1;
}

// 删除顺序表中指定的元素
// 删除成功,返回1,否则返回0
int deleteElement(elementTypePtr e, seqlistPtr list, cmpFunc cmp) {
    int pos = locate(e, list, cmp);
    if (-1 == pos) {
        return 0;
    }

    return deleteElementByIndex(pos, NULL, list);
}

// 删除顺序表指定位置上的元素
// 删除成功,返回1,否则返回0
// 被删除的元素使用del指针来接收,该参数可以为NULL
int deleteElementByIndex(int index, elementTypePtr del, seqlistPtr list) {
    if (!list) {
        fatalOf("list 不能为NULL");
    }

    if (index < 0 || index >= list->len) {
        return 0;
    }

    list->len--;
    if (del) {
        *del = *(list->base + index);
    }
    for (int i = index; i < list->len; i++) {
        *(list->base + i) = *(list->base + i + 1);
    }
    ensureCap(list);
    return 1;
}

// 将e插入在顺序表指定的index位置上,index从0算起
// 如果插入成功,返回1,否则返回0
int insertByIndex(int index, elementTypePtr e, seqlistPtr list) {
    if (!e || !list) {
        fatalOf("非法的NULL形参");
    }

    if (index < 0 || index > list->len) {
        return 0;
    }

    ensureCap(list);
    for (int i = list->len; i > index; i--) {
        *(list->base + i) = *(list->base + i - 1);
    }
    *(list->base + index) = *e;
    list->len++;

    return 1;
}

// 将元素e追加到顺序表的最后一个位置
void addLast(elementTypePtr e, seqlistPtr list) {
    insertByIndex(list->len, e, list);
}

// 将元素追加到顺序表的最后一个位置
void addFirst(elementTypePtr e, seqlistPtr list) {
    insertByIndex(0, e, list);
}

void printList(seqlistPtr list, visitFunc visit) {
    if (!list || !visit) {
        fatalOf("非法的NULL形参");
    }

    for (int i = 0; i < list->len; i++) {
        visit(list->base + i);
    }
}

// 回收顺序表的空间
void freeSeqList(seqlistPtr list) {
    if (!list) {
        return;
    }

    if (!list->base) {
        free(list);
        return;
    }

    free(list->base);
    free(list);
}

3.3 Makefile文件编写(Window平台下)

如果想要更改为linux平台下运行,只需要修改makefile文件中的clean部分,将del换成rm -rf

cfiles=main.c seqlist.c

target=seqlist_test.exe

obj=main.o seqlist.o

cc=gcc -std=c99

$(target):$(obj)
	$(cc) $(obj) -o $(target)

$(obj):$(cfiles)
	$(cc) -c $(cfiles)

.PHONY:clean
clean:
	del $(target) $(obj)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值