【C/数据结构与算法】:顺序表的实现

简介: 【C/数据结构与算法】:顺序表的实现

1. 顺序表的定义

顺序表是用一段物理地址连续存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。动态顺序表与数组的本质区别是——根据需要动态的开辟空间大小。

2. 顺序表的功能

动态顺序表的功能一般有如下几个:

  • 初始化顺序表
  • 打印顺序表中的数据
  • 检查顺序表的容量
  • 顺序表头部插入数据
  • 顺序表尾部插入数据
  • 顺序表头部删除数据
  • 顺序表尾部删除数据
  • 顺序表任意位置插入数据
  • 顺序表任意位置删除数据
  • 顺序表中查找数据
  • 顺序表中修改指定位置数据
  • 顺序表的销毁

3.顺序表各功能的实现

3.1 创建顺序表

typedef int SQDateType;//对int进行重命名,可增加代码的普适性。
typedef struct SeqList
{
  SQDateType* a;
  size_t size; //有效数据
  size_t capacity;//容量的空间大小
}SL;//对这个结构体重命名为SL,书写方便

3.2 初始化顺序表

由于建立顺序表后并没有原始空间,所以我们自行动态开辟空间,又因为后续要进行扩容,所以我们必须初始化容量大小。

void SeqListInit(SL*ps)
{
  ps->a = (SQDateType*)malloc(sizeof(SQDateType) * 4);//初始化开辟4个int类型的空间
  //检查是否开辟成功
  if (ps->a == NULL)
  {
    printf("malloc fail!\n");
    return;
  }
  ps->capacity = 4;//初始化容量大小为4
  ps->size = 0;
}

3.3 打印顺序表中的数据

对顺序表进行增删查改等操作后,我们要把数据打印在控制台以便观察。

void SeqListPrint(SL* ps)
{
  //判断顺序表中是否有数据
  assert(ps->size > 0);
  for (int i = 0; i < ps->size; i++)
  {
    printf("%d ", ps->a[i]);
  }
  printf("\n");
}

3.4 检查顺序表的容量

对顺序表进行插入(增加)数据的操作时,我们必须先对顺序表进行容量的检查,若容量不够,则扩容。

并且扩容的幅度一般是原来容量的2倍。

void CheckSeqListCapacity(SL*ps)
{
  assert(ps);//断言
  //检查容量是否已满
  if (ps->capacity == ps->size)
  {
    //扩容
    SQDateType* tmp = (SQDateType*)realloc(ps->a, ps->capacity * 2 * sizeof(SQDateType*));
    if (tmp == NULL)
    {
      printf("realloc fail!\n");
      return;
    }
    else
    {
      ps->a = tmp;
      ps->capacity *= 2;//容量增大为原来的两倍
    }
  }
}

3.5 顺序表头部插入数据

头插,意思是在顺序表的最前面插入一个数据。我们需要把顺序表里的原数据(如果原来有数据的话)从后往前向后挪一个空间,再把要插入的数据放到第一个位置即可。时间复杂度:O(N).

代码实现如下:

void SeqListPushFront(SL* ps, SQDateType x)
{
     assert(ps);
  //检查是否要扩容
  CheckSeqListCapacity(ps);
  int end = ps->size;
  for (end = ps->size ; end >0; end--)
  {
    ps->a[end] = ps->a[end - 1];
  }
  ps->a[end] = x;
  ps->size++;//有效数据+1
}

3.6 顺序表尾部插入数据

尾插,意思是在顺序表的最后面插入一个数据。只需要找到该位置的下标(ps->size处)直接插入即可。

代码实现如下:

void SeqListPushBack(SL* ps, SQDateType x)
{
     assert(ps);
     //检查是否要扩容
  CheckSeqListCapacity(ps);
  ps->a[ps->size] = x;
  ps->size++;//有效数据+1
}

3.7 顺序表头部删除数据

头删,意思是删除一个顺序表最前面的数据。只需把原数据从前往后开始向前覆盖一个空间即可。时间复杂度:O(N).

代码实现如下:

void SeqListPopFront(SL* ps)
{
  assert(ps->size > 0);//断言,当顺序表内有数据时才删除
  
  for (int start = 0; start < ps->size; start++)
  {
    ps->a[start] = ps->a[start + 1];
  }
  ps->size--;//有效数据-1
}

3.8 顺序表尾部删除数据

尾删,直接删除顺序表中最后一个数据即可。

void SeqListPopBack(SL* ps)
{
  assert(ps->size > 0);
  ps->size--;//有效数据-1
}

3.9 顺序表任意位置插入数据

在顺序表的pos位置处插入一个数据,先要把pos处及其后面的原数据向后挪一个空间,再把数据放入pos处。时间复杂度:O(N).

代码实现如下:

void SeqListInsert(SL* ps, int pos, SQDateType x)
{
  assert(ps && pos < ps->size);//pos<size时才满足
  CheckSeqListCapacity(ps);
  int end = ps->size ;
  for (end = ps->size ; end > pos; end--)
  {
    ps->a[end] = ps->a[end-1];
  }
  ps->a[pos] = x;//在pos的位置放入数据
  ps->size++;//有效数据+1
}

3.10 顺序表任意位置删除数据

在顺序表的pos位置处删除一个数据,只要把pos处后面的数据向前覆盖一个空间。时间复杂度:O(N).

代码实现如下:

void SeqListEarse(SL* ps, int pos)
{
  assert(ps && pos < ps->size);//pos<size时才满足
  int start = pos;
  for (start = pos; start < ps->size; start++)
  {
    ps->a[start] = ps->a[start + 1];
  }
  ps->size--;//有效数据-1
}

3.11 顺序表中查找数据

把顺序表中的数据循环遍历,判断每个数据与查找数据是否相等,若相等,返回1,否则返回-1。

int  SeqListFind(SL* ps, SQDateType x)
{
  assert(ps && ps->size > 0);//表中有数据时才能查找
  for (int i = 0; i < ps->size; i++)
  {
    if (ps->a[i] == x)
    {
      return 1;
      break;
    }
  }
  return -1;
}

3.12 顺序表中修改指定位置数据

只要在顺序表中找到指定位置,把修改的值赋给它即可。

void SeqListModify(SL* ps, int pos, SQDateType x)
{
  assert(ps && pos < ps->size);//要修改的位置要小于数据个数
  ps->a[pos] = x;
}

3.13 顺序表的销毁

顺序表的空间的动态开辟的,最后需要free释放空间,避免造成空间泄漏。

void SeqListDestory(SL* ps)
{
  free(ps->a);
  ps->a = NULL;
  ps->capacity = 0;
  ps->size = 0;
}

4. 完整代码

SeqList.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SQDateType;
typedef struct SeqList
{
  SQDateType* a;
  size_t size; //有效数据
  size_t capacity;//容量的空间大小
}SL;
//初始化顺序表
void SeqListInit(SL* ps);
//头部插入数据
void SeqListPushFront(SL* ps, SQDateType x);
//尾部插入数据
void SeqListPushBack(SL* ps, SQDateType x);
//头部删除数据
void SeqListPopFront(SL* ps);
//尾部删除数据
void SeqListPopBack(SL* ps);
//任意位置插入数据
void SeqListInsert(SL* ps, int pos,SQDateType x);
//任意位置删除数据
void SeqListEarse(SL* ps, int pos);
//打印数据函数
void SeqListPrint(SL* ps);
//查找数据
int SeqListFind(SL* ps, SQDateType x);
//修改指定位置数据
void SeqListModify(SL* ps, int pos, SQDateType x);
//销毁顺序表
void SeqListDestory(SL* ps);

SeqList.c

#define _CRT_SECURE_NO_WARNINGS 
#include "SeqList.h"
void SeqListInit(SL*ps)
{
  ps->a = (SQDateType*)malloc(sizeof(SQDateType) * 4);//初始化开辟4个int类型的空间
  if (ps->a == NULL)
  {
    printf("malloc fail!\n");
    return;
  }
  ps->capacity = 4;
  ps->size = 0;
}
void CheckSeqListCapacity(SL*ps)
{
  assert(ps);
  //检查容量是否已满
  if (ps->capacity == ps->size)
  {
    //扩容
    SQDateType* tmp = (SQDateType*)realloc(ps->a, ps->capacity * 2 * sizeof(SQDateType*));
    if (tmp == NULL)
    {
      printf("realloc fail!\n");
      return;
    }
    else
    {
      ps->a = tmp;
      ps->capacity *= 2;
    }
  }
}
void SeqListPushFront(SL* ps, SQDateType x)
{
  assert(ps);
  CheckSeqListCapacity(ps);
  int end = ps->size;
  for (end = ps->size ; end >0; end--)
  {
    ps->a[end] = ps->a[end - 1];
  }
  ps->a[end] = x;
  ps->size++;
}
void SeqListPrint(SL* ps)
{
  //判断顺序表中是否有数据
  assert(ps->size > 0);
  for (int i = 0; i < ps->size; i++)
  {
    printf("%d ", ps->a[i]);
  }
  printf("\n");
}
void SeqListPushBack(SL* ps, SQDateType x)
{
  CheckSeqListCapacity(ps);
  ps->a[ps->size] = x;
  ps->size++;
}
void SeqListPopFront(SL* ps)
{
  assert(ps->size > 0);
  for (int start = 0; start < ps->size; start++)
  {
    ps->a[start] = ps->a[start + 1];
  }
  ps->size--;
}
void SeqListPopBack(SL* ps)
{
  assert(ps->size > 0);
  ps->size--;
}
void SeqListInsert(SL* ps, int pos, SQDateType x)
{
  assert(ps && pos < ps->size);
  CheckSeqListCapacity(ps);
  int end = ps->size ;
  for (end = ps->size ; end > pos; end--)
  {
    ps->a[end] = ps->a[end-1];
  }
  ps->a[pos] = x;
  ps->size++;
}
void SeqListEarse(SL* ps, int pos)
{
  assert(ps && pos < ps->size);
  int start = pos;
  for (start = pos; start < ps->size; start++)
  {
    ps->a[start] = ps->a[start + 1];
  }
  ps->size--;
}
int  SeqListFind(SL* ps, SQDateType x)
{
  assert(ps && ps->size > 0);
  for (int i = 0; i < ps->size; i++)
  {
    if (ps->a[i] == x)
    {
      return 1;
      break;
    }
  }
  return -1;
}
void SeqListModify(SL* ps, int pos, SQDateType x)
{
  assert(ps && pos < ps->size);
  ps->a[pos] = x;
}
void SeqListDestory(SL* ps)
{
  free(ps->a);
  ps->a = NULL;
  ps->capacity = 0;
  ps->size = 0;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 
#include "SeqList.h"
void SeqListTest()
{
  SL sl;
  
  //在这里调用各数据接口(函数)进行测试
}
int main()
{
  SeqListTest();
  return 0;
}
目录
相关文章
|
3月前
|
存储 C语言
【数据结构】顺序表
数据结构中的动态顺序表
39 3
【数据结构】顺序表
|
4月前
|
存储
数据结构—顺序表(如果想知道顺序表的全部基础知识点,那么只看这一篇就足够了!)
数据结构—顺序表(如果想知道顺序表的全部基础知识点,那么只看这一篇就足够了!)
|
16天前
|
存储 Java 程序员
【数据结构】初识集合&深入剖析顺序表(Arraylist)
Java集合框架主要由接口、实现类及迭代器组成,包括Collection和Map两大类。Collection涵盖List(有序、可重复)、Set(无序、不可重复),Map则由键值对构成。集合通过接口定义基本操作,具体实现由各类如ArrayList、HashSet等提供。迭代器允许遍历集合而不暴露其实现细节。List系列集合元素有序且可重复,Set系列元素无序且不可重复。集合遍历可通过迭代器、增强for循环、普通for循环及Lambda表达式实现,各有适用场景。其中ArrayList实现了动态数组功能,可根据需求自动调整大小。
28 11
|
24天前
|
存储 C语言 C++
数据结构基础详解(C语言) 顺序表:顺序表静态分配和动态分配增删改查基本操作的基本介绍及c语言代码实现
本文介绍了顺序表的定义及其在C/C++中的实现方法。顺序表通过连续存储空间实现线性表,使逻辑上相邻的元素在物理位置上也相邻。文章详细描述了静态分配与动态分配两种方式下的顺序表定义、初始化、插入、删除、查找等基本操作,并提供了具体代码示例。静态分配方式下顺序表的长度固定,而动态分配则可根据需求调整大小。此外,还总结了顺序表的优点,如随机访问效率高、存储密度大,以及缺点,如扩展不便和插入删除操作成本高等特点。
|
24天前
|
存储 算法 C语言
C语言手撕数据结构代码_顺序表_静态存储_动态存储
本文介绍了基于静态和动态存储的顺序表操作实现,涵盖创建、删除、插入、合并、求交集与差集、逆置及循环移动等常见操作。通过详细的C语言代码示例,展示了如何高效地处理顺序表数据结构的各种问题。
|
2月前
|
存储 算法
【数据结构与算法】顺序表
【数据结构与算法】顺序表
17 0
【数据结构与算法】顺序表
|
2月前
|
存储 算法
【初阶数据结构篇】顺序表和链表算法题
此题可以先找到中间节点,然后把后半部分逆置,最近前后两部分一一比对,如果节点的值全部相同,则即为回文。
|
2月前
|
存储 测试技术
【初阶数据结构篇】顺序表的实现(赋源码)
线性表(linearlist)是n个具有相同特性的数据元素的有限序列。
|
2月前
|
存储 编译器
【数据结构】顺序表(长期维护)
【数据结构】顺序表(长期维护)
|
2月前
|
存储 缓存
【数据结构】——顺序表与链表
【数据结构】——顺序表与链表
下一篇
无影云桌面