从零教你拿捏数据结构(顺序表)C语言版

简介: 从零教你拿捏数据结构(顺序表)C语言版

       首先我要知道数据结构中的线性表:线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。


1.顺序表概念及结构

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


       顺序表分为静态与动态两种,日常使用上,动态顺序表比静态顺序表要使用的多些。静态顺序表实现起来也更加简单些,使用定长数组存储元素,然后对数据进行增删查改的操作。动态顺序表则更加灵活,可以对空间大小进行调节,容量不够可以扩容。


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

大小,所以下面我们实现动态顺序表。


2.动态顺序表实现    

2.1.所需要用到的接口:

typedef int SLDatatype;
typedef struct SeqList
{
  SLDatatype* array;
  int size;//有效数据个数
  int capacity;//空间大小
}SL;
//增删查改
void SLInit(SL* ps);//初始化
void SLdestroy(SL* ps);//销毁
//打印
void SLprint(SL* ps);
//检查空间
void Inspect(SL* ps);
//尾插尾删
void SLpushBack(SL* ps, SLDatatype x);
void SLpopback(SL* ps);
//头插头删
void SLpushFront(SL*ps, SLDatatype x);
void SLpopFront(SL*ps);
//查找数据下标
int  SLFind(SL* ps, SLDatatype x);
//在pox位置插入x
void SLInsert(SL* ps, int pox,SLDatatype x);
//删除pox位置的值
void SLErase(SL* ps, int pox);
//修改pox位置的值
void SLModify(SL* ps, int pox, SLDatatype x);

2.2初始化

对顺序表进行初始化,使表的开始大小可以存储4个有效元素

void SLInit(SL* ps)
{
  assert(ps);
  ps->array = (SLDatatype*)malloc(sizeof(SLDatatype) * 4);
  if (ps->array == NULL)
  {
    perror("malloc failde");
    exit(-1);
  }
  ps->size = 0;
  ps->capacity = 4;
}

2.3空间检查

若是表中的元素个数已满,则进行扩容,扩容的后大小是原顺序表的2倍

void Inspect(SL* ps)
{
  assert(ps);
  if (ps->size == ps->capacity)
  {
    SLDatatype* tmp = (SLDatatype*)realloc(ps->array, ps->capacity * 2 * sizeof(SLDatatype));
    if (tmp == NULL)
    {
      perror("realloc failed");
      exit(-1);
    }
    ps->array = tmp;
    ps->capacity *= 2;
  }
}

2.4打印

打印顺序表中所有的元素

void SLprint(SL* ps)
{
  assert(ps);
  int i = 0;
  for (i = 0; i < ps->size; i++)
  {
    printf("%d ", ps->array[i]);
  }
  printf("\n");
}

2.5尾插

在顺序表的末尾插入元素

void SLpushBack(SL* ps, SLDatatype x)
{
  assert(ps);
  Inspect(ps);
  ps->array[ps->size] = x;
  ps->size++;
}

2.6尾删

将顺序表最后一个元素删除

void SLpopback(SL* ps)
{
  assert(ps);
  assert(ps->size > 0);
  ps->size--;
}

2.7头插

在顺序表的头部,表中首元素的前面插入新的元素,这时就需要将旧数据都往后挪一位

void SLpushFront(SL* ps, SLDatatype x)
{
  assert(ps);
  Inspect(ps);
  int len = ps->size - 1;
  while (len >= 0)
  {
    ps->array[len + 1] = ps->array[len];
    len--;
  }
  ps->array[0] = x;
  ps->size++;
}

2.8头删

将头部的首元素删除

//头删
void SLpopFront(SL* ps)
{
  assert(ps);
  int left = 1;
  while (left < ps->size)
  {
    ps->array[left - 1] = ps->array[left];
    left++;
  }
  ps->size--;
}

2.9查找数据下标

这一步是为了在指定的位置进行增删改查

int  SLFind(SL* ps, SLDatatype x)
{
  assert(ps);
  for (int i = 0; i < ps->size; i++)
  {
    if (ps->array[i] == x)
    {
      return i;
    }
  }
  return -1;
}

2.10在指定位置插入元素

查找到要插入的位置下标,将元素插入

void SLInsert(SL* ps, int pox, SLDatatype x)
{
  assert(ps);
  assert(pox >= 0 && pox <= ps->size);
  Inspect(ps);
  int end = ps->size - 1;
  while (end >= pox)
  {
    ps->array[end + 1] = ps->array[end];
    end--;
  }
  ps->array[pox] = x;
  ps->size++;
}

2.11删除指定位置的元素

查找到要删除的位置下标,将元素删除

void SLErase(SL* ps, int pox)
{
  assert(ps);
  assert(pox >= 0 && pox < ps->size);
  int begin = pox + 1;
  while (begin < ps->size)
  {
    ps->array[begin - 1] = ps->array[begin];
    begin++;
  }
  ps->size--;
}

2.12删除指定位置的值

查找到要修改的值的位置下标,将旧的元素改为新的值

void SLModify(SL* ps, int pox, SLDatatype x)
{
  assert(ps);
  assert(pox >= 0 && pox < ps->size);
  ps->array[pox] = x;
}

2.13顺序表的销毁

顺序表使用完后,可以进行释放空间的操作

void SLdestroy(SL* ps)
{
  assert(ps);
  free(ps->array);
  ps->array = NULL;
  ps->size = ps->capacity = 0;
}

这里就要提一下出顺序表的劣势了:


中间/头部的插入删除,时间复杂度为O(N)

增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。

增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

这就引申出一个问题,那有没有更好的数据结构呢?能够避开这些缺点?


答案是有的,这就是链表,下期来讲解链表都有哪些好处,以及链表如何来实现。


本期的内容就到此为止结束啦,感谢小伙伴们的阅读。

目录
相关文章
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
622 1
|
存储 算法 搜索推荐
【趣学C语言和数据结构100例】91-95
本文涵盖多个经典算法问题的C语言实现,包括堆排序、归并排序、从长整型变量中提取偶数位数、工人信息排序及无向图是否为树的判断。通过这些问题,读者可以深入了解排序算法、数据处理方法和图论基础知识,提升编程能力和算法理解。
191 4
|
存储 机器学习/深度学习 搜索推荐
【趣学C语言和数据结构100例】86-90
本文介绍并用C语言实现了五种经典排序算法:直接插入排序、折半插入排序、冒泡排序、快速排序和简单选择排序。每种算法都有其特点和适用场景,如直接插入排序适合小规模或基本有序的数据,快速排序则适用于大规模数据集,具有较高的效率。通过学习这些算法,读者可以加深对数据结构和算法设计的理解,提升解决实际问题的能力。
183 4
|
存储 算法 数据处理
【趣学C语言和数据结构100例】81-85
本文介绍了五个经典算法问题及其C语言实现,涵盖图论与树结构的基础知识。包括使用BFS求解单源最短路径、统计有向图中入度或出度为0的点数、统计无向无权图各顶点的度、折半查找及二叉排序树的查找。这些算法不仅理论意义重大,且在实际应用中极为广泛,有助于提升编程能力和数据结构理解。
163 4
|
算法 数据可视化 数据建模
【趣学C语言和数据结构100例】76-80
本文介绍了五种图论算法的C语言实现,涵盖二叉树的层次遍历及广度优先搜索(BFS)和深度优先搜索(DFS)的邻接表与邻接矩阵实现。层次遍历使用队列按层访问二叉树节点;BFS利用队列从源节点逐层遍历图节点,适用于最短路径等问题;DFS通过递归或栈深入图的分支,适合拓扑排序等场景。这些算法是数据结构和算法学习的基础,对提升编程能力和解决实际问题至关重要。
221 4
|
11月前
|
定位技术 C语言
c语言及数据结构实现简单贪吃蛇小游戏
c语言及数据结构实现简单贪吃蛇小游戏
|
12月前
|
搜索推荐 C语言
数据结构(C语言)之对归并排序的介绍与理解
归并排序是一种基于分治策略的排序算法,通过递归将数组不断分割为子数组,直到每个子数组仅剩一个元素,再逐步合并这些有序的子数组以得到最终的有序数组。递归版本中,每次分割区间为[left, mid]和[mid+1, right],确保每两个区间内数据有序后进行合并。非递归版本则通过逐步增加gap值(初始为1),先对单个元素排序,再逐步扩大到更大的区间进行合并,直至整个数组有序。归并排序的时间复杂度为O(n*logn),空间复杂度为O(n),且具有稳定性,适用于普通排序及大文件排序场景。
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】顺序表的基本运算(头歌实践教学平台习题)【合集】
本文档介绍了线性表的基本运算任务,涵盖顺序表和链表的初始化、销毁、判定是否为空、求长度、输出、查找元素、插入和删除元素等内容。通过C++代码示例详细展示了每一步骤的具体实现方法,并提供了测试说明和通关代码。 主要内容包括: - **任务描述**:实现顺序表的基本运算。 - **相关知识**:介绍线性表的基本概念及操作,如初始化、销毁、判定是否为空表等。 - **具体操作**:详述顺序表和链表的初始化、求长度、输出、查找、插入和删除元素的方法,并附有代码示例。 - **测试说明**:提供测试输入和预期输出,确保代码正确性。 - **通关代码**:给出完整的C++代码实现,帮助完成任务。 文档
406 5
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
404 5

热门文章

最新文章