• 【数据结构】顺序表


    顺序表

    概念及结构

    顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

    顺序表一般可以分为:

    1. 静态顺序表:使用定长数组存储单元

      image-20221031200436218

    2. 动态顺序表:使用动态开辟的数组存储

    image-20221031200758371

    接口实现

    静态顺序表只适用于确定知道需要存多少个数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以实现中基本都是使用动态顺序表,根据需要动态的分配空间的大小,所以我们就用动态顺序表。

    typedef int SLDataType;
    
    //顺序表的动态存储
    
    typedef struct SeqList {
        SLDataType* a;  //指向动态开辟的数组
        int size;        //有效数据的个数
        int capacity;    //容量空间的大小
    }SeqList;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    //顺序表初始化
    void SeqListInit(SeqList* ps)
    {
        pa->a = NULL;
        ps->size = 0;
        ps->capacity = 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    //检查空间,如果满了,进行增容
    void CheckCapacity(SeqList* ps)
    {
        if(ps->size == ps->capacity)
        {
            int newCapacity = ps->capacity = 0 ? 4 : ps->capacity * 2;
            DataType* tmp = (DataType*)realloc(ps->a, newCapacity * sizeof(DataType));
            if(tmp == NULL)
            {
                perror("realloc fail");
                exit(-1);
            }
            ps->a = tmp;
            ps->capacity = newCapacity;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    //顺序表尾插
    void SeqListPushBack(SeqList* ps, DataType x)
    {
        //先判断是否需要增容
        CheckCapacity(ps);
        ps->[ps->size] = x;
        ps->size++;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    //顺序表尾删
    void SeqListPopBack(SeqList* ps)
    {
        if(ps->size == 0) //如果size是0了就结束,否则会变成-1,造成数组越界
        {
            return;
        }
        ps->size --;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    //顺序表头插
    void SeqListPushFront(SeqList* ps, DataType x)
    {
        //先判断是否需要增容
        CheckCapacity(ps);
        //需要将如图所示的数字往后移动一位
        int end = ps->size - 1;
        while(end >= 0)
        {
            ps->a[end + 1] = ps->a[end];
            end --;
        }
        ps->a[0] = x;
        ps->size ++;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    image-20221031205226594

    //顺序表头删
    void SeqListPopFront(SeqList* ps)
    {
        if(ps->size < 0)
        {
            return;
        }
        //直接让1包含之后的数字都往前移动一位
        int begin = 1;
        while(begin < ps->size)
        {
            ps->a[begin - 1] = ps->a[begin];
            begin ++;
        }
        ps->size --;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    image-20221031205919230

    //顺序表查找
    int SeqListFind(SeqList* ps, DataType x)
    {
        for(int i = 0; i < ps->size; i ++)
        {
            if(ps->a[i] == x)
            {
                return i;
            }
        }
        return -1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    //顺序表在pos位置插入x
    void SeqListInsert(SeqList* ps, int pos, DataType x)
    {
        //判断是否需要扩容
        CheckCapacity(ps);
        //pos代表的是下标, 需要将pos位置的值都往后移动一位
        //从后往前移动
        int end = ps->size - 1;
        while(end >= pos)
        {
            ps->a[end + 1] = ps->a[end];
            end --;
        }
        ps->a[pos] = x;
        ps->size ++;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    // 顺序表删除pos位置的值
    void SeqListErase(SeqList* ps, int pos)
    {
        //判断是否越界
        if(ps->size < 0)
        {
            return;
        }
        //直接让pos之后的数字都往前移动一位
        int begin = pos;
        while(begin < ps->size)
        {
            ps->a[begin] = ps[begin + 1];
            begin ++;
        }
        ps->size --;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    //顺序表的销毁
    void SeqListDestory(SeqList* ps)
    {
        if(ps->a)
        {
            ps->a = NULL;
            ps->size = ps-> capacity = 0;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    //顺序表的打印
    void SeqListPrint(SeqList* ps)
    {
        for(int i = 0; i < ps->size; i ++)
        {
            printf("%d ", ps->a[i]);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    数组相关的面试题

    原地移除数组中的所有元素val

    image-20221031214120650

    思路:

    ​ 设置两个指针,如果src不等于val则直接赋值给dest所在位置的下标,然后两个指针都加加,如果相等就只让src加加就可以

    代码:

    int removeElement(int* nums, int numsSize, int val){
        int src = 0;
        int dest = 0;
        while(src < numsSize)
        {
            if(nums[src] == val)
            {
                src ++;
            }
            else 
            {
                nums[dest] = nums[src];
                dest ++;
                src ++;
            }
        }
        return dest;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    删除排序数组中的重复项

    代码:

    int removeDuplicates(int* nums, int numsSize){
        int src = 1;
        int dest = 0;
        while(src < numsSize) 
        {
            if(nums[src] == nums[dest])
            {
                src ++;
            }
            else
            {
                dest ++;
                nums[dest] = nums[src];
                src ++;
            }
        }
        return dest + 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    image-20221101162051549

    合并两个有序数组

    image-20221103122930266

    思路:

    从后面往前面开始一个个比较,如果大的就放到最后一个。

    代码:

    void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
       int i1 = m - 1;
       int i2 = n - 1;
       int j = m + n - 1;
       while(i1 >= 0 && i2 >= 0)
       {
           if(nums1[i1] > nums2[i2])
           {
               nums1[j --] = nums1[i1 --];
           }
           else 
           {
               nums1[j --] = nums2[i2 --];
           }
       }
       while(i2 >= 0)
       {
           nums1[j --] = nums2[i2 --];
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    顺序表的问题和思考

    问题:

    1. 中间/头部的插入删除,时间复杂度为O(N)
    2. 增容需要申请新空间,拷贝数据,释放旧空间,会有不小的消耗。
    3. 增容一般是呈2倍的增长,势必会有一定的空间浪费,例如当前容量为100,满了以后增容到200,我们继续插入了5个数据,后面没有数据插入了,那么就浪费了95个空间。

    思考:

    如何解决以上问题?下面我们看看链表的结构

  • 相关阅读:
    明阳天下团建游戏------围城
    Transformer Block运算量
    Nuxt3框架全局引用外部JS/CSS文件的相关配置方法
    Dreamweaver网页设计与制作100例——HTML5期末考核大作业——票务网站整套网页
    Spire.Office for .NET 7.12.0 2022年最后版本?
    【Linux】Nignx的入门&使用负载均衡&动静分离(前后端项目部署)---超详细
    监控平台SkyWalking9入门实践
    文生视频领域SOTA工作 Show-1:论文和代码解读
    java计算机毕业设计网上图书分享系统源码+系统+mysql数据库+lw文档+部署
    学人工智能等于失业?
  • 原文地址:https://blog.csdn.net/qq_63474430/article/details/127624818