数据结构第二课 -----线性表之顺序表

简介: 数据结构第二课 -----线性表之顺序表

线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使

用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…

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


顺序表

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

顺序表一般分为两种:


1.静态顺序表:使用定长数组存储元素。

#include<stdio.h>
#define N 6
typedef int SLDatatype;
typedef struct SLSelist
{
  SLDatatype slt[N];//数组
  size_t count;//计算个数
} SLT;
int main()
{
  SLT selist = { {1,2,3},3 };
  printf("%d", selist.count);
  return 0;
}

047d5052f2384f094a961311270fae4a_2fa83256f9d54540917f3cbe6e6e1ddf.png

一般静态的顺序表用处不大,这里主要介绍动态顺序表

2. 动态顺序表: 使用动态开辟的数组存储。


#include<stdio.h>
typedef int SLDatatype;
typedef struct SLSelist
{
  SLDatatype* Sel;
  size_t count;//个数
  size_t capacity;//容量
}SeList;

动态顺序表的使用

动态顺序表开辟扩大问题

在使用动态顺序表的大小,一般都是先开辟一定数量的空间,如果开辟的空间不够用时我们就可以利用realloc扩大空间,我们时常为扩大多少空间烦恼,在顺序表中是没有规定扩大多少,一个个扩大或几千或者几万的扩大都是可以的,但是为了节省空间,和减少扩大频率,我们默认一般都是把目前的空间扩大两倍,比如定义大小为100 下次扩大为200,下次为400,

ff7babc4de14aac03bb537187b762aaf_42fd43ae6f004948a7db2169b10404d1.png

遇到问题就要具体分析,空间的扩大没有限制,按实际情况来


顺序表的初始化

//初始化
void SLinit(SeList* SL)
{
  //方式1
  /*SL->Sel = NULL;
  SL->count = 0;
  SL->capacity = 0;*/
  //方式2
  SL->Sel = calloc(SIZE, sizeof(SLDatatype));
  SL->count = 0;
  SL->capacity = SIZE;
}

初始化有两种一种是没有给空间的初始化,一种是给空间的初始化。所以我们可以二选一


顺序表的销毁

因为我们是动态开辟。所以我们要使用free来释放空间,否则就会可能造成内存泄漏,这里用于结束顺序表


//销毁
void Desstroy(SeList* SL) 
{
  free(SL->Sel);
  SL->Sel = NULL;
  SL->count = 0;
  SL->capacity = 0;

}

顺序表的尾部插入

思路:第一步先判断空间是否满了。没满直接在最后面插入,满了就要先扩容,再插入,我们需要注意的是SL->capacity的个数是否为0


//尾插
void SLPushBack(SeList* SL, SLDatatype elemest)
{
  
  //扩大空间
  if (SL->capacity == SL->count)
  {
    //扩2倍
    size_t NewSize = ((SL->capacity * 2) > 0 ? (SL->capacity * 2) : 4);
    SLDatatype  * mdt = realloc(SL->Sel, sizeof(SLDatatype) * NewSize);
    if (mdt != NULL)
    {
      SL->Sel = mdt;
      SL->capacity = SL->capacity * 2;
    }
    else
    {
      perror("SLPushBack _ realloc");
      return;
    }
  }
  SL->Sel[SL->count] = elemest;
  SL->count++;
  

}

这里我们只要的知识还是顺序表,这个过程我们要使用到realloc函数,这个函数主要的作用是扩大动态开辟的空间,扩大的方式有两种,一种是原地扩容(效率高),一种是异地扩容(效率低,会找到一块符合条件的空间开辟,然后把原来的数据拷贝过来,free掉原来的地址)realloc是可以对NULL进行扩容相当于malloc

代码如下:

#include<stdio.h>
int main()
{

  int* arr = (int*)malloc(100);
  if (arr == NULL)
  {
    perror("malloc");
    return 1;
  }
  int* tmp = realloc(arr, sizeof(int) * 1000);
  if (tmp != NULL)
  {
    arr = tmp;
  }
  else
  {
    perror("realloc");
  }
  return 0;
}

这里就可以简单的判断出是原地扩容还是异地扩容了,


顺序表的头部插入

思路:第一步先判断空间是否满了。没满直接在前面插入,满了就要先扩容,再插入,我们需要注意的是SL->capacity的个数是否为0

//扩大空间
void capacityadd(SeList* SL)
{
  //扩大空间
  if (SL->capacity == SL->count)
  {
    //扩2倍
    size_t NewSize = ((SL->capacity * 2) > 0 ? (SL->capacity * 2) : 4);
    SLDatatype* mdt = realloc(SL->Sel, sizeof(SLDatatype) * NewSize);
    if (mdt != NULL)
    {
      SL->Sel = mdt;
      SL->capacity = SL->capacity * 2;
    }
    else
    {
      perror("SLPushBack _ realloc");
      return;
    }
  }
}
// 头插
void SLPushFront(SeList* SL, SLDatatype elemest)
{
  capacityadd(SL);
  // 往后移动
  
  for (int i = SL->count; i > 0; i--)
  {
    SL->Sel[i] = SL->Sel[i - 1];
  }
  //插入
  SL->Sel[0] = elemest;
  SL->count++;

}

在这里的时间复杂度是O(n),但是我们如果插入n个数据,时间复杂度就是O(n^2),插入的数量越多时间复杂度就越高


顺序表尾部删除

思路:要判断顺序表的存储的数据是否为0,不判断可能SL->capacity为负数,后面插入数据就有可能会越界访问


//尾删
void SLPopBack(SeList* SL) 
{
  if (SL->count)
  {
    SL->count--;
  }
  else
  {
    return;
  }

}

这里我们不需要把空间删除我们只需把SL->capacity减1,就可以下次尾插或者头插就覆盖掉这个数据

我们还可以使用assert函数来判断


顺序表的头部删除

思路:就是我们只需要把后面的往前覆盖就行,我们还要判断是否是SL->count为0


// 头删
void SLPopFront(SeList* SL)
{
  assert(SL && SL->count);
  int idx = 0;
  while (idx < SL->count - 1)
  {
    SL->Sel[idx] = SL->Sel[idx + 1];
    idx++;
  }
  SL->count--;


}

顺序表的随机插入

这里插入我们要注意顺序表的空间是连续的,存储数据也是连续的,不可以跳过一两个空间存储数据

只能紧挨这其他数据


05eb94cf57ff78ffd9468c981ba233fc_e5ec3831903c485e80d049f6dbc5842e.png

/随机插入
void SLInsert(SeList* SL,int pos, SLDatatype elemest)
{
  assert(SL && pos >= 0 && pos <= SL->count);
  //判断容量是否正常
  capacityadd(SL);
  //往后移
  int i = SL->count - 1;
  while (i >= pos)
  {
    SL->Sel[i + 1] = SL->Sel[i];
  }
  SL->Sel[pos] = elemest;
  SL->count++;
  
}

顺序表的随机删除

和上面的思路是一样的


//随机删除
void SLErase(SeList* SL, int pos, SLDatatype elemest)
{
  assert(SL && pos >= 0 && pos < SL->count);
  //覆盖
  int i = pos;
  while (i < SL->count - 1)
  {
    SL->Sel[i] = SL->Sel[i + 1];
    i++;
  }
  SL->count--;

}

顺序表的查找

//查找
int SLFind(SeList* SL, SLDatatype elemest)
{
  assert(SL);
  int i = 0;
  for (i = 0; i < SL->count; i++)
  {
    if (SL->Sel[i] == elemest)
    {
      return i;
    }
  }
  return -1;
}

顺序表的菜单操作

void menu()
{
  printf("****************************\n");
  printf("**** 0.exit   1.pushback ***\n");
  printf("**** 2.pushfront 3.popback**\n");
  printf("**** 4.popfront 5.inser **\n");
  printf("**** 6.ereat   7.print    **\n");
  printf("****        8.find        **\n");
  printf("***************************/\n");

}
int main()
{
  /*Test();*/
  int input = 0;
  printf("是否开始:1/0:");
  scanf("%d", &input);
  SeList S1;
  SLinit(&S1);
  do
  {
    menu();
    int select = 0;
    printf("请选择你的决定:>");
    scanf("%d", &select);
    if (select == 0)
    {
      printf("退出程序\n");
      break;


    }
    
    else if (select == 1)
    {
      int elemest = 0;
      printf("请输入你要尾插数据个数和数据:>");
      scanf("%d", &elemest);
      while (elemest)
      {
        int data = 0;
        scanf("%d", &data);
        SLPushBack(&S1, data);
        elemest--;
      }
    }
    else if (select == 2)
    {
      int elemest = 0;
      printf("请输入你要头插数据个数和数据:>");
      scanf("%d", &elemest);
      while (elemest)
      {
        int data = 0;
        scanf("%d", &data);
        
        SLPushFront(&S1, data);
        elemest--;
      }
    }
    else if (select == 3)
    {
      int elemest = 0;
      printf("请输入你要尾部删除数据个数:>");
      scanf("%d", &elemest);
      while (elemest)
      {
        SLPopBack(&S1);
        elemest--;
      }
    }
    else if (select == 4)
    {
      int elemest = 0;
      printf("请输入你要头部删除数据个数:>");
      scanf("%d", &elemest);
      while (elemest)
      {
        SLPopFront(&S1);
        elemest--;
      }
    }
    else if (select == 5)
    {
      int position = 0;
      int elemest = 0;
      printf("请输入你要插入的位置和数据:>");
      scanf("%d %d", &position, &elemest);
        SLInsert(&S1, position, elemest);
    }
    else if (select == 6)
    {
      int position = 0;
      printf("请输入你要删除的位置:>");
      scanf("%d", &position);
      SLErase(&S1, position);
      continue;
    
    }
    else if (select == 7)
    {
      SLPrint(&S1);
    }
    else if (select == 8)
    {
      int position = 0;
      printf("请输入你要查找的数据:>");
      scanf("%d", &position);
      int a = SLFind(&S1, position);
      if (a == -1)
      {
        printf("找不到\n");
      }
      else 
      {
        printf("下标为%d\n", a);
      }
      continue;
    }
    else
    {
      printf("输入有误,请重新输入\n");
    }
  } while (input);
  SLDesstroy(&S1);

  

  return 0;
}

总结

这里主要介绍了顺序表,有不懂的小可爱可以私聊我

相关文章
|
2天前
|
存储
数据结构第二课 -----线性表之单向链表
数据结构第二课 -----线性表之单向链表
|
19小时前
|
C语言
顺序表的实现(迈入数据结构的大门)(2)
顺序表的实现(迈入数据结构的大门)(2)
6 0
|
20小时前
顺序表的实现(迈入数据结构的大门)(1)
顺序表的实现(迈入数据结构的大门)(1)
7 0
|
2天前
|
C++
数据结构(顺序表 动态定义
数据结构(顺序表 动态定义
12 2
|
2天前
|
C++
数据结构(顺序表
数据结构(顺序表
11 1
|
2天前
|
存储
【栈】基于顺序表的栈功能实现
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
13 0
|
2天前
|
存储 编译器
【数据结构】~顺序表
【数据结构】~顺序表
|
2天前
|
存储
数据结构第五课 -----线性表之树
数据结构第五课 -----线性表之树
|
2天前
数据结构第四课 -----线性表之队列
数据结构第四课 -----线性表之队列
|
2天前
数据结构第四课 -----线性表之栈
数据结构第四课 -----线性表之栈