【数据结构】二、有序顺序表

写在前面

本人是一位计算机的大一新生,也是经常在网上查找文章进行学习,经常有时候会懒得做笔记了,就在网上找相应写的比较好的文章收藏便于复习,但是有时候一个知识点我需要收集好几个文章的信息并且内容多有重复,不便于整理,于是萌发了自己写文章的念头。目的是能督促自己对知识点进行总结整理,加深理解,以自己能理解的方式记录下来,也希望能帮助到向我一样努力积极勤奋的计算机新手,欢迎大家讨论交流!

如果有哪里理解的不对的地方,欢迎指正!

一、引言

有序顺序表是一种特殊的线性表,其内部的元素是有序的。

关于线性表,可以看我之前发表的文章

【数据结构】一、线性表-CSDN博客

二、有序顺序表的特点

  1. 有序性:元素按照特定的顺序排列,通常是升序或降序。

  2. 随机访问:可以通过索引快速访问表中的任意元素。

  3. 搜索效率:有序顺序表支持高效的搜索操作,如二分搜索。

  4. 插入和删除操作:插入和删除元素可能需要移动其他元素以保持顺序。

三、使用 C 语言实现有序顺序表

  1. 定义数据结构
#define MaxSize 100

typedef struct {
    int list[MaxSize];  //使用数组来存储元素
    int size;           //记录存储的元素个数
}SeqList;
  1. 基本操作
  • 插入元素:找到合适的位置插入新元素,这里使用二分法进行查找,在处理较多元素时可以大大减少时间
int SeqList_Insert(SeqList* head, int x)
{
    if (head->size >= MaxSize)
    {
        printf("顺序表已满,无法插入!\n");
        return -1;
    }

    if (head->size == 0 || head->list[head->size - 1] < x) // 边界条件处理,如果顺序表为空或者x比顺序表中的所有元素都大
    {
        head->list[head->size++] = x; // 直接将x插入到末尾
        return x;
    }

    int low = 0, high = head->size - 1;
    int mid;
    while (low <= high) // 二分查找插入位置
    {
        mid = (low + high) / 2;
        if (head->list[mid] < x)
            low = mid + 1;
        else
            high = mid - 1;
    }

    // 将大于等于x的元素后移
    for (int i = head->size; i > low; i--)
    {
        head->list[i] = head->list[i - 1];
    }
    head->list[low] = x;
    head->size++;
    return x;
}
  • 删除元素:根据值删除元素
//删除第i个位置的元素
//失败返回-1,成功返回删除的元素值
int SeqList_Delete(SeqList* head, int i)
{
    if (i >= head->size || i < 0)
    {
        printf("输入范围不合法!\n");
        return -1;
    }

    int x = head->list[i];
    for (int k = i; k < head->size - 1; k++) //减去1是为了防止数组越界
    {
        head->list[i + 1] = head->list[i];
    }
    head->size--;
    return x;
}
  • 搜索元素:使用二分搜索查找元素。
//不存在则返回-1,查找成功返回该元素下标
int SeqList_Find(SeqList head, int x)
{
    int low = 0, high = head.size - 1;
    int mid;
    while (low <= high)
    {
        mid = (low + high) / 2;
        if (head.list[mid] == x)
        {
            return mid;
        }
        else if (head.list[mid] > x)
        {
            high = mid - 1;
        }
        else
        {
            low = mid + 1;
        }
    }
    return -1;
}

四、性能分析

  1. 时间复杂度
  • 插入和删除操作的时间复杂度为$O(n)$,其中$n$为表的长度。

  • 搜索操作的时间复杂度为$O(logn)$,使用二分搜索时。

  1. 空间复杂度
  • 有序顺序表的空间复杂度为$O(n)$,用于存储元素。

五、进阶学习

对于线性表,最大的缺陷就是它的空间是有限的,在实际应用中我们 ~~直接选择使用C++~~ 如果不确定需要存储的数据大小,如果给的内存太小就会不够用,给的内存太大又会造成浪费,因此,我们可以通过动态内存分配的方法去解决。当然使用链式存储也是很好的方法,但这不是本篇文章要讲的内容。

但并不是说每添加一个元素就重新分配一次内存,这样太麻烦了,我们可以在达到内存限度的时候重新分配原来两倍的空间,这样会方便很多。

1、结构体定义

typedef struct {
    int* list;          //指针指向开辟的内存
    int size;           //记录存储的元素个数
    int capacity;       //容量
}SeqList_Var;

2、核心函数,检查是否对其进行增容

//检查是否需要增容,每次增容一倍
static void SeqCheckCapacity(SeqList_Var* head)
{
    if (head->size == head->capacity)
    {
        int newcapacity = head->capacity == 0 ? 4 : head->capacity * 2;
        int* newA = (int*)malloc(sizeof(int) * newcapacity);
        if (newA == NULL)
        {
            printf("内存分配失败!\n");
            return;
        }

        for (int i = 0; i < head->capacity; i++)   //保存原有数据
        {
            newA[i] = head->list[i];
        }
        free(head->list);
        head->list = newA;
        head->capacity = newcapacity;
    }
}

3、插入元素(这里只需要对原来的进行小小修改)

//插入一个元素x
//插入成功返回插入的元素,失败返回-1
int SeqList_Var_Insert(SeqList_Var* head, int x)
{
    SeqCheckCapacity(head);//检查是否需要增容

    if (head->size == 0 || head->list[head->size - 1] < x) // 边界条件处理,如果顺序表为空或者x比顺序表中的所有元素都大
    {
        head->list[head->size++] = x; // 直接将x插入到末尾
        return x;
    }

    int low = 0, high = head->size - 1;
    int mid;
    while (low <= high) // 二分查找插入位置
    {
        mid = (low + high) / 2;
        if (head->list[mid] < x)
            low = mid + 1;
        else
            high = mid - 1;
    }

    // 将大于等于x的元素后移
    for (int i = head->size; i > low; i--)
    {
        head->list[i] = head->list[i - 1];
    }
    head->list[low] = x;
    head->size++;
    return x;
}

4、合并两个有序顺序表

//合并两个有序表
SeqList_Var SeqList_Var_Combine(SeqList_Var a, SeqList_Var b)
{
    SeqList_Var c;
    c.list = (int*)malloc(sizeof(int) * (a.capacity + b.capacity));
    c.size = a.size + b.size;
    c.capacity = a.capacity + b.capacity;

    int pa = 0, pb = 0, pc = 0;
    while (pa != a.size && pb != b.size)//不断比较a和b,选择较小的插入c中
    {
        if (a.list[pa] < b.list[pb])
        {
            c.list[pc++] = a.list[pa++];
        }
        else
        {
            c.list[pc++] = b.list[pb++];
        }
    }
    while (pa != a.size)
    {
        c.list[pc++] = a.list[pa++];
    }
    while (pb != b.size)
    {
        c.list[pc++] = b.list[pb++];
    }

    return c;
}

简单测试一下

宾狗!😆

5、初始化和销毁(既然是分配了内存,当然需要free掉)

//初始化有序表
void SeqList_Var_Init(SeqList_Var* head)
{
    head->list = NULL;
    head->size = head->capacity = 0;
}

//释放有序表的内存
void SeqList_Var_Distory(SeqList_Var* head)
{
    free(head->list);
    head->list = NULL;
    head->size = 0;
    head->capacity = 0;
}

实际上这一部分内容使用的还是比较少的,(除了合并顺序表可能会遇到考试)为了方便增加容量,更多的还是使用链式存储,这也是我下一篇文章要写的内容

六、结论

有序顺序表是一种简单而实用的数据结构,适用于需要有序存储和高效访问的场景。

最后感谢你观看完我的文章,如果文章对你有帮助,可以点赞收藏评论,这是对作者最好的鼓励!不胜感激🥰

  • 34
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值