【C语言入门数据结构】顺序表

简介: c语言入门数据结构之顺序表 详解 增、删、查、改

🎈今日心语:你所看到的惊艳,都曾被平庸所历练。



前言:继【时间复杂度和空间复杂】度之后,本章我们来介绍数据结构中的顺序表和链表,若觉得文章不错,希望支持一下博主👍,如果发现有问题也欢迎❀大家在评论区指正。


文章目录


1.线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…

线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

2.顺序表(数组)

2.1概念及结构

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

顺序表一般可以分为静态顺序表动态顺序表

  1. 静态顺序表:使用定长数组存储元素。
  • 缺陷:给小了不够用,给大了可能浪费,不实用。

  1. 动态顺序表:使用动态开辟的数组存储。
  • 动态顺序表可根据我们的需要分配空间大小
  • size 表示当前顺序表中已存放的数据个数
  • capacity 表示顺序表总共能够存放的数据个数

2.2 动态顺序表的接口实现

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

首先新建一个工程:

  • SeqList.h(顺序表的类型定义、接口函数声明、引用的头文件)
  • SeqList.c(顺序表接口函数的实现)
  • Test.c(主函数、测试顺序表各个接口功能)

(1)初始化顺序表:

  • 记得一定要加上断言,防止传进来的指针为空
  • “->”在声明结构体指针时,访问结构体成员变量时使用。
  • “.”在声明结构体时,访问结构体成员变量时使用。
void SLInit(SL* ps)//初始化顺序表
{
  assert(ps);//断言,为真执行,为假终止,报错
  ps->a = NULL;    //初始数据表为空   ->在声明结构体指针时,访问结构体成员变量时使用
  ps->size = 0;    //初始数据个数为0
  ps->capacity = 0;//初始空间容量为0
}

(2)内存空间的释放(销毁)

void SLDestroy(SL* ps)
{
  //if(ps->a !=NULL)
  assert(ps);
  if (ps->a)         //为真说明内存开辟成功,需要释放置空
  {
    free(ps->a);   //释放,free报错往往是越界,或者是释放的地方不对,一般free的时候才会检查越界
    ps->a = NULL;  //置空
    ps->size = ps->capacity = 0;//释放后进行初始化
  }
}

(3)检查容量,若满则扩容

size和capacity相等时,可能是还没有开辟空间(这时capacity和size为0),也可能是空间已满。为了方便,我们直接将已满的空间扩大2倍,所以还需要判断是哪种情况。

  • realloc后面的参数是大小,前面的参数是需要进行扩容的空间
  • relloc返回的是新开空间的地址,需要用临时变量tmp接收地址。
void SLCheckCapacity(SL* ps)//检查,扩容
{
  assert(ps);
  //判断空间是否满,否则涉及到越界问题
  //判断size和capacity是否相等,相等则满,或者还没开辟空间,因为开始时都初始化为0
  if (ps->size == ps->capacity)
  {
    int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//如果是0还没开辟空间,如果不是就直接扩为2倍
    SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));//扩容  ,realloc后面的参数是大小,前面的是需要扩容的空间//需要用临时变量tmp接收地址,因为relloc返回的是新开空间的地址
    if (tmp == NULL)//判断扩容是否成功
    {
      perror("relloc fail");
      exit(-1);       //-1异常终止程序,0代表正常终止
    }
    ps->a = tmp;
    ps->capacity = newCapacity;
  }
}

(4)顺序表尾插

size指向的位置刚好是最后一个元素的后一个元素,所以尾插时直接插到size指向的位置即可。

//尾插   时间复杂度O(1)
void SLPushBack(SL* ps, SLDataType x)
{
  assert(ps);
  SLCheckCapacity(ps);//检查容量
  ps->a[ps->size] = x;
  ps->size++;
}
  • 写代码时应该边写边测,及时找到错误,否则后面很难找到。


(5)顺序表打印

size前面刚好是最后一个数据,所以这里 i < ps->size;

//打印函数
void SLPrint(SL* ps)
{
  assert(ps);
  for (int i = 0; i < ps->size; ++i)
  {
    printf("%d ", ps->a[i]);
  }
  printf("\n");
}

(6)顺序表尾删

  • ps->a[ps->size - 1] = 0;将最后一个数据改为0,再size–,意义不大,因为print是以size为基础的,只会访问size前面的有效数据,再插入数据会将无效数据直接覆盖,所以直接ps-> size–;
  • size–;当size=0时再进行会导致size出现负数,后面如果再尾插,插入到了不属于自己的空间,就是越界访问,所以我们前面必须进行检查,而如下“温柔的检查”很难让我们发现错误,所以使用assert(断言,“暴力的检查”)可以帮助我们及时发现错误。
//尾删
void SLPopBack(SL* ps)
{
  //温柔的检查
  //*if (ps->size == 0)
  //{
  //  return;
  //}*/
  //暴力的检查(断言)
  assert(ps->size > 0);//会直接报错,为真继续走,为假结束
  //ps->a[ps->size - 1] = 0;//将最后一个数据改为0,再size--,意义不大,因为print是以size为基础的,只会访问size前面的数据,再插入数据会将无效数据直接覆盖
  ps->size--;
}

如下图:暴力检查的好处是,当size=0时,再向下执行,会直接终止程序报错。若没有检查,size取到负值。

若不加入assert,程序会在size变为负值后进行尾插时报错。但是去掉SLDestroy(&sl);会发现程序不报错,说明程序在释放的时候才会检查出问题,但也有随机性,这时一般会报错free。

越界不一定报错,系统对越界的检查是一种抽查

  • 越界读一般是检查不出来的
  • 越界写如果是修改到标志位才会检查出来

(系统在数组末尾后设的有标志位,越界写时,恰好修改到标志位了,就会被检查出来)


(7)顺序表头插

要想头插,就得先将后面的数据从后依次向后挪动:

//头插   时间复杂度O(N)
void SLPushFront(SL* ps, SLDataType x)
{
  assert(ps);
  SLCheckCapacity(ps);//检查容量
  //挪动数据
  int end = ps->size - 1;//size-1是最后一个元素的下标
  while (end >= 0)
  {
    ps->a[end + 1] = ps->a[end];
    --end;
  }
  ps->a[0] = x;
  ps->size++;
}

(8)顺序表头删

//头删
void SLPopFront(SL* ps)
{
  assert(ps);
  assert(ps->size>0);
  int begin = 1;
  while (begin < ps->size)
  {
    ps->a[begin - 1] = ps->a[begin];
    begin++;
  }
  ps->size--;
}

(9)在指定下标位置插入数据

//在pos位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
  assert(ps);
  assert(pos >= 0);
  assert(pos <= ps->size);//必须是连续存储
  SLCheckCapacity(ps);//考虑到后面空间不够可能越界
  int end = ps->size - 1;
  while (end >= pos)
  {
    ps->a[end + 1] = ps->a[end];
    end--;
  }
  ps->a[pos] = x;
  ps->size++;
}
  • 实现了此接口,同样可以进行头插和尾插,可以进行复用以改进顺序表头插和尾插的代码:

头插尾插代码改进

//头插O(N)
void SLPushFront(SL* ps, SLDataType x)
{
  //assert(ps);
  //SLCheckCapacity(ps);
  ////挪动数据
  //int end = ps->size - 1;//size-1是最后一个元素的下标
  //while (end >= 0)
  //{
  //  ps->a[end + 1] = ps->a[end];
  //  --end;
  //}
  //ps->a[0] = x;
  //ps->size++;
  SLInsert(ps, 0, x);//复用
}
//尾插O(1)
void SLPushBack(SL* ps, SLDataType x)
{
  /*assert(ps);
  SLCheckCapacity(ps);
  ps->a[ps->size] = x;
  ps->size++;*/
  SLInsert(ps, ps->size, x);//复用
}

(10)删除指定下标位置数据

方法与头删类似,这里不作演示.

//删除pos位置数据
void SLErase(SL* ps, int pos)
{
  assert(ps);
  assert(pos >= 0);
  assert(pos < ps->size);
  //assert(ps->size > 0);
  //挪动数据覆盖
  int begin = pos + 1;
  while (begin < ps->size)
  {
    ps->a[begin - 1] = ps->a[begin];
    begin++;
  }
  ps->size--;
}
  • 实现了此接口,同样可以进行头删和尾删,同样可以进行复用以改进顺序表头删和尾删的代码:

头删尾删代码改进:

//头删
void SLPopFront(SL* ps)
{
  /*assert(ps);
  assert(ps->size>0);
  int begin = 1;
  while (begin < ps->size)
  {
    ps->a[begin - 1] = ps->a[begin];
    begin++;
  }
  ps->size--;*/
  SLErase(ps, 0);//复用
}
//尾删
void SLPopBack(SL* ps)
{
  ////温柔的检查
  ///*if (ps->size == 0)
  //{
  //  return;
  //}*/
  ////暴力的检查(断言)
  //assert(ps->size > 0);//会直接报错,为真继续走,为假结束
  ////ps->a[ps->size - 1] = 0;//将最后一个数据改为0,再size--,意义不大,因为print是以size为基础的,只会访问size前面的数据,再插入数据会将无效数据直接覆盖
  //ps->size--;
  SLErase(ps, ps->size - 1);//复用
}

(11)查找,删除指定数据

  • 查找数据初始代码:
//查找
int SLFind(SL* ps, SLDataType x)
{
  assert(ps);
  for (int i = 0; i < ps->size; i++)
  {
    if (ps->a[i] == x)
    {
      return i;//查找到,并返回下标位置
    }
  }
  return -1;////未找到,返回-1
}
  • 测试,删除数据初始代码:
void TestSeqList6()
{
  SL sl;//定义顺序表的结构
  SLInit(&sl);//初始化,传给了SeqList.c中的函数,实参,传给形参,形参是实参的临时拷贝
  SLPushBack(&sl, 1);//尾插数据
  SLPushBack(&sl, 2);//尾插数据
  SLPushBack(&sl, 3);//尾插数据
  SLPushBack(&sl, 4);//尾插数据
  SLPushBack(&sl, 5);//尾插数据
  SLPushBack(&sl, 7);//尾插数据
  SLPushBack(&sl, 8);//尾插数据
  SLPushBack(&sl, 5);//尾插数据
  SLPrint(&sl);
  int pos = SLFind(&sl, 5);//查找5
  if (pos != -1)//为真,找到了
  {
    SLErase(&sl, pos);//删除下标位置数据
  }
  SLPrint(&sl);
  SLDestroy(&sl);
}
int main()
{
  TestSeqList6();
  return 0;
}

如上图删除了指定的数据5,但是如果有几个相同的数据呢,想要删除前面的,中间的,结尾的或者全部的5该怎么办呢?

查找数据最终代码:

//查找改进,针对有重复数据
//加入参数     从begin起始位置开始查找
int SLFind(SL* ps, SLDataType x,int begin)
{
  assert(ps);
  for (int i = begin; i < ps->size; i++)
  {
    if (ps->a[i] == x)
    {
      return i;//查找到,并返回下标位置
    }
  }
  return -1;//未找到,返回-1
}

此时,测试,删除数据的代码也要进行略微改动。

测试,删除数据最终代码:

void TestSeqList6()
{
  SL sl;//定义顺序表的结构
  SLInit(&sl);//初始化,传给了SeqList.c中的函数,实参,传给形参,形参是实参的临时拷贝
  SLPushBack(&sl, 1);
  SLPushBack(&sl, 2);
  SLPushBack(&sl, 3);
  SLPushBack(&sl, 4);
  SLPushBack(&sl, 5);
  SLPushBack(&sl, 7);
  SLPushBack(&sl, 8);
  SLPushBack(&sl, 5);
  SLPrint(&sl);
  /*int pos = SLFind(&sl, 5);
  if (pos != -1)
  {
    SLErase(&sl, pos);
  }
  SLPrint(&sl);*/ //原来代码
  int pos = SLFind(&sl, 4,0);//从0开始查找
  if (pos != -1)
  {
    SLErase(&sl, pos);
  }
  SLPrint(&sl);
  //删除所有的5
  pos = SLFind(&sl, 5, 0);
  while (pos != -1)
  {
    SLErase(&sl, pos);
    pos = SLFind(&sl, 5, pos);//从删除5的位置继续往后找
  }
  SLPrint(&sl);
  SLDestroy(&sl);
}
int main()
{
  TestSeqList6();
  return 0;
}

(12)补充顺序表菜单

菜单一般在程序结束后写,方便调试。

给出简单菜单:

//菜单
void menu()
{
  printf("********************************************\n");
  printf("1、尾插数据 2、尾删数据\n");
  printf("3、头插数据 2、头删数据\n");
  printf("5、打印数据 -1、退出\n");
  printf("********************************************\n");
}
int main()
{
  SL s;//定义顺序表
  SLInit(&s);//初始化
  int val = 0;
  int option = 0;
  do
  {
    menu();
    printf("请输入你的操作:>");
    scanf("%d", &option);
    switch (option)
    {
    case 1:
      printf("请依次输入你要尾插的数据,以-1结束");
      scanf("%d", &val);
      while (val != -1)
      {
        SLPushBack(&s, val);
        scanf("%d", &val);
      }
      break;
    case 2:
      SLPopFront(&s);
      break;
    case 3:
      break;
    case 4:
      break;
    case 5:
      SLPrint(&s);
        break;
      default:
        break;
    }
  }while (option != -1);
  SLDestroy(&s);
  return 0;
}

2.3源码:

SeqList.h文件

#pragma once//防止重复调用/包含
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//动态顺序表 --按需扩空间
typedef int SLDataType;     //为方便维护修改数据类型,重定义顺序表的数据类型
typedef struct SeqList      //多个数据定义为结构体,
{
  SLDataType* a;          //想到动态开辟内存函数malloc,定义一个指针,指向动态开辟的数组
  int size;               //有效数据个数
  int capacity;           //空间容量大小,满了relloc扩容
}SL;//typedef重定义
void SLPrint(SL* ps);           //打印函数的声明
//void SeqListInit(SL s);   //全称
void SLInit(SL* ps);            //定义一个顺序表
void SLDestroy(SL* ps);         //释放动态开辟的内存
void SLCheckCapacity(SL* ps);   //检查,扩容
//尾插尾删
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);               
//头插头删
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);              
//在pos位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x);
//删除pos位置数据,删除指定位置的数据
void SLErase(SL* ps, int pos);
////查找数据,为实现删除指定数据
//int SLFind(SL* ps, SLDataType x);
//查找改进,针对有重复数据
int SLFind(SL* ps, SLDataType x, int begin);

SeqList.c文件

#include"SeqList.h"//引用头文件
void SLInit(SL* ps)
{
  assert(ps);
  //初始化
  ps->a = NULL;//结构体指针用->
  ps->size = 0;
  ps->capacity = 0;
}
//打印函数
void SLPrint(SL* ps)
{
  assert(ps);
  for (int i = 0; i < ps->size; ++i)
  {
    printf("%d ", ps->a[i]);
  }
  printf("\n");
}
void SLDestroy(SL* ps)
{
  //if(ps->a !=NULL)
  assert(ps);
  if (ps->a)         //为真说明内存开辟成功,需要释放置空
  {
    free(ps->a);   //释放,free报错往往是越界,或者是释放的地方不对,一般free的时候才会检查越界
    ps->a = NULL;  //置空
    ps->size = ps->capacity = 0;//顺便修改他俩的值
  }
}
void SLCheckCapacity(SL* ps)//检查,扩容
{
  assert(ps);
  //插入前得开空间,判断空间是否满,否则涉及到越界问题
  //判断size和capacity是否相等,相等则满,或者还没开辟空间,因为开始时都初始化为0
  if (ps->size == ps->capacity)
  {
    int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//如果是0还没开辟空间,如果不是就直接扩为2倍
    //ps->a = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));//扩容  ,realloc后面的参数是大小,前面的是需要扩容的空间//需要接收地址,因为relloc返回的是新开空间的地址
    SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));//扩容  ,realloc后面的参数是大小,前面的是需要扩容的空间//需要用临时变量tmp接收地址,因为relloc返回的是新开空间的地址
    if (tmp == NULL)//判断扩容是否成功
    {
      perror("relloc fail");
      exit(-1);//异常终止程序,0代表正常终止
    }
    ps->a = tmp;
    ps->capacity = newCapacity;
  }
}
//尾插O(1)
void SLPushBack(SL* ps, SLDataType x)
{
  /*assert(ps);
  SLCheckCapacity(ps);
  ps->a[ps->size] = x;
  ps->size++;*/
  SLInsert(ps, ps->size, x);//复用
}
//尾删
void SLPopBack(SL* ps)
{
  //温柔的检查
  /*if (ps->size == 0)
  {
    return;
  }*/
  //暴力的检查(断言)
  assert(ps->size > 0);//会直接报错,为真继续走,为假结束
  //ps->a[ps->size - 1] = 0;//将最后一个数据改为0,再size--,意义不大,因为print是以size为基础的,只会访问size前面的数据,再插入数据会将无效数据直接覆盖
  ps->size--;
  //SLErase(ps, ps->size - 1);//复用
}
//头插O(N)
void SLPushFront(SL* ps, SLDataType x)
{
  //assert(ps);
  //SLCheckCapacity(ps);
  ////挪动数据
  //int end = ps->size - 1;//size-1是最后一个元素的下标
  //while (end >= 0)
  //{
  //  ps->a[end + 1] = ps->a[end];
  //  --end;
  //}
  //ps->a[0] = x;
  //ps->size++;
  SLInsert(ps, 0, x);//复用
}
//头删
void SLPopFront(SL* ps)
{
  /*assert(ps);
  assert(ps->size>0);
  int begin = 1;
  while (begin < ps->size)
  {
    ps->a[begin - 1] = ps->a[begin];
    begin++;
  }
  ps->size--;*/
  SLErase(ps, 0);//复用
}
//在pos位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
  assert(ps);
  assert(pos >= 0);
  assert(pos <= ps->size);//必须是连续存储
  SLCheckCapacity(ps);//考虑到后面空间不够可能越界
  int end = ps->size - 1;
  while (end >= pos)
  {
    ps->a[end + 1] = ps->a[end];
    end--;
  }
  ps->a[pos] = x;
  ps->size++;
}
//删除pos位置数据
void SLErase(SL* ps, int pos)
{
  assert(ps);
  assert(pos >= 0);
  assert(pos < ps->size);
  //assert(ps->size > 0);
  //挪动数据覆盖
  int begin = pos + 1;
  while (begin < ps->size)
  {
    ps->a[begin - 1] = ps->a[begin];
    begin++;
  }
  ps->size--;
}
////查找
//int SLFind(SL* ps, SLDataType x)
//{
//  assert(ps);
//  for (int i = 0; i < ps->size; i++)
//  {
//    if (ps->a[i] == x)
//    {
//      return i;
//    }
//  }
//  return -1;
//}
//查找改进,针对有重复数据
int SLFind(SL* ps, SLDataType x,int begin)
{
  assert(ps);
  for (int i = begin; i < ps->size; i++)
  {
    if (ps->a[i] == x)
    {
      return i;
    }
  }
  return -1;
}

test.c文件

在插入菜单前,main函数中调用的测试函数可以根据需要进行修改。

#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"
void TestSeqList1()
{
  SL sl;//定义顺序表的结构
  SLInit(& sl);//初始化,传给了SeqList.c中的函数,实参,传给形参,形参是实参的临时拷贝
  SLPushBack(&sl, 1);
  SLPushBack(&sl, 2);
  SLPushBack(&sl, 3);
  SLPushBack(&sl, 4);
  SLPushBack(&sl, 5);
  SLPushBack(&sl, 5);
  SLPushBack(&sl, 5);
  SLPushBack(&sl, 5);
  SLPushBack(&sl, 5);//插入了9个数据,扩了两次容
  SLPrint(&sl);
  SLDestroy(&sl);
}
void TestSeqList2()
{
  SL sl;//定义顺序表的结构
  SLInit(&sl);//初始化,传给了SeqList.c中的函数,实参,传给形参,形参是实参的临时拷贝
  SLPushBack(&sl, 1);
  SLPushBack(&sl, 2);
  SLPushBack(&sl, 3);
  SLPushBack(&sl, 4);
  SLPrint(&sl);
  SLPopBack(&sl);
  SLPrint(&sl);
  SLPopBack(&sl);
  SLPrint(&sl);
  SLPopBack(&sl);
  SLPopBack(&sl);
  SLPopBack(&sl);
  SLPopBack(&sl);
  //SLPopBack(&sl);//越界调试,size--会导致size出现负数,后面如果再尾插,插入到了不属于自己的空间,就是越界访问
  SLPrint(&sl);
  SLPushBack(&sl, 3);
  SLPushBack(&sl, 4);
  SLDestroy(&sl);//去掉不报错
}
void TestSeqList3()
{
  SL sl;//定义顺序表的结构
  SLInit(&sl);//初始化,传给了SeqList.c中的函数,实参,传给形参,形参是实参的临时拷贝
  SLPushFront(&sl, 1);
  SLPushFront(&sl, 2);
  SLPushFront(&sl, 3);
  SLPushFront(&sl, 4);
  SLPrint(&sl);
  SLPopFront(&sl);
  SLPopFront(&sl);
  SLPopFront(&sl);
  SLPopFront(&sl);
  SLPopFront(&sl);//四个数据删除五次size为空,运行正常,可能没测试到位
  SLPushBack(&sl, 10);//这时就出现了问题,发生了越界,free会报错
  SLPrint(&sl);
  SLDestroy(&sl);
}
void TestSeqList4()
{
  SL sl;//定义顺序表的结构
  SLInit(&sl);//初始化,传给了SeqList.c中的函数,实参,传给形参,形参是实参的临时拷贝
  SLPushBack(&sl, 1);
  SLPushBack(&sl, 2);
  SLPushBack(&sl, 3);
  SLPushBack(&sl, 4);
  SLPrint(&sl);
  SLInsert(&sl, 2, 20);//第二个位置插入20
  SLPrint(&sl);
  SLInsert(&sl, 5, 500);//相当于尾插,需要考虑复用
  SLPrint(&sl);
  SLInsert(&sl, 0, 100);//相当于头插,需要考虑复用
  SLPrint(&sl);
  SLDestroy(&sl);
}
void TestSeqList5()
{
  SL sl;//定义顺序表的结构
  SLInit(&sl);//初始化,传给了SeqList.c中的函数,实参,传给形参,形参是实参的临时拷贝
  SLPushBack(&sl, 1);
  SLPushBack(&sl, 2);
  SLPushBack(&sl, 3);
  SLPushBack(&sl, 4);
  SLPrint(&sl);
  SLErase(&sl, 2);//删除下标为2的值
  SLPrint(&sl);
  SLErase(&sl, 2);//删除下标为2的值
  SLPrint(&sl);
  SLErase(&sl, 0);//删除下标为2的值
  SLPrint(&sl);
  SLDestroy(&sl);
}
void TestSeqList6()
{
  SL sl;//定义顺序表的结构
  SLInit(&sl);//初始化,传给了SeqList.c中的函数,实参,传给形参,形参是实参的临时拷贝
  SLPushBack(&sl, 1);
  SLPushBack(&sl, 2);
  SLPushBack(&sl, 3);
  SLPushBack(&sl, 4);
  SLPushBack(&sl, 5);
  SLPushBack(&sl, 7);
  SLPushBack(&sl, 8);
  SLPushBack(&sl, 5);
  SLPrint(&sl);
  /*int pos = SLFind(&sl, 5);
  if (pos != -1)
  {
    SLErase(&sl, pos);
  }
  SLPrint(&sl);*/
  int pos = SLFind(&sl, 4,0);//从0开始查找
  if (pos != -1)
  {
    SLErase(&sl, pos);
  }
  SLPrint(&sl);
  //删除所有的5
  pos = SLFind(&sl, 5, 0);
  while (pos != -1)
  {
    SLErase(&sl, pos);
    pos = SLFind(&sl, 5, pos);//从删除5的位置继续往后找
  }
  SLPrint(&sl);
  SLDestroy(&sl);
}
//int main()
//{
//  //TestSeqList1();
//  //TestSeqList2();
//  //TestSeqList3();
//  //TestSeqList4();
//  //TestSeqList5();
//  TestSeqList6();
//
//  //int* p1 = malloc(4);
//  //int* p2 = realloc(p1, 20);
//  //int* p3 = realloc(p2, 2000);//realloc原地扩容和异地扩容的演示
//
//  ////越界读基本不会被检查出来
//  //int a[10] = { 0 };
//  //printf("%d\n", a[10]);
//  //printf("%d\n", a[11]);
//  //
//  ////越界写,可能会被检查出来
//  ////a[10] = 0;//检查出来
//  //a[11] = 0;//未检查出来
//  //
//
//  return 0;
//}
//菜单
void menu()
{
  printf("********************************************\n");
  printf("1、尾插数据 2、尾删数据\n");
  printf("3、头插数据 2、头删数据\n");
  printf("5、打印数据 -1、退出\n");
  printf("********************************************\n");
}
int main()
{
  SL s;//定义顺序表
  SLInit(&s);//初始化
  int val = 0;
  int option = 0;
  do
  {
    menu();
    printf("请输入你的操作:>");
    scanf("%d", &option);
    switch (option)
    {
    case 1:
      printf("请依次输入你要尾插的数据,以-1结束");
      scanf("%d", &val);
      while (val != -1)
      {
        SLPushBack(&s, val);
        scanf("%d", &val);
      }
      break;
    case 2:
      SLPopFront(&s);
      break;
    case 3:
      break;
    case 4:
      break;
    case 5:
      SLPrint(&s);
        break;
      default:
        break;
    }
  }while (option != -1);
  SLDestroy(&s);
  return 0;
}


结语:

这里本章内容就介绍完了, 希望以上内容对大家有所帮助👀,如有不足望指出🙏

前路漫漫!努力变强💪💪 吧!!




相关文章
|
1月前
|
定位技术 C语言
c语言及数据结构实现简单贪吃蛇小游戏
c语言及数据结构实现简单贪吃蛇小游戏
|
2月前
|
搜索推荐 C语言
数据结构(C语言)之对归并排序的介绍与理解
归并排序是一种基于分治策略的排序算法,通过递归将数组不断分割为子数组,直到每个子数组仅剩一个元素,再逐步合并这些有序的子数组以得到最终的有序数组。递归版本中,每次分割区间为[left, mid]和[mid+1, right],确保每两个区间内数据有序后进行合并。非递归版本则通过逐步增加gap值(初始为1),先对单个元素排序,再逐步扩大到更大的区间进行合并,直至整个数组有序。归并排序的时间复杂度为O(n*logn),空间复杂度为O(n),且具有稳定性,适用于普通排序及大文件排序场景。
|
2月前
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】顺序表的基本运算(头歌实践教学平台习题)【合集】
本文档介绍了线性表的基本运算任务,涵盖顺序表和链表的初始化、销毁、判定是否为空、求长度、输出、查找元素、插入和删除元素等内容。通过C++代码示例详细展示了每一步骤的具体实现方法,并提供了测试说明和通关代码。 主要内容包括: - **任务描述**:实现顺序表的基本运算。 - **相关知识**:介绍线性表的基本概念及操作,如初始化、销毁、判定是否为空表等。 - **具体操作**:详述顺序表和链表的初始化、求长度、输出、查找、插入和删除元素的方法,并附有代码示例。 - **测试说明**:提供测试输入和预期输出,确保代码正确性。 - **通关代码**:给出完整的C++代码实现,帮助完成任务。 文档
77 5
|
2月前
|
存储 编译器 C语言
【C语言程序设计——入门】C语言入门与基础语法(头歌实践教学平台习题)【合集】
本文档介绍了C语言环境配置和编程任务,主要内容包括: - **C语言环境配置**:详细讲解了在Windows系统上配置C语言开发环境的步骤。 - **第1关:程序改错**:包含任务描述、相关知识(如头文件引用、基本语法规则)、编程要求、测试说明及通关代码。 - **第2关:scanf函数**:涉及`scanf`和`printf`函数的格式与使用方法,提供编程要求、测试说明及通关代码。 文档结构清晰,涵盖从环境搭建到具体编程任务的完整流程,适合初学者学习和实践。
71 4
|
2月前
|
C语言
【C语言程序设计——入门】基本数据类型与表达式(头歌实践教学平台习题)【合集】
这份文档详细介绍了编程任务的多个关卡,涵盖C语言的基础知识和应用。主要内容包括: 1. **目录**:列出所有关卡,如`print函数操作`、`转义字符使用`、`数的向上取整`等。 2. **各关卡的任务描述**:明确每关的具体编程任务,例如使用`printf`函数输出特定字符串、实现向上取整功能等。 3. **相关知识**:提供完成任务所需的背景知识,如格式化输出、算术运算符、关系运算符等。 4. **编程要求**:给出具体的代码编写提示。 5. **测试说明**:包含预期输入输出,帮助验证程序正确性。 6. 文档通过逐步引导学习者掌握C语言的基本语法和常用函数,适合初学者练习编程技能。
85 1
|
3月前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
3月前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
135 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
4月前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
127 1
|
4月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
107 1
|
14天前
|
算法 调度 C++
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
26 11