【数据结构】动态顺序表的增删查改实现

简介: 【数据结构】动态顺序表的增删查改实现

概述


什么是动态顺序表?

顺序表是用一段物理地址连续存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。静态顺序表需要提前开好固定大小的数组空间,而动态顺序表的数组空间大小可以依据实际随时调整


接口实现


动态顺序表,根据需要动态的分配空间 大小,所以下面我们实现动态顺序表。首先对头文件的定义:

// SeqList.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
 // 顺序表的动态存储
typedef int SLDateType;
typedef struct SeqList
{
  SLDateType* a;  // 指向动态开辟的数组
  int size;       // 定义有效数据个数,便于统计
  int capacity;   // 容量空间的大小
}SeqList;
// 基本增删查改接口
// 对数据的管理:增删查改 
void SeqListInit(SeqList* ps);  // 顺序表初始化
void SeqListDestroy(SeqList* ps); // 检查空间,如果满了,进行增容
void SeqListPrint(SeqList* ps);  //顺序表打印
void SeqListPushBack(SeqList* ps, SLDateType x); // 顺序表尾插
void SeqListPushFront(SeqList* ps, SLDateType x); //顺序表头插
void SeqListPopFront(SeqList* ps);  //顺序表头删
void SeqListPopBack(SeqList* ps); //顺序表尾删
// 顺序表查找
int SeqListFind(SeqList* ps, SLDateType x);
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDateType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos);


对数据的管理:增删查改实现

下面是对于顺序表的初始化,销毁,打印,检测空间容量函数的具体实现:

#include "SeqList.h"
void SeqListInit(SeqList* ps)
{
  assert(ps); //断言ps是否空指针,assert为断言函数
  ps->a = NULL;
  ps->size = 0;
  ps->capacity = 0;
}
void SeqListDestroy(SeqList* ps)
{
  assert(ps);
  free(ps->a);
  ps->a = NULL;
  ps->size = ps->capacity = 0;
}
void SeqListPrint(SeqList* ps)
{
  assert(ps);
  for (inti = 0; i < ps->size; ++i)
  {
    printf("%d ", ps->a[i]);
  }
  printf("%\n");
}
void CheckCacpity(SeqList* ps)
{
  if (ps->size == ps->capacity)
  {
    int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2; //这里巧用三目运算符解决
    ps->a = (SLDateType*)realloc(ps->a, newcapacity*sizeof(SLDateType));
    ps->capacity = newcapacity;
  }
}


在做完Insert(pos位置插入x)和Erase(删除pos位置x值)函数功能后,对于增删功能我们可以复用Insert和Erase函数,以下几个接口先讲不复用Insert和Erase的实现,最后再讲复用实现:


注:注释选中行为不复用相关实现


尾插尾删

void SeqListPushBack(SeqList* ps, SLDateType x)
{
  //assert(ps);
  //CheckCacpity(ps); //检查增容
  //ps->a[ps->size] = x;
  //ps->size++;
  SeqListInsert(ps, ps->size, x);
}
void SeqListPopBack(SeqList* ps)
{
  assert(ps);
  //ps->a[ps->size - 1] = 0;
  //ps->size--;
  SeqListErase(ps, ps->size - 1);
}


头插头删

头插相对于尾插稍多些步骤,因为在表头插入,表中的数据都要往后挪动一位。

void SeqListPushFront(SeqList* ps, SLDateType x)
{
  assert(ps);
  /*CheckCacpity(ps);
  int end = ps->size;
  while (end > 0)
  {
    ps->a[end] = ps->a[end - 1];
    --end;
  }
  ps->a[0] = x;
  ++ps->size;*/
  SeqListInsert(ps, 0, x);
}
void SeqListPopFront(SeqList* ps)
{
  assert(ps);
  //int start = 0;
  //while (start < ps->size-1)
  //{
  //  ps->a[start] = ps->a[start + 1];
  //  ++start;
  //}
  //int start = 1;
  //while (start < ps->size)
  //{
  //  ps->a[start-1] = ps->a[start];
  //  ++start;
  //}
  //--ps->size;
  SeqListErase(ps, 0);
}


顺序表查找

因为顺序表本身为数组,查找的实现相对简单,我们从begain位置开始遍历即可:

int SeqListFind(SeqList* ps, SLDateType x)
{
  for (int i = 0; i < ps->size; ++i)
  {
    if (ps->a[i] == x)
    {
      return i;
    }
  }
  return -1;
}


在任意pos位置插入和删除数据


对pos位置的插入和删除,需要注意pos位置必须是在数组的有效范围内。


顺序表在pos位置插入x


实现思路:我们将pos位置之后的数据全部向后挪动(复制)一位(包括原pos位置值),,然后对pos位置进行赋值x(实际是对原pos位置值进行覆盖)

// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, size_t pos, SLDateType x)
{
  assert(ps);
  assert(pos <= ps->size);  //检查pos位置值是否越界
  CheckCacpity(ps);
  //int end = ps->size - 1;
  //while (end >= pos)
  //{
  //  ps->a[end + 1] = ps->a[end];
  //  --end;
  //}
  int end = ps->size ;
  while (end > pos) //挪动数据
  {
    ps->a[end] = ps->a[end - 1];
    --end;
  }
  ps->a[pos] = x;
  ps->size++;
}



顺序表删除pos位置的值


实现思路:我们将pos位置值用后一位数据值进行覆盖,后一位用其后一位的数据值进行覆盖,如此遍历整个pos位置后数据即可


// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos)
{
  assert(ps && pos < ps->size);
  //int start = pos;
  //while (start < ps->size-1)
  //{
  //  ps->a[start] = ps->a[start + 1];
  //  ++start;
  //}
  int start = pos+1;
  while (start < ps->size) //进行数据覆盖
  {
    ps->a[start-1] = ps->a[start];
    ++start;
  }
  ps->size--;  //最后对size标记值减一
}


总结


顺序表的优缺点


优点:顺序表相对于链表拥有连续物理空间,可以实现下标随机访问(随机读取)。

缺点:


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

对于实际,我们通常较稳定的线性表选择顺序存储,而频繁进行插入、删除操作的线性表则选择链式存储。


本节完

相关文章
|
3月前
|
存储 编译器 C语言
数据结构-顺序表详解(看这篇就足够了,哈哈哈)
数据结构-顺序表详解(看这篇就足够了,哈哈哈)
71 2
|
8天前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
2月前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习之顺序表【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
顺序表的定义和基本操作之插入;删除;按值查找;按位查找等具体详解步骤以及举例说明
|
2月前
|
存储 C语言
【数据结构】顺序表(c语言实现)(附源码)
本文介绍了线性表和顺序表的基本概念及其实现。线性表是一种有限序列,常见的线性表有顺序表、链表、栈、队列等。顺序表是一种基于连续内存地址存储数据的数据结构,其底层逻辑是数组。文章详细讲解了静态顺序表和动态顺序表的区别,并重点介绍了动态顺序表的实现,包括初始化、销毁、打印、增删查改等操作。最后,文章总结了顺序表的时间复杂度和局限性,并预告了后续关于链表的内容。
85 3
|
2月前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之顺序表习题精讲【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
顺序表的定义和基本操作之插入;删除;按值查找;按位查找习题精讲等具体详解步骤以及举例说明
|
3月前
|
存储 Java
数据结构第二篇【关于java线性表(顺序表)的基本操作】
数据结构第二篇【关于java线性表(顺序表)的基本操作】
48 6
|
3月前
|
存储 安全 Java
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
30 3
|
3月前
|
存储 C语言
探索C语言数据结构:利用顺序表完成通讯录的实现
本文介绍了如何使用C语言中的顺序表数据结构实现一个简单的通讯录,包括初始化、添加、删除、查找和保存联系人信息的操作,以及自定义结构体用于存储联系人详细信息。
41 2
|
3月前
|
存储
【数据结构】线性表和顺序表
【数据结构】线性表和顺序表
30 1
|
3月前
|
存储 算法 索引
【数据结构】——顺序表
【数据结构】——顺序表