手把手教你怎么写顺序表 2

简介: 手把手教你怎么写顺序表

6.删除顺序表中成员的内容

6.1尾删

这个就很简单了,我们只需要将此时顺序表的成员数量往回拨一位就行。为什么?举个例子,成员中已经有了1,2,3,4,5那么不难得出sz此时是5,指向的是5后面的空间,而当我们把数量往回拨的话,sz就指向了4,那么此时sz就指向了5对应的空间,下次你在再增加内容的时候这个空间就会被自动覆盖掉,同样,打印的话也是根据sz来打印的,会直接略过。唯一需要注意的一点就是,当顺序表的成员为空的时候,也就是没有成员的时候,强行删除的话就会造成越界问题的发生。因为没有成员的时候,sz为0,你对它强行进行删除,那么就会导致sz=-1,下一次再增加元素的时候,就会越界访问。

void seqlist_popback(seqlist* s1)
{
  if (s1->sz == 0)
  {
    printf("顺序表为空,操作失败\n");
    return;
  }
  s1->sz--;
}

6.2头删

头删相对于尾删麻烦一些,我们通过从前往后覆盖的方式将前面的内容覆盖成后面的内容

void seqlist_popfront(seqlist* s1)
{
  if (s1->sz == 0)
  {
    printf("顺序表为空,操作失败\n");
    return;
  }
  int i = 0;
  for (i = 1; i < s1->sz; i++)
  {
    s1->a[i-1] = s1->a[i];//从前往后覆盖
  }
  s1->sz--;
}

同样,我们可以测试一下

7.查找成员

目标很简单,就是查找到目标成员然后打印出来,找不到就打印找不到,通过一次遍历就可以搞定

void seqlist_fine(const seqlist* s1,const SlDateType x)
//查找的目标是x,用const修饰是因为只是查找,不用修改
{
  if (s1->sz == 0)
  {
    printf("顺序表为空,操作失败\n");
    return;
  }
  int i = 0;
  for (i = 0;i<s1->sz; i++)
  {
    if (s1->a[i] == x)//相等意味着找到了
    {
      printf("找到%d了,下标为%d\n",x,i);
      return;
    }
  }
  printf("目标不存在\n");
}

同样可以测试一下

8.修改(替换)

给对应的下标,和被修改的结果,对目标下标的内容进行修改,要注意的是,我们修改下标的范围只能是在成员组中进行修改,什么意思?就是说,我们不能对还没放成员的下标进行修改

void seqlist_modify(seqlist* s1, int pos, const SlDateType x)
{
  if (pos >= s1->sz)
  //当pos=sz时就已经是对还不是成员的空间进行操作了,更别说后面的了
  {
    printf("当前空间还不是成员的一部分,操作失败\n");
    return;
  }
  s1->a[pos] = x;
}

同样可以测试一下

9.插入(在目标位置插入成员)

需要注意的便是,我们插入的范围是多少,很显然0~sz-1都是可以插入的,这里面已经是有内容的了,那么sz可不可以呢?可以,因为在sz这个位置插入就相当于尾插,还有一点需要注意,那便是插入成员,成员的数量是会增加的,那么也就是说,我们一样要在插入前判断是否需要扩容。

void seqlist_insert(seqlist* s1, int pos, const SlDateType x)
{
  if_enough(s1);
  if (pos > s1->sz)//比sz还大,意味着插入的位置会导致连续性被破坏
  {
    printf("插入位置出错,操作失败\n");
    return;
  }
  int i = 0;
  for (i = 0;i<s1->sz-pos; i++)
  {
    s1->a[s1->sz-i] = s1->a[s1->sz-i-1];//从后向前覆盖
  }
  s1->a[pos] = x;
  s1->sz++;
}

测试一下

10.定向删除(将目标位置的成员删除)

有了之前插入的经验,这个就显得很简单,但要注意的一点则是,它的删除范围只能是0~sz-1,sz不被包括在内,因为sz显而易见还不是成员。

void seqlist_erase(seqlist* s1, int pos)
{
  if (pos >= s1->sz)//等于sz时就已经是在删除未被定义的内容了
  {
    printf("删除位置出错,操作失败\n");
    return;
  }
  int i = 0;
  for (i=0;pos+i+1<s1->sz;i++)
  {
    s1->a[pos+i] = s1->a[pos+i+1];//从前向后覆盖
  }
  s1->sz--;
}

测试一下

当我们完成了插入和定向删除,其实之前的头插头删,尾插尾删都可以通过这两个函数来替换,这里就不再一一展示,升级版直接放在全部代码里面。

三、全部代码

1.函数头文件

// SeqList.h
//将所需函数和所需头文件的引用放在一个头文件中,那么在使用的时候就只用引用一个头文件
#pragma once//防止头文件被重复引用
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>//可能要用到的头文件
typedef int SlDateType;
//将int typedef成SlDateType这样就可以区分int和顺序表成员
// 虽然它们的类型本质上都是int但是它们的字面含义已经不同
//当然了,可以把int换成别的类型
//这样子设计其实更多的是为了到时顺序表成员类型想更换直接从这换就全换了,不用一个一个换
typedef struct seqlist
{
  SlDateType* a;
  //创建一个指针类型的顺序表成员数据,为什么是指针?
  //这样做是为了到时能够使用malloc和realloc对空间的大小进行开辟与修改
  //相当于柔性数组
  int sz;//已经存放了多少个成员
  int capacity;//容量大小,以后判定空间是否够用可以通过这个来判定
}seqlist;//将结构体名字命名为seqlist,使用时更加方便
//初始化顺序表
void init_seqlist(seqlist*s1);
//打印顺序表
void print_seqlist(const seqlist* s1);
//尾增
void seqlist_pushback(seqlist* s1, SlDateType x);
//头增
void seqlist_pushfront(seqlist* s1, SlDateType x);
//尾删
void seqlist_popback(seqlist* s1);
//头删
void seqlist_popfront(seqlist* s1);
//查找成员
void seqlist_fine(const seqlist* s1,const SlDateType x);
//修改成员
void seqlist_modify(seqlist* s1, int pos, const SlDateType x);
//插入成员
void seqlist_insert(seqlist* s1, int pos, const SlDateType x);
//删除成员
void seqlist_erase(seqlist* s1, int pos);

2.函数实现

//顺序表.c
#include"顺序表.h"
void init_seqlist(seqlist* s1)//通过指针的形式访问,便能够对内容进行修改
{
  s1->capacity = 3;//将容量大小初始化为3,为了更好地测试到时的扩容效果
  s1->sz = 0;//将成员数量初始化为0,代表着此时没有存放成员
  s1->a = (SlDateType*)malloc(sizeof(SlDateType) * s1->capacity);
  //将顺序表的成员数组大小初始化和容量一样的大小
  if (s1->a == NULL)//开辟失败的话直接退出程序
  {
    exit(-1);
  }
}
void if_enough(seqlist* s1)
{
  if (s1->sz == s1->capacity)
  //当容量和成员个数相当时,显然就已经存满了,需要扩容
  {
    s1->a = realloc(s1->a,sizeof(SlDateType)*s1->capacity*2);
    //将容量扩大到原来容量的两倍
    if (s1->a == NULL)
    {
      perror("if_enough");//错误提示
      return;//中止程序
    }
    s1->capacity *= 2;//扩容成功,容量翻倍
    printf("扩容成功,当前容量为%d\n",s1->capacity);//扩容成功给个提示
  }
}
void print_seqlist(const seqlist* s1)
//将内容打印出来,但内容是不会被改变的,因此用const修饰,避免内容被修改
{
  if (s1->sz == 0)
  {
    printf("顺序表为空,操作失败\n");
    return;
  }
  int i = 0;
  for (i = 0; i < s1->sz; i++)
  {
    printf("%d ", s1->a[i]);//将内容通过循环的方式打印出来
  }
  printf("\n");//打印完所有内容之后最好换行
}
void seqlist_pushback(seqlist*s1,SlDateType x)
{
  //if_enough(s1);
  //s1->a[s1->sz] = x;//在顺序表的最后插入一个数据x
  //s1->sz++;
  seqlist_insert(s1,s1->sz,x);
}
void seqlist_pushfront(seqlist* s1, SlDateType x)
{
  //if_enough(s1);
  //int i = 0;
  //for (i=0;i<s1->sz;i++)//通过循环从后往前覆盖
  //{
  //  s1->a[s1->sz - i] = s1->a[s1->sz-i-1];
  //}
  //s1->a[0] = x;//将首元素替换成x
  //s1->sz++;
  seqlist_insert(s1,0, x);
}
void seqlist_popback(seqlist* s1)
{
  /*if (s1->sz == 0)
  {
    printf("顺序表为空,操作失败\n");
    return;
  }
  s1->sz--;*/
  seqlist_erase(s1,s1->sz-1);
}
void seqlist_popfront(seqlist* s1)
{
  //if (s1->sz == 0)
  //{
  //  printf("顺序表为空,操作失败\n");
  //  return;
  //}
  //int i = 0;
  //for (i = 1; i < s1->sz; i++)
  //{
  //  s1->a[i-1] = s1->a[i];//从前往后覆盖
  //}
  //s1->sz--;
  seqlist_erase(s1,0);
}
void seqlist_fine(const seqlist* s1,const SlDateType x)
//查找的目标是x,用const修饰是因为只是查找,不用修改
{
  if (s1->sz == 0)
  {
    printf("顺序表为空,操作失败\n");
    return;
  }
  int i = 0;
  for (i = 0;i<s1->sz; i++)
  {
    if (s1->a[i] == x)//相等意味着找到了
    {
      printf("找到%d了,下标为%d\n",x,i);
      return;
    }
  }
  printf("目标不存在\n");
}
void seqlist_modify(seqlist* s1, int pos, const SlDateType x)
{
  if (pos >= s1->sz)
  //当pos=sz时就已经是对还不是成员的空间进行操作了,更别说后面的了
  {
    printf("当前空间还不是成员的一部分,操作失败\n");
    return;
  }
  s1->a[pos] = x;
}
void seqlist_insert(seqlist* s1, int pos, const SlDateType x)
{
  if_enough(s1);
  if (pos > s1->sz)//比sz还大,意味着插入的位置会导致连续性被破坏
  {
    printf("插入位置出错,操作失败\n");
    return;
  }
  int i = 0;
  for (i = 0;i<s1->sz-pos; i++)
  {
    s1->a[s1->sz-i] = s1->a[s1->sz-i-1];//从后向前覆盖
  }
  s1->a[pos] = x;
  s1->sz++;
}
void seqlist_erase(seqlist* s1, int pos)
{
  if (pos >= s1->sz)//等于sz时就已经是在删除未被定义的内容了
  {
    printf("删除位置出错,操作失败\n");
    return;
  }
  int i = 0;
  for (i=0;pos+i+1<s1->sz;i++)
  {
    s1->a[pos+i] = s1->a[pos+i+1];//从前向后覆盖
  }
  s1->sz--;
}

好了,今天的分享到这里就结束了,感谢各位友友的来访,祝各位友友前程似锦O(∩_∩)O

相关文章
|
算法 前端开发
三维形体投影面积
三维形体投影面积
107 0
|
12月前
|
Web App开发 XML 移动开发
HTML5 MathML
HTML5 支持 MathML,用于显示数学公式,通过 `&lt;math&gt;` 标签实现。MathML 是基于 XML 的标准,但目前仅 Firefox 和 Safari 浏览器支持。其他浏览器可借助第三方库如 mspace.js 来实现兼容。示例代码展示了如何使用 MathML 显示勾股定理、二次方程及矩阵。
|
分布式计算 并行计算 数据处理
大规模数据处理的最佳实践:使用 Dask 进行高效并行计算
【8月更文第29天】在大数据时代,高效地处理大规模数据集是至关重要的。Python 社区提供了一些强大的工具来帮助开发者进行并行和分布式计算,其中之一就是 Dask。本文将详细介绍如何使用 Dask 来优化大规模数据集的处理效率,并提供一些实用的代码示例。
1998 3
|
数据可视化 关系型数据库 MySQL
|
存储 缓存 安全
通用缓存存储设计实践
通用缓存存储设计实践
4889 3
通用缓存存储设计实践
|
存储 安全 Java
利用POI多线程导出数据错位解决
通过反射替换解决
951 0
|
存储 Java 数据库
SpringBoot整合InfluxDB
SpringBoot整合InfluxDB
553 0
|
算法 程序员 编译器
你是真的“C”——Visual Studio 2022(VS2022)编译器 -—实用调试技巧(上)
前言🙌 1. 什么是bug?🙌 2. 调试是什么?有多重要?🙌 2.1 调试是什么? 2.2 调试的基本步骤 2.3 Debug和Release的介绍。 3. Windows环境调试介绍🙌 3.1 调试环境的准备 3.2 学会快捷键 3.3 调试的时候查看程序当前信息 3.3.1 查看临时变量的值 3.3.2 查看内存信息 3.3.3 查看调用堆栈 3.3.4 查看汇编信息 3.3.5 查看寄存器信息
786 0
|
移动开发 前端开发 JavaScript
前端|利用js实现在日历中的签到效果
前端|利用js实现在日历中的签到效果
383 0
|
机器学习/深度学习
连载|逻辑回归
连载|逻辑回归