数据结构C语言版 —— 顺序表增删改查实现


顺序表

1. 线性表

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

数据结构中包括逻辑结构物理结构两个层次

  • 逻辑结构

    数据的逻辑结构是从逻辑关系上描述数据,它与数据的存储无关,是独立与计算机的。所以,数据的逻辑结构可以看作是从具体问题抽象出来的数学模型。

    数据的逻辑结构有两个要素:一是数据元素,二是关系。数据元素的含义如前所述,关系是指数据元素之间的逻辑关系。它根据元素之间关系的不同特性,通常有4类基本结构。

在这里插入图片描述

  • 集合结构

    数据元素之间除了属于同一个集合的关系外,没有其它关系。

  • 线性结构

    数据元素之间存在这一对一的关系,比如将一些学生的信息按照它的学号按照先后顺序排序,组成一个线性结构

  • 树结构

    数据元素之间存在着一对多的关系,比如在班级管理体系中,班长管理多个组长,每个组长管理多名组员

  • 图结构

    数据元素之间存在着多对多的关系,比如多位同学之间的朋友关系,任何两个同学都可以是朋友,从何构成图结构。

在这里插入图片描述

  • 物理结构

    数据对象在计算机中的存储表示称为存储结构,也称为物理结构。把数据对象存储到计算机时,通常要求既要存储数据元素的数据,又要存储数据元素之间的逻辑关系,数据元素在计算机内用一个节点来表示。

    物理结构又可以分为顺序存储和链式存储。顺序表和链表就是典型代表。

2. 顺序表

3. 顺序表基本概念

顺序表使用一段连续物理地址的存储单元依次存储数据元素的一种线性结构,简单来说就是一个数组,一个可以动态增长的数组,并且要求里面存储的数据必须是从左往右是连续的。然后在这个数组上完成增删改查。顺序表的特点是逻辑上相邻的数据元素,物理上也是相邻的

顺序表的缺陷

  1. 动态扩容有性能消耗
  2. 如果在头部插入数据,需要挪动元素

顺序表的存储结构

typedef int SeqDataType;
typedef struct SeqList 
{
	SeqDataType* arr;//指向动态开辟数组首元素
	size_t size;//存放元素个数
	size_t capacity;//容量
}SeqList;

在这里插入图片描述

4. 顺序表实现

静态的顺序表只适合用于知道需要存储多少数据的场景,如果静态的顺序表定义的数组太大就会出现浪费,而定义的太小就会不够用。所以使用动态的顺序表会更加合理,通过动态分配空间来实现顺序表。

typedef int SeqDataType;
typedef struct SeqList 
{
	SeqDataType* arr;
	size_t size;//存放元素个数
	size_t capacity;//容量
}SeqList;

//初始化顺序表
void SeqListInit(SeqList* pq);
//销毁顺序表
void SeqListDestory(SeqList* pq);
//打印顺序表元素
void SeqListPrint(SeqList* pq);
//判断顺序表是否为空
int SeqListEmpty(SeqList* pq);
//获取顺序表元素个数
size_t SeqListSize(SeqList* pq);
//判断扩容顺序表
int CheckCapacity(SeqList* pq);
//尾插
void SeqListPushBack(SeqList* pq, SeqDataType data);
//头插
void SeqListPushFront(SeqList* pq, SeqDataType data);
//删除末尾元素
void SeqListPopBack(SeqList* pq);
//删除头部元素
void SeqListPopFront(SeqList* pq);
//在顺序表中查找元素
SeqDataType SeqListFind(SeqList* pq, SeqDataType data);
//获取指定位置的数据元素
SeqDataType SeqListGet(SeqList* pq, size_t pos);
//在顺序表pos位置插入数据
void SeqListInsert(SeqList* pq, size_t pos, SeqDataType data);
//删除顺序表中pos位置的元素
void SeqListErase(SeqList* pq, size_t pos);
//修该顺序表pos位置的元素
void SeqListModify(SeqList* pq, size_t pos, SeqDataType target);

这里我只实现比价关键的接口

顺序表初始化

顺序表初始化比较简单,只需要通过malloc开辟一块合适的空间就好.

  1. 判断结构体是否为空
  2. 初始化容量为10
  3. 使用malloc函数开辟空间
//初始化顺序表
void SeqListInit(SeqList* pq)
{
	assert(pq);
	pq->capacity = 10;
	pq->arr = (SeqDataType*)malloc(sizeof(int) * pq->capacity);
	if (pq->arr == NULL)
	{
		printf("初始化失败\n");
		exit(1);
	}
	memset(pq->arr, 0, sizeof(int) * pq->capacity);//给顺序表初始化
	pq->size = 0;
}

顺序表的扩容

如果在插入数据元素的时候发现满了就要进行扩容,通过realloc来对数组进行扩容,需要进行扩容的数组进行判空。

  1. 如果当前元素个数等于数组容量就进行扩容
  2. 使用realloc对数组进行二倍扩容
  3. 如果扩容失败,则什么都不做
  4. 扩容成功就把结构体数组指向扩容空间的首地址
//判断扩容顺序表
int CheckCapacity(SeqList* pq)
{
	if (pq->size == pq->capacity)
	{
		//二倍扩容
		pq->capacity = pq->capacity * 2;
		SeqDataType* ptr = (SeqDataType*)(realloc(pq->arr, sizeof(int) * pq->capacity));
		if (ptr == NULL)
		{
			printf("扩容失败\n");
			pq->capacity = pq->capacity / 2;
			return 1;
		}
		pq->arr = ptr;
		printf("扩容成功\n");
	}

	return 0;
}

顺序表的插入

顺序表元素插入分为3种,从头部插入、从末尾插入、从指定位置插入。其实只要实现了从指定位置插入,头插和尾插都可以调用指定位置插入的接口。指定位置插入思路。

  1. 首先得判NULL和是否越界
  2. 判断是否需要扩容
  3. 从最后一个元素位置开始,将元素从前往后移动
//在顺序表pos位置插入数据
void SeqListInsert(SeqList* pq, size_t pos, SeqDataType data)
{
	assert(pq);
	assert(pos <= pq->size);//越界判断
	CheckCapacity(pq);
	size_t index = pq->size;
	while (index > pos)
	{
		pq->arr[index] = pq->arr[index - 1];
		index--;
	}
	pq->arr[pos] = data;
	pq->size++;

}

比如要在下标为2的位置插入元素

在这里插入图片描述

实现了指定位置插入之后,头插和尾插就比较简单了

头插

//头插
void SeqListPushFront(SeqList* pq, SeqDataType data)
{
	assert(pq);
	
	SeqListInsert(pq, 0, data);
}

尾插

//尾插
void SeqListPushBack(SeqList* pq, SeqDataType data)
{
	assert(pq);

	SeqListInsert(pq, pq->size, data);
}

顺序表插入元素的时间复杂度是 O ( N ) O(N) O(N)

顺序表的删除

顺序表的删除分为头删、尾删和指定位置删除,和插入类似,只需要从后往前挪动元素将要删除的元素覆盖即可。

  1. 判NULL和是否越界
  2. 将pos位置的元素全部往前挪动
  3. 元素个数减1
//删除顺序表中pos位置的元素
void SeqListErase(SeqList* pq, size_t pos)
{
	assert(pq);
	assert(pos < pq->size);
	size_t index = pos;
	while (index < pq->size - 1)
	{
		pq->arr[index] = pq->arr[index + 1];
		index++;
	}
	pq->size--;
}

在这里插入图片描述

完成了指定位置删除就可以,调用它完成头删和尾删了

头删

//删除头部元素
void SeqListPopFront(SeqList* pq)
{
	assert(pq);

	SeqListErase(pq, 0);
}

尾删

//删除末尾元素
void SeqListPopBack(SeqList* pq)
{
	assert(pq);
	assert(pq->size != 0);
	
	SeqListErase(pq, pq->size-1);
}

顺序表删除的时间复杂为 O ( N ) O(N) O(N)

顺序表的查找

顺序表的查找分两种,一种是查找指定数据元素返回下标,另外一种是直接获取某个下标的元素

查找元素

//在顺序表中查找元素
SeqDataType SeqListFind(SeqList* pq, SeqDataType data)
{
	assert(pq);
	size_t i = 0;
	for (i = 0; i < pq->size; i++)
	{
		if (pq->arr[i] == data)
		{
			//返回第一个找到的元素的下标
			return i;
		}
	}

	return -1;
}

获取指定位置的元素

//获取指定位置的数据元素
SeqDataType SeqListGet(SeqList* pq, size_t pos)
{
	assert(pq);
	assert(pos < pq->size);

	return pq->arr[pos];
}

顺序表查找的时间复杂度为 O ( N ) O(N) O(N),但如果是给定下标获取元素则是 O ( 1 ) O(1) O(1)

顺序表的修改

顺序表的修改指定下标的元素,也是非常简单的

//修该顺序表pos位置的元素
void SeqListModify(SeqList* pq, size_t pos, SeqDataType target)
{
	assert(pq);
	assert(pos < pq->size);

	pq->arr[pos] = target;
}

顺序表的修改时间复杂度为 O ( N ) O(N) O(N)

顺序表的销毁

顺序表的销毁只需要

  1. 把元素个数容量赋值成0

  2. 将开辟的内存空间释放掉

  3. 再将数组指向NULL

//销毁顺序表
void SeqListDestory(SeqList* pq)
{
	assert(pq);
	pq->size = 0;
	pq->capacity = 0;
	free(pq->arr);
	pq->arr = NULL;
}

5. 顺序表总结

  1. 顺序表的插入和删除时间复杂度为 O ( N ) O(N) O(N)
  2. 顺序表的扩容使用的是realloc,如果后面的空间不够就会重新开辟新的空间,再把数据拷贝到新的空间,这样就会有一定的额性能消耗
  3. 顺序表存在着一定的资源浪费,比如当前顺序表的容量为200,当放满之后再插入10个元素,就会进行2倍扩容到400,此时就浪费了190个数据空间。

我们发现顺序表存在着一定缺陷,如果想不那么浪费空间就想着扩容扩少一点,但扩容小了如果有大量数据插入又会存着不断扩容的情况,那么又会有着性能的消耗。

针对以上问题,就可以引入链表。
下一篇链表博客马上发布。


  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
下面是一个简单的在 C# Winform 窗体中实现顺序表增删改查的实例: 1. 首先,在你的 Winform 窗体中添加四个 TextBox 控件和四个 Button 控件,分别用于输入元素、输入要删除的元素位置、输入要修改的元素位置和输入新值,以及按钮事件。 2. 接下来,在窗体的 Load 事件中,声明一个 List<int> 类型的变量来存储顺序表中的元素: ``` List<int> array = new List<int>(); ``` 3. 实现插入元素的方法。在 button1_Click 事件中,通过 TextBox 控件获取要插入的元素值,并调用 List 类型的 Add 方法将元素插入到顺序表中: ``` private void button1_Click(object sender, EventArgs e) { int value = int.Parse(textBox1.Text); array.Add(value); // 更新 DataGridView 控件中的数据 dataGridView1.DataSource = null; dataGridView1.DataSource = array; } ``` 4. 实现删除元素的方法。在 button2_Click 事件中,通过 TextBox 控件获取要删除的元素位置,然后调用 List 类型的 RemoveAt 方法删除指定位置的元素: ``` private void button2_Click(object sender, EventArgs e) { int index = int.Parse(textBox2.Text); array.RemoveAt(index); // 更新 DataGridView 控件中的数据 dataGridView1.DataSource = null; dataGridView1.DataSource = array; } ``` 5. 实现修改元素的方法。在 button3_Click 事件中,通过 TextBox 控件获取要修改的元素位置和新值,然后调用 List 类型的 indexer 语法修改指定位置的元素: ``` private void button3_Click(object sender, EventArgs e) { int index = int.Parse(textBox3.Text); int value = int.Parse(textBox4.Text); array[index] = value; // 更新 DataGridView 控件中的数据 dataGridView1.DataSource = null; dataGridView1.DataSource = array; } ``` 6. 实现查找元素的方法。在 button4_Click 事件中,通过 TextBox 控件获取要查找的元素值,然后调用 List 类型的 FindIndex 方法查找元素的位置: ``` private void button4_Click(object sender, EventArgs e) { int value = int.Parse(textBox5.Text); int index = array.FindIndex(x => x == value); if(index >= 0) { MessageBox.Show($"元素 {value} 的位置是:{index}"); } else { MessageBox.Show($"元素 {value} 不存在!"); } } ``` 以上就是一个简单的在 C# Winform 窗体中实现顺序表增删改查的实例。你可以将这些方法放在一个类中,并在 Winform 中调用这个类来操作顺序表

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱敲代码的三毛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值