线性表之顺序表(C语言实现)

简介: 线性表之顺序表(C语言实现)

一、线性表


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


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


二、顺序表


概念:


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


顺序表一般分为;两种:1.静态顺序表 2.动态顺序表


静态顺序表实际作用不大,本篇主要讲解动态顺序表.


2.1 静态顺序表简单介绍:



静态顺表是指顺序表的容量是固定的,如果看过c语言实现通讯录的友友们,对于静态顺序表可以轻松拿捏.


2.2 动态顺序表:



三、顺序表的常见操作(接口)


3.1 顺序表的类型声明:


//动态版
typedef int DataType;
#define  MAX 10
typedef struct SQList
{
  DataType* data;//指向一段连续的内存空间
  int size;//代表当前顺序表的长度
  int capacity;//表示最大容量
}SQL;


3.2 顺序表的初始化


动态顺序表虽然容量没有限制,会自动扩容,但是也要设置初始值,当达到初始值的最大限制时,才会去动态扩容.


  SQL SL;//用顺序表类型创建一个SL顺序表
  InitSQL(&SL);


顺序表的初始化是需要修改顺序表中的成员的,所以需要传址调用,否则形参不会影响实参.


void InitSQL(SQL* SL)
{
  assert(SL);//防止传入空指针
  SL->capacity = MAX;
  SL->size = 0;//顺序表初始状态,当前数据量为0
  SL->data = (DataType*)malloc(sizeof(DataType) * MAX);//初始化顺序表大小
  if (SL->data == NULL)
  {
    printf("初始化申请空间失败.\n");
  }
}


3.3 "增容"函数


当顺序表需要增容时,increase函数利用realloc函数对data指针指向的空间,重新分配合适大小.


DataType* increase(SQL* SL)
{
  assert(SL);
  SL->capacity *= 2;//增容扩大为原来的两倍,这里根据情况,可自由选择每次扩容的增量
  DataType* ret=(DataType*)realloc(SL->data,sizeof(DataType) * SL->capacity);//重新申请一段增容后大小的空间.
  assert(ret);//如果增容失败,则报错.
  return ret;
}


3.4 顺序表的"插入"操作:


顺序表的"尾插"


顺序表的尾插很简单,size为当前顺序表的数据个数,将其作为下标,刚好可以指向新的位置(尾部的下一个位置),将新元素插入后,size自增1,可完成顺序表的尾插操作.



尾插:


  1. 插入操作之前都需要先判断顺序表是否已满.


  1. 以size作为下标,将新元素插入进数组.


  1. size++,顺序表长度+1.


注意:


执行插入操作之前,要先判断目前的顺序表是否已经达到最大容量(capacity),如果顺序表已满,则需要调用增容函数(increase),对函数进行增容.


代码:


//顺序表的尾插
void PushBack(SQL* SL, DataType x)
{
  assert(SL);
  if (SL->size == SL->capacity)//
  {
    SL->data = increase(SL);//调用增容函数,进行动态扩容
  }
  SL->data[SL->size] = x;
  SL->size++;
}


顺序表的"头插"


顺序表的头插就显得稍微复杂一些,效率不高,需要移动数据.



头插:


  1. 插入操作之前都需要先判断顺序表是否已满.


  1. 将所有的原有数据向后移动一个位置. (这里只能从最后一个位置开始往后移,如果从前面开始移动数据,会将后面的数据覆盖,导致数据出错.)


  1. 将新数据插入进数组首位置.


  1. size+1,表示顺序表长度+1


//顺序表的头插
void PushFront(SQL* SL, DataType x)
{
  assert(SL);
  //同样在进行插入操作之前,要先判断是否.
  if (SL->size == SL->capacity)
  {
    SL->data = increase(SL);
  }
  for (int i = SL->size; i > 0; i--)//将原来数据往后移
  {
    SL->data[i] = SL->data[i - 1];
  }
  //将新的数据插入到数组首位置
  SL->data[0] = x;
  SL->size++;
}


3.5 顺序表的"判空"


size=0时,表示顺序表中没有元素,即顺序表为空.


顺序表如果为空,则返回"真"


顺序表不为空,则返回"假".


//判断是否为空顺序表
bool Empty(SQL* SL)
{
  assert(SL);
  if (SL->size == 0)
  {
    return true;
  }
  else 
    return false;
}


3.6 顺序表的删除操作


顺序表的"尾删"


顺序表的尾删也是很舒服的.


尾删:


  1. 判空:进行删除元素的操作之前,我们应当先对顺序表进行"判空"操作,如果顺序表为空,则不能删除


  1. .size–,即长度-1.


顺序表的尾删操作没有必要真的将最后一个数据删除,只需要调整size的值,那样我们就不能访问到已经删除的元素,这也就等于删除了.


代码:


void PopBack(SQL* SL)
{
  assert(SL);
  assert(!Empty(SL));
  SL->size--;
}


顺序表的"头删"


  顺序表的头删效率不高,需要将数据从第二个元素开始,往后的所有元素(包括第二个元素)向前移动一个位置.



//顺序表的头删
void PopFront(SQL* SL)
{
  assert(SL);
  assert(!Empty(SL));
  for (int i = 0; i < SL->size-1; i++)//将后面的数据向前覆盖
  {
    SL->data[i] = SL->data[i + 1];
  }
  SL->size--;
}


3.7 顺序表的指定位置"插入"


//函数声明.h
//指定位置插入元素,位置是下标+1
void SLInsert(SQL* SL, int pos, DataType x);


提供给该函数要插入的元素及其要被插入的位置.


步骤:


  1. 判断插入的位置是否合法.


  1. 插入操作之前都需要先判断顺序表是否已满.


  1. 将数据从最后一个元素开始到pos位置结束(包括pos处的元素),向后移动一个元素.


  1. 将数据插入到pos位置处.


  1. size++,顺序表的长度+1


该函数主要注意点有两个:


  1. pos位置的合法判断.


pos的取值范围应该是,[1,size+1].


不理解时可以举个例子:



  1. 需要移动的元素的下标的确定.



理解完这两点,代码就不难写了.


代码:


//指定位置的插入
void SLInsert(SQL* SL, int pos, DataType x)
{
  assert(SL);
  assert(!(pos < 0 || pos > SL->size+1));//这里可以在最后一个位置的后面插入,例如:size=5时,可以在6号位置插入
  //老样子,插入之前先检查顺序表是否已满
  if (SL->size == SL->capacity)
  {
    SL->data = increase(SL);
  }
  int i = 0;
  //移动数据
  for ( i = SL->size; i > pos - 1; i--)
  {
    SL->data[i] = SL->data[i - 1];
  }
  SL->data[pos-1] = x;
  SL->size++;
}


图解:



此时我们不妨可以利用SLInsert函数来简写尾插和头插.


尾插:


之所以是size+1,是因为位置是坐标+1


void PushBack(SQL* SL, DataType x)
{
  SLInsert(SL, SL->size+1, x);
}


头插:


之所以是0+1,也是因为位置是坐标+1


//顺序表的头插
void PushFront(SQL* SL, DataType x)
{
  SLInsert(SL, 0 + 1, x);
}


3.8 顺序表的指定位置"删除"


看到这里,相信对于pos的位置范围应该不难判断了吧.


/指定位置删除
void SLErase(SQL* SL, int pos)
{
  assert(SL);
  assert(!Empty(SL));
  assert(!(pos<0 || pos>SL->size));//删除只能在[1-size]范围
  for (int i = pos - 1; i < SL->size-1; i++)
  {
    SL->data[i] = SL->data[i + 1];
  }
  SL->size--;
}


修改后的头删:


//顺序表的头删
void PopFront(SQL* SL)
{
  SLErase(SL, 0 + 1);
}


修改后的尾删:


void PopBack(SQL* SL)
{
  SLErase(SL, SL->size);
}


3.9 顺序表的"打印"


void PrintSQL(SQL* SL)//最好是传指针,保证接口的统一性,传指针的时候记得断言
{
  if (SL->size == 0)
  {
    printf("该顺序表为空.\n");
    return;
  }
  for (int i = 0; i < SL->size; i++)
  {
    printf("%d ", SL->data[i]);
  }
  printf("\n");
}


3.10 顺序表的"查找"


要查找顺序表中的某个值,只需要遍历这个顺序表,依次比较即可.(如果数据有重复,该函数只返回第一次遇到的目标值)


//查找函数
//查找成功返回元素的下标.
//查找失败,返回-1.(因为下标不可能为负数).
int Find(SQL SL, DataType x)
{
  for (int i = 0; i < SL.size; i++)
  {
    if (SL.data[i] == x)
    {
      return i;
    }
  }
  //遍历完顺序表都没找到时.
  return -1;
}


3.11 顺序表的"销毁"


顺序表的销毁及其简单.


1.将data指针释放并置空.


2.将capacity和size设置为0.


//顺序表的销毁后记得要将顺序表置空,该函数不会置空操作.
void DestorySQL(SQL* SL)
{
  assert(SL);
  free(SL->data);
  SL->data = NULL;
  SL->size = 0;
  SL->capacity = 0;
}
目录
相关文章
|
1月前
|
存储 C语言
【数据结构】顺序表(c语言实现)(附源码)
本文介绍了线性表和顺序表的基本概念及其实现。线性表是一种有限序列,常见的线性表有顺序表、链表、栈、队列等。顺序表是一种基于连续内存地址存储数据的数据结构,其底层逻辑是数组。文章详细讲解了静态顺序表和动态顺序表的区别,并重点介绍了动态顺序表的实现,包括初始化、销毁、打印、增删查改等操作。最后,文章总结了顺序表的时间复杂度和局限性,并预告了后续关于链表的内容。
82 3
|
2月前
|
存储 C语言
探索C语言数据结构:利用顺序表完成通讯录的实现
本文介绍了如何使用C语言中的顺序表数据结构实现一个简单的通讯录,包括初始化、添加、删除、查找和保存联系人信息的操作,以及自定义结构体用于存储联系人详细信息。
38 2
|
2月前
|
C语言
链式顺序表实现(C语言描述)
本文介绍了如何在C语言中实现链式顺序表,包括数据结构的定义、节点的创建、数据的插入和删除以及链表的打印和销毁。
49 2
|
2月前
|
C语言
顺序表数组法构建(C语言描述)
如何使用C语言通过数组方法构建有序顺序表,包括顺序表的创建、插入、删除和打印等。
22 2
|
3月前
|
存储 C语言 C++
数据结构基础详解(C语言) 顺序表:顺序表静态分配和动态分配增删改查基本操作的基本介绍及c语言代码实现
本文介绍了顺序表的定义及其在C/C++中的实现方法。顺序表通过连续存储空间实现线性表,使逻辑上相邻的元素在物理位置上也相邻。文章详细描述了静态分配与动态分配两种方式下的顺序表定义、初始化、插入、删除、查找等基本操作,并提供了具体代码示例。静态分配方式下顺序表的长度固定,而动态分配则可根据需求调整大小。此外,还总结了顺序表的优点,如随机访问效率高、存储密度大,以及缺点,如扩展不便和插入删除操作成本高等特点。
230 5
|
3月前
|
存储 算法 C语言
C语言手撕数据结构代码_顺序表_静态存储_动态存储
本文介绍了基于静态和动态存储的顺序表操作实现,涵盖创建、删除、插入、合并、求交集与差集、逆置及循环移动等常见操作。通过详细的C语言代码示例,展示了如何高效地处理顺序表数据结构的各种问题。
|
6月前
|
存储 C语言
数据结构——顺序表(C语言版)
数据结构——顺序表(C语言版)
71 5
|
C语言
C语言及程序设计提高例程-26 实现线性表基本操作的函数
贺老师教学链接  C语言及程序设计提高 本课讲解 删除指定位置上的数据 #include &lt;stdio.h&gt; #define SIZE 100 int deleteData(int[], int, int); int n=10; //数组中实际有用元素 int main() { int d[SIZE]= {1,3,9,12,32,41,45,62,75,77};
996 0
|
25天前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
49 10
|
25天前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
43 9