顺序表(2)

简介: 顺序表(2)

今天接着顺序表

Test.c主函数

int main()
{
  SL ps;//创建一个结构题变量-传地址调用🆗-形参是实参的一份临时拷贝
  test5(&ps);//任意位置插入
  test6(&ps);//任意位置删除
  test7(&ps);//查找
  return 0;
}

test5

void test5(SL* ps)//测试在任意位置插入数据
{
  SLInit(ps);
  SLPushBack(ps, 10);
  SLPushBack(ps, 20);
  SLPushBack(ps, 30);
  SLPushBack(ps, 40);
  SLInsert(ps, 3, 7);
  SLInsert(ps, 4, 9);
  SLPrint(ps);
  SLDestroy(ps);
}

test6

void test6(SL* ps)
{
  SLInit(ps);
  SLPushBack(ps, 10);
  SLPushBack(ps, 20);
  SLPushBack(ps, 30);
  SLPushBack(ps, 40);
  SLErase(ps, 1);
  SLPrint(ps);
  SLErase(ps, 2);
  SLPrint(ps);
    SLDestroy(ps);
}

test7

void test7(SL* ps)
{
  SLInit(ps);
  SLPushBack(ps, 10);
  SLPushBack(ps, 20);
  SLPushBack(ps, 30);
  SLPushBack(ps, 40);
  if (SLFind(ps, 10) == 1)
  {
    printf("找到了\n");
  }
  else
  {
    printf("未能找到\n");
  }
  SLDestroy(ps);
}

菜单

在前面我们写过【通讯录】的多文件代码,有人询问为什么这里不先写【菜单】建议是:最后把所有功能测试成功,再去写【菜单】,这样好【调试】。


写菜单可以使用:switch&case 或 if&else

如果我们要把用户输入的数据分布放到每个函数实现内部,菜单中代码会微少,用switch&case

如果我们要把用户输入的数据放到菜单里面,菜单中代码会多,用if&else

因为我们之前【三子棋】&【扫雷】&【通讯录】都使用switch&case,这里我们就用if&else写。还有补充,在数据结构当中我们一般是不写菜单的,只要功能正确即可。建议,写好函数,先单个测试,没问题,最后在写菜单。

void menu()
{
  printf("*******************************\n");
  printf("1、尾插数据  2、尾删数据\n");
  printf("3、头插数据  4、头删数据\n");
  printf("5、打印数据  0、退出  \n");
  printf("*******************************\n");
}
int main()
{
  SL s;
  SLInit(&s);
  int option = 0;
  do
  {
    menu();
    printf("请输入你的选择:>");
    scanf("%d", &option);
    if (option == 1)//尾插
    {
      printf("请依次输入你的要尾插数据个数和数据:>");
      int n = 0;
      scanf("%d", &n);
      for (int i = 0; i < n; i++)
      {
        int x = 0;
        scanf("%d", &x);
        SLPushBack(&s, x);
      }
    }
    else if (option == 2)//尾删
    {
      printf("请依次输入你的要尾删数据个数和数据:>");
      int n = 0;
      scanf("%d", &n);
      for (int i = 0; i < n; i++)
      {
        int x = 0;
        scanf("%d", &x);
        SLPopBack(&s, x);
      }
    }
    else if (option == 3)//头插
    {
      printf("请依次输入你的要头插数据个数和数据:>");
      int n = 0;
      scanf("%d", &n);
      for (int i = 0; i < n; i++)
      {
        int x = 0;
        scanf("%d", &x);
        SLPushFront(&s, x);
      }
    }
    else if (option == 4)//头删
    {
      printf("请依次输入你的要头删数据个数和数据:>");
      int n = 0;
      scanf("%d", &n);
      for (int i = 0; i < n; i++)
      {
        int x = 0;
        scanf("%d", &x);
        SLPopFront(&s, x);
      }
    }
    else if (option == 5)//打印
    {
      SLPrint(&s);
    }
    else if (option == 0)
    {
      break;
    }
    else
    {
      printf("无此选项,请重新输入\n");
    }
  } while (option != 0);
  SLDestroy(&s);
  return 0;
}
/*printf("请输入你的要尾插的数据,以-1结束:>");
      int x = 0;
      scanf("%d", &x);
      while (x != -1)
      {
        SLPushBack(&s, x);
        scanf("%d", &x);
      }*/

Test.c总代码

#include"SeqList.h"
void test1(SL*ps)//测试尾插
{
  SLInit(ps);
  SLPushBack(ps, 10);
  SLPushBack(ps, 20);
  SLPushBack(ps, 30);
  SLPushBack(ps, 40);
  SLPrint(ps);
  SLDestroy(ps);
}
void test2(SL*ps)//测试头插
{
  SLInit(ps);
  SLPushFront(ps, 10);
  SLPushFront(ps, 20);
  SLPushFront(ps, 30);
  SLPushFront(ps, 40);
  SLPrint(ps);
  SLDestroy(ps);
}
void test3(SL*ps)//测试头删
{
  SLInit(ps);
  SLPushBack(ps, 10);
  SLPushBack(ps, 20);
  SLPushBack(ps, 30);
  SLPushBack(ps, 40);
  SLPopBack(ps);
  SLPopBack(ps);
  SLPrint(ps);
  SLDestroy(ps);
}
void test4(SL*ps)//测试尾删//
{
  SLInit(ps);
  SLPushBack(ps, 10);
  SLPushBack(ps, 20);
  SLPushBack(ps, 30);
  SLPushBack(ps, 40);
  SLPopFront(ps);
  SLPopFront(ps);
  SLPrint(ps);
  SLDestroy(ps);
}
void test5(SL* ps)
{
  SLInit(ps);
  SLPushBack(ps, 10);
  SLPushBack(ps, 20);
  SLPushBack(ps, 30);
  SLPushBack(ps, 40);
  SLInsert(ps, 3, 7);
  SLInsert(ps, 4, 9);
  SLPrint(ps);
  SLDestroy(ps);
}
void test6(SL* ps)
{
  SLInit(ps);
  SLPushBack(ps, 10);
  SLPushBack(ps, 20);
  SLPushBack(ps, 30);
  SLPushBack(ps, 40);
  SLErase(ps, 1);
  SLPrint(ps);
  SLErase(ps, 2);
  SLPrint(ps);
    SLDestroy(ps);
}
void test7(SL* ps)
{
  SLInit(ps);
  SLPushBack(ps, 10);
  SLPushBack(ps, 20);
  SLPushBack(ps, 30);
  SLPushBack(ps, 40);
  if (SLFind(ps, 10) == 1)
  {
    printf("找到了\n");
  }
  else
  {
    printf("未能找到\n");
  }
  SLDestroy(ps);
}
int main()
{
  SL ps;//创建一个结构题变量-传地址调用🆗-形参是实参的一份临时拷贝
  test1(&ps);//测试尾插 
  test2(&ps);//测试头插
  test3(&ps);//测试尾删
  test4(&ps);//测试头删
  test5(&ps);//任意位置插入
  test6(&ps);//任意位置删除
  test7(&ps);//查找
  return 0;
}

SeqList.h头文件&函数声明

头文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>//断言

函数声明

int SLFind(SL* ps, SLDataType x);//查找
void SLInsert(SL* ps);//任意位置插入
void SLErase(SL* ps);//任意位置删除

SeqList.h总代码

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>//断言
//声明一个结构体
typedef int SLDataType;
typedef struct SeqList
{
  SLDataType* a;//如果后期a的类型改变就很方便
  int size;//有效数据
  int capacity;//空间容量
}SL;//SL是这个结构体的类型,用typedef定义更加方便了
//初始化
void SLInit(SL* ps);
//释放销毁
void SLDestroy(SL* ps);
//展示
void SLPrint(SL* ps);
void SLPushBack(SL* ps, SLDataType x);//尾插
void SLPushFront(SL* ps, SLDataType x);//头插
void SLPopBack(SL* ps);//尾删
void SLPopBack(SL* ps);//头删
void SLInsert(SL* ps,int pos, SLDataType x);//任意位置插入
void SLErase(SL* ps,int pos);//任意位置删除
int SLFind(SL* ps, SLDataType x);//查找

SeqList.c函数实现

查找SeqListFind

  • 遍历一遍数组去查找元素
//元素查找
//找到了返回1
//没有找到返回-1
int SLFind(SL* ps, SLDataType x)
{
  assert(ps);
  int i = 0;
  for (i = 0; i < ps->size - 1; i++)
  {
    if (ps->a[i] == x)
    {
      return 1;
    }
  }
  return -1;
}

某位置插入SeqListInsert

  • pos是指我们从第一个位置往后数(数组下标是从0开始)
  • 数据往后挪动,从后往前依次向后挪动
  • 在pos-1的下标位置处放入元素,不要忘记size++哦
  • size是数据个数,看作下标的话,它是最后一个数据的下一个位置。

//任意位置插入
void SLInsert(SL* ps, int pos, SLDataType x)
{
  assert(ps);
  assert(pos >= 0 && pos <= ps->size);
  SLCheckCapacity(ps);
  int begin = ps->size;
  while (begin >= pos - 1)
  {
    ps->a[begin] = ps->a[begin - 1];
    begin--;
  }
  ps->a[pos - 1] = x;
  ps->size++;
}

某位置删除SeqListErase

  • pos是指我们从第一个位置往后数(数组下标是从0开始)
  • 数据往前挪动,从前往后依次向前挪动
  • 在pos-1的下标位置处放入元素,不要忘记size++哦
//任意位置删除
void SLErase(SL* ps, int pos)
{
  assert(ps);
  assert(pos >= 0 && pos <= ps->size);
  SLCheckCapacity(ps);
  int begin = pos - 1;
  while (begin < ps->size-1)
  {
    ps->a[begin] = ps->a[begin + 1];//会越界,注意循环条件
    begin++;
  }
  ps->size--;
}

SeqList.c总代码

#include"SeqList.h"
//初始化
void SLInit(SL* ps)
{
  ps->a = NULL;
  ps->size = 0;
  ps->capacity = 0;
}
//关于初始化 可以首先置为NULL  也可以首先放点值  
// memset一般用于数组初始化 直接初始化更加清晰
//销毁
void SLDestroy(SL* ps)
{
  if (ps->a != NULL)
  {
    free(ps->a);
    ps->a = NULL;
    ps->size = 0;
    ps->capacity = 0;
  }
}
//展示
void SLPrint(SL* ps)
{
  int i = 0;
  for (i = 0; i < ps->size; i++)
  {
    printf("%d ", ps->a[i]);
  }
  printf("\n");
}
//扩容
void SLCheckCapacity(SL* ps)
{
  if (ps->size == ps->capacity)//容量满了需要扩容的条件
  {
    int newcapacity = ps->capacity == 0 ? 4 : 2 * (ps->capacity);
    SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * newcapacity);
    if (tmp == NULL)
    {
      perror("CheckCapacity");//
      return;
    }
    ps->a = tmp;
    ps->capacity = newcapacity;
  }
}
//尾插
void SLPushBack(SL* ps, SLDataType x)
{
  SLCheckCapacity(ps);//扩容
  ps->a[ps->size] = x;
  ps->size++;
}
//头插
void SLPushFront(SL* ps, SLDataType x)
{
  SLCheckCapacity(ps);
  int end = ps->size - 1;
  while (end >= 0)//注意可以等于0 size为1 但是不能为负数会越界访问
  {
    ps->a[end+1] = ps->a[end];
    end--;
  }
  ps->a[0] = x;
  ps->size++;
}
//尾删
void SLPopBack(SL* ps)
{
  assert(ps->size);//本质问题就是害怕这个顺序表空了还在删除
  ps->size--;
}
//头删
void SLPopFront(SL* ps)
{
  assert(ps->size);
  int begin = 1;
  while (begin < ps->size)
  {
    ps->a[begin] = ps->a[begin + 1];
    begin++;
  }
  ps->size--;
}
//任意位置插入
void SLInsert(SL* ps, int pos, SLDataType x)
{
  assert(ps);
  assert(pos >= 0 && pos <= ps->size);
  SLCheckCapacity(ps);
  int begin = ps->size;
  while (begin >= pos - 1)
  {
    ps->a[begin] = ps->a[begin - 1];
    begin--;
  }
  ps->a[pos - 1] = x;
  ps->size++;
}
//任意位置删除
void SLErase(SL* ps, int pos)
{
  assert(ps);
  assert(pos >= 0 && pos <= ps->size);
  SLCheckCapacity(ps);
  int begin = pos - 1;
  while (begin < ps->size-1)
  {
    ps->a[begin] = ps->a[begin + 1];//会越界注意循环条件
    begin++;
  }
  ps->size--;
}
//元素查找
//找到了返回1
//没有找到返回-1
int SLFind(SL* ps, SLDataType x)
{
  assert(ps);
  int i = 0;
  for (i = 0; i < ps->size - 1; i++)
  {
    if (ps->a[i] == x)
    {
      return 1;
    }
  }
  return -1;
}


顺序表的问题及其思考

  • 中间/头部的插入删除,时间复杂度为O(N)
  • 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  • 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。


多文件调试小技巧

  • 连带错误❌
  • 退出码不为0❌
  • 不要每个函数都去查看,要找到具体错误地方再去一步步调试❌
  • 指针断言❌
  • free报错是 越界访问的问题---开了这么多访问多❌
  • 指向的释放位置错误----没开这么多以为有这么多❌
  • realloc报错是 越界访问的问题❌
  • 越界访问错误不一定会被检查出来,大概率会被检查出来而已❌(类比查酒驾)

最后为什么数组的下标从0开始?? 【指针和数组是相互融洽的】

思考:如何解决以上顺序表的问题?下章博客我们将介绍链表的结构来看看。

代码---------→【唐棣棣 (TSQXG) - Gitee.com

联系---------→【邮箱:2784139418@qq.com】

目录
相关文章
|
6月前
|
存储 测试技术 C语言
顺序表详解(SeqList)
顺序表详解(SeqList)
179 0
|
存储
【顺序表】
【顺序表】
51 0
|
5月前
|
算法
顺序表的应用
顺序表的应用
38 5
|
5月前
|
存储 算法
顺序表专题
顺序表专题
40 4
|
5月前
|
存储
25.顺序表专题
25.顺序表专题
|
存储
顺序表和链表(三)
顺序表和链表
48 0
|
6月前
|
存储
顺序表讲解
顺序表讲解
49 0
|
6月前
顺序表的实现
顺序表的实现
|
存储 C语言
顺序表(1)
顺序表(1)
77 0
|
存储 NoSQL
03 顺序表
03 顺序表
32 0