顺序表详解(SeqList)

简介: 顺序表详解(SeqList)

本文使用C语言进行顺序表的代码实现。

博主将使用代码和相关知识相结合的方式进行讲解,简单易懂,懵懂的大学生一听就会~

顺序表是一种线性表的存储结构,它将数据元素存储在一段连续的存储空间中,每个元素占据一个存储单元,并按照逻辑顺序依次存放。顺序表可以采用数组来实现,通过数组的下标来表示元素在顺序表中的位置,从而实现对元素的快速访问和操作。


一、顺序表的定义

线性表是一种基本的数据结构,它是由一组相同类型的数据元素组成的有序序列。线性表的特点是元素之间的关系是线性的,即每个元素都有一个唯一的索引(也称为位置),并且可以通过索引访问元素。

线性表有两种常见的实现方式:顺序表和链表。顺序表是使用一组连续的内存空间来存储元素,而链表则是使用一组离散的内存空间来存储元素,并通过指针将它们连接起来。

本文讲解顺序表的代码实现。

二、顺序表的实现

我们使用多文件的方法来进行顺序表的实现。

  • SeqList.h用存放需要使用的头文件及声明函数
  • SeqList.c用来实现对于顺序表的操作函数
  • test用来进行顺序表的功能测试和使用

1.顺序表的初始化

//结构体创建
typedef int SQDataType;
typedef struct SeqList
{
  SQDataType* a;
  int size;     // 有效数据的个数
  int capacity; // 容量
}SL;
//顺序表初始化
void SeqListInit(SL* ps)
{
  ps->a = NULL;
  ps->size = 0;
  ps->capacity = 0;
}

首先创建一个结构体用来存放一个指针a,数据个数和顺序表的容量。

2.设置修改检查函数

//作为每次增添时的检查函数,如果顺序表的存储满了就进行扩容
void SeqListCheckCapacity(SL* ps)
{
  // 满了就要扩容
  if (ps->size == ps->capacity)
  {
    int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
    SQDataType* tmp = (SQDataType*)realloc(ps->a, newcapacity * sizeof(SQDataType));
    if (tmp == NULL)
    {
      printf("realloc fail\n");
      exit(-1);
    }
    else
    {
      ps->a = tmp;
      ps->capacity = newcapacity;
    }
  }
}

该函数用于检查顺序表(a)是否大小足够进行添加数据,如果大小为0那么将空间设置为4,如果不够用即用realloc扩容到原大小的二倍。,之后再对相关数值进行修改。

3.从头部插入数据

void SeqListPushFront(SL* ps, SQDataType x)
{
  SeqListCheckCapacity(ps);
  int end = ps->size - 1;
  while (end >= 0)
  {
    ps->a[end + 1] = ps->a[end];
    --end;
  }
  ps->a[0] = x;
  ps->size++;
}

头插的方法相当于是将整个顺序表向后挪动一位,最后将第一个位置空下然后插入数据。但是挪动的顺序是从后往前挪动,如果是从前往后的话就会将数据覆盖,从而无法正常插入。

4.从尾部插入数据

void SeqListPushBack(SL* ps, SQDataType x)
{
  SeqListCheckCapacity(ps);
  ps->a[ps->size] = x;
  ps->size++;
}

尾插就是直接向顺序表最后添加数据。

5.指定位置插入数据

void SeqListInsert(SL* ps, int pos, SQDataType x)
{
  assert(pos <= ps->size);
  SeqListCheckCapacity(ps);
  int end = ps->size - 1;
  while (end >= pos)
  {
    ps->a[end + 1] = ps->a[end];
    --end;
  }
  //将pos位置及之后全部向后移动一位,然后再pos位置上插入x
  ps->a[pos] = x;
  ps->size++;
}

关于assert断言相关知识如果想深入学习可以点击这段话跳转至博主的另一篇博客~

首先使用assert断言进行一个条件判断,确保插入的位置合法。之后的移动数据位置的操作和头插的方法类似,将pos位置和之后的数据全部向后移动一个位置,然后再在pos这个下标的位置上添加数据。

6.从头部删除数据

void SeqListPopFront(SL* ps)
{
  assert(ps->size > 0);
  
  int start = 1;
  while (start < ps->size)
  {
    ps->a[start - 1] = ps->a[start];
    ++start;
  }
  ps->size--;
}

代码实现的操作就是将第一个位置上的位置进行覆盖,用第一个之后的数据逐一将前面一个的数据进行覆盖,从而达到删除第一个数据的效果。最后将size进行修改。

7.从尾部删除数据

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

直接将顺序表的size进行-1就可以实现删除最后一个数据的操作。

8.指定位置删除数据

void SeqListErase(SL* ps, int pos)
{
  assert(pos < ps->size);
  int start = pos + 1;
  while (start < ps->size)
  {
    ps->a[start - 1] = ps->a[start];
    ++start;
  }
  ps->size--;
}

操作与头删类似,先设置start的数值为要删除的那个下标,然后将start之后的数据依次向前挪动覆盖数据实现在该指定位置的数据删除。

9.指定位置修改数据

void SeqListModity(SL* ps, int pos, SQDataType x)
{
  assert(pos < ps->size);
  ps->a[pos] = x;
}

修改数据的操作十分简单,找到该位置的下标然后进行修改即可完成操作。

10.查找数据

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

遍历顺序表,逐个进行对比,与要查找的数据相同时返回该数据位置的下标,未找到返回-1.

三、整体程序代码展现

因为指定位置插入数据的函数和指定位置删除数据的函数可以完成头插尾插,头删尾删的操作,所以在程序中直接使用这两个插入方法进行头尾的操作减少了代码量。

在test.c中通过简单的菜单和分支操作可以对顺序表进行简单的操作。

SeqList.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include <stdlib.h>
#include <assert.h>
// 增强程序可维护性
typedef int SQDataType;
// 动态的
typedef struct SeqList
{
  SQDataType* a;
  int size;     // 有效数据的个数
  int capacity; // 容量
}SL;
//typedef struct SeqList SL;
// 增删查改等接口函数
void SeqListInit(SL* ps);
void SeqListPrint(SL* ps);
void SeqListDestory(SL* ps);
// 头插 尾插 头删 尾删
void SeqListPushBack(SL* ps, SQDataType x);
void SeqListPushFront(SL* ps, SQDataType x);
void SeqListPopBack(SL* ps);
void SeqListPopFront(SL* ps);
void SeqListInsert(SL* ps, int pos, SQDataType x);
void SeqListErase(SL* ps, int pos);
int SeqListFind(SL* ps, SQDataType x);
void SeqListModity(SL* ps, int pos, SQDataType x);

SeqList.c

#include "SeqList.h"
// 增删查改等接口函数
//初始化数组
void SeqListInit(SL* ps)
{
  ps->a = NULL;
  ps->size = 0;
  ps->capacity = 0;
}
//清除内存
void SeqListDestory(SL* ps)
{
  free(ps->a);
  ps->a = NULL;
  ps->capacity = ps->size = 0;
}
//作为每次增添时的检查函数,如果顺序表的存储满了就进行扩容
void SeqListCheckCapacity(SL* ps)
{
  // 满了就要扩容
  if (ps->size == ps->capacity)
  {
    int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
    SQDataType* tmp = (SQDataType*)realloc(ps->a, newcapacity * sizeof(SQDataType));
    if (tmp == NULL)
    {
      printf("realloc fail\n");
      exit(-1);
    }
    else
    {
      ps->a = tmp;
      ps->capacity = newcapacity;
    }
  }
}
// 头插 尾插 头删 尾删
void SeqListPushBack(SL* ps, SQDataType x)
{
  /*SeqListCheckCapacity(ps);
  ps->a[ps->size] = x;
  ps->size++;*/
  SeqListInsert(ps, ps->size, x);
}
void SeqListPushFront(SL* ps, SQDataType x)
{
  //SeqListCheckCapacity(ps);
   1、初始条件
   2、结束条件
   3、迭代过程
  //int end = ps->size - 1;
  //while (end >= 0)
  //{
  //  ps->a[end + 1] = ps->a[end];
  //  --end;
  //}
  //ps->a[0] = x;
  //ps->size++;
  SeqListInsert(ps, 0, x);
}
void SeqListPopBack(SL* ps)
{
  //assert(ps->size > 0);
  ps->a[ps->size - 1] = 0;
  //ps->size--;
  SeqListErase(ps, ps->size - 1);
}
void SeqListPopFront(SL* ps)
{
  assert(ps->size > 0);
  /*
  int start = 1;
  while (start < ps->size)
  {
    ps->a[start - 1] = ps->a[start];
    ++start;
  }
  ps->size--;*/
  SeqListErase(ps, 0);
}
void SeqListInsert(SL* ps, int pos, SQDataType x)
{
  assert(pos <= ps->size);
  SeqListCheckCapacity(ps);
  int end = ps->size - 1;
  while (end >= pos)
  {
    ps->a[end + 1] = ps->a[end];
    --end;
  }
  //将pos位置及之后全部向后移动一位,然后再pos位置上插入x
  ps->a[pos] = x;
  ps->size++;
}
void SeqListErase(SL* ps, int pos)
{
  assert(pos < ps->size);
  int start = pos + 1;
  while (start < ps->size)
  {
    ps->a[start - 1] = ps->a[start];
    ++start;
  }
  ps->size--;
}
int SeqListFind(SL* ps, SQDataType x)
{
  for (int i = 0; i < ps->size; ++i)
  {
    if (ps->a[i] == x)
    {
      return i;
    }
  }
  return -1;
}
//修改pos位置上的值为x
void SeqListModity(SL* ps, int pos, SQDataType x)
{
  assert(pos < ps->size);
  ps->a[pos] = x;
}
void SeqListPrint(SL* ps)
{
  for (int i = 0; i < ps->size; ++i)
  {
    printf("%d ", ps->a[i]);
  }
  printf("\n");
}

test.c

#include "SeqList.h"
void TestSeqList1()
{
  SL sl;
  SeqListInit(&sl);
  SeqListPushBack(&sl, 1);
  SeqListPushBack(&sl, 2);
  SeqListPushBack(&sl, 3);
  SeqListPushBack(&sl, 4);
  SeqListPushBack(&sl, 5);
  SeqListPushBack(&sl, 6);
  SeqListPushBack(&sl, 7);
  SeqListPushBack(&sl, 8);
  SeqListPushBack(&sl, 9);
  SeqListPushBack(&sl, 10);
  SeqListPushBack(&sl, 11);
  SeqListPushBack(&sl, 11);
  SeqListPushBack(&sl, 11);
  SeqListPushBack(&sl, 11);
  SeqListPushBack(&sl, 11);
  SeqListPushBack(&sl, 11);
  SeqListPushBack(&sl, 11);
  SeqListPushBack(&sl, 11);
  SeqListPushBack(&sl, 11);
  SeqListPushBack(&sl, 11);
  SeqListPushBack(&sl, 11);
  SeqListPushBack(&sl, 11);
  SeqListPushBack(&sl, 11);
  SeqListPushBack(&sl, 11);
  SeqListPushBack(&sl, 11);
  SeqListPushBack(&sl, 11);
  SeqListPushBack(&sl, 11);
  SeqListPrint(&sl);
  SeqListDestory(&sl);
}
void TestSeqList2()
{
  SL sl;
  SeqListInit(&sl);
  SeqListPushFront(&sl, 1);
  SeqListPushFront(&sl, 2);
  SeqListPushFront(&sl, 3);
  SeqListPushFront(&sl, 4);
  SeqListPushFront(&sl, 5);
  SeqListPushFront(&sl, 6);
  SeqListPrint(&sl);
  SeqListPopBack(&sl);
  SeqListPopBack(&sl);
  SeqListPopBack(&sl);
  SeqListPrint(&sl);
  SeqListPopFront(&sl);
  SeqListPrint(&sl);
  SeqListDestory(&sl);
}
void TestSeqList3()
{
  SL sl;
  SeqListInit(&sl);
  SeqListPushFront(&sl, 1);
  SeqListPushFront(&sl, 2);
  SeqListPushFront(&sl, 3);
  SeqListPushFront(&sl, 4);
  SeqListPushFront(&sl, 5);
  SeqListPushFront(&sl, 6);
  SeqListPrint(&sl);
  SeqListInsert(&sl, 1, 20);
  SeqListPrint(&sl);
  SeqListErase(&sl, 1);
  SeqListPrint(&sl);
  SeqListDestory(&sl);
}
void menu()
{
  printf("**********************************************\n");
  printf("1.尾插数据, 2.头插数据\n");
  printf("3.尾删数据, 4.头删数据\n");
  printf("5.打印数据,-1.退出\n");
  printf("**********************************************\n");
  printf("请输入你操作选项:");
}
int main()
{
  SL s;
  SeqListInit(&s);
  int option = 0;
  int x = 0;
  while (option != -1)
  {
    menu();
    scanf("%d", &option);
    switch (option)
    {
    case 1:
      printf("请输入你要插入的数据,以-1结束\n");
      do {
        scanf("%d", &x);
        if (x != -1)
        {
          SeqListPushBack(&s, x);
        }
      } while (x != -1);
      break;
    case 2:
      break;
    case 3:
      break;
    case 4:
      break;
    case 5:
      SeqListPrint(&s);
      break;
    default:
      break;
    }
  }
  SeqListDestory(&s);
  return 0;
}

那么本篇博客到此就结束了,如果可以帮助到你是博主的荣幸!

目录
相关文章
|
7月前
|
存储
【顺序表】
【顺序表】
32 0
|
2月前
|
存储 缓存
【顺序表和链表的对比】
【顺序表和链表的对比】
|
4月前
|
存储
顺序表讲解
顺序表讲解
22 0
|
10月前
|
存储
顺序表和链表(三)
顺序表和链表
35 0
|
5月前
顺序表的实现
顺序表的实现
|
6月前
|
存储 C语言
顺序表(1)
顺序表(1)
51 0
|
6月前
|
测试技术
顺序表(2)
顺序表(2)
527 0
|
6月前
|
存储 NoSQL
03 顺序表
03 顺序表
16 0
|
9月前
|
存储
顺序表详解
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
|
9月前
|
存储 C++
顺序表的实现(详解版)
顺序表的实现(详解版)