数据结构——顺序表

目录

 

1. 引言

2.  定义顺序表的结构体

 3. 顺序表的初始化

4. 打印顺序表

5. 顺序表头尾位置的插入和删除

  5.0 关于扩容函数

  5.1 顺序表的尾插

5.2 顺序表的头插

5.3 顺序表的尾删

5.4 顺序表的头删

6. 顺序表在任意位置的插入和删除

  6.1 顺序表在任意位置的插入

6.2 顺序表在任意位置的删除

7. 顺序表的查找

8. 顺序表的释放

9.  顺序表的优缺点

10.  结语


1. 引言

        数据结构是计算机科学中的重要基础,而顺序表作为一种简单而常用的数据结构,为我们解决实际问题提供了便利。顺序表将数据元素按照逻辑顺序依次存储在一块连续的存储空间中,具有存储空间利用率高、元素访问速度快和支持随机访问等优势。在实际应用中,顺序表可以用来解决各种问题,如图书馆管理系统和学生成绩管理系统

        然而,顺序表的插入和删除操作较慢,并且大小固定,需要进行扩容。综合考虑其优势和限制,我们可以合理运用顺序表来组织和管理数据,提高工作效率。在接下来的博客中,我们将深入探讨顺序表的实现原理、应用场景以及优化方法,帮助读者更好地理解和应用这一重要的数据结构。


2.  定义顺序表的结构体

此片文章讨论的是动态的顺序表,因此,在定义的时候此顺序表中会由三个变量:

  1. 第一个是一个任意元素类型的指针;
  2. 第二个是记录此顺序表的容量;
  3. 第三个是记录当前顺序表中含有几个元素。

代码示例如下:

typedef int SLDataType;
typedef struct SeqList
{
	SLDataType* a; //任意元素类型(此处以int为例)
	int size;  // 当前顺序表有多少元素
	int capacity; //顺序表的总容量
}SL;

 3. 顺序表的初始化

    使用顺序表,首先就是要给顺序表初始化,初始化的时候需要给:

  1. 当前顺序表的容量随意给个值;
  2. 上述定义的元素类型动态开辟一个空间(用malloc或者是realloc函数);
  3. 其次就是把 0 赋给 size。

代码如下:

void InitSeqList(SL* ps)
{
	assert(ps);
    ps->capacity = 4;
	SLDataType* tmp = (SLDataType*)malloc(capacity * sizeof(SLDataType));
	if (tmp != NULL)
	{
		ps->a = tmp;
	}
	else
	{
		perror("InitSeqList -> malloc:");
		return;
	}
	
	ps->size = 0;
}

4. 打印顺序表

顺序表的打印思路较为简单,直接遍历以下就好,直接上代码:

void SLPrint(SL* ps)
{
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

5. 顺序表头尾位置的插入和删除

  5.0 关于扩容函数

当顺序表需要插入元素的时候,都要首先判断一下顺序表的空间是否满了,即:

ps -> size == ps -> capacity

如果满了就需要扩容,那么如何扩容呢? 思路如下:

  1. 首先判断上述代码是否为真;
  2. 之后再让新的容两变为原来的两倍;
  3. 其次再使用realloc函数增加空间;
  4. 判断realloc函数返回值;

具体代码如下:

void CheckCapacity(SL* ps)
{
	if (ps->capacity == ps->size)
	{
		int newcapacity = 2 * (ps->capacity);
		SLDataType* tmp =
			(SLDataType*)realloc(ps->a, sizeof(SLDataType) * newcapacity);
		if (tmp != NULL)
		{
            ps->capacity = newcapacity;
			ps->a = tmp;
		}
		else
		{
			perror("CheckCapacity -> realloc:");
			return;
		}
		
	}

}

  5.1 顺序表的尾插

顺序表的尾插步骤如下:

  1. 首先需要判断当前顺序表元素是否满了,需要调用上方的CheckCapacity函数;
  2. 之后就可与直接在size的位置插入数据,size继续加1即可;

代码如下:

void test2()
{
	SL sl;
	InitSeqList(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPushBack(&sl, 4);
	SLPushBack(&sl, 5);
	SLPushBack(&sl, 6);
	SLPushBack(&sl, 7);
	SLPrint(&sl);
}

void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);
	CheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}

运行结果如下:

5.2 顺序表的头插

 顺序表头插步骤如下:

  1. 首先还是要判断顺序表元素是否满了,是否要进行扩容;
  2. 其次是要将所有元素向后移动一个位置;
  3. 最后再首元素位置插入一个元素。

代码如下:

void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	CheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}



void test3()
{
	SL sl;
	InitSeqList(&sl);
	SLPushFront(&sl, 1);
	SLPushFront(&sl, 2);
	SLPushFront(&sl, 3);
	SLPushFront(&sl, 4);
	SLPushFront(&sl, 5);
	SLPushFront(&sl, 6);
	SLPushFront(&sl, 7);
	SLPrint(&sl);
}

代码解释如下图:

运行结果如下:

5.3 顺序表的尾删

顺序表尾删步骤如下:

  1. 首先要判断此时顺序表的元素是否为空;
  2. 之后直接让ps -> size 减1 就好;

代码实现如下:

void SLPopBack(SL* ps)
{
	assert(ps);
	assert(ps->size > 0);
	ps->size--;
}


void test3()
{
	SL sl;
	InitSeqList(&sl);
	SLPushFront(&sl, 1);
	SLPushFront(&sl, 2);
	SLPushFront(&sl, 3);
	SLPushFront(&sl, 4);
	SLPushFront(&sl, 5);
	SLPushFront(&sl, 6);
	SLPushFront(&sl, 7);
	SLPrint(&sl);
    SLPopBack(&sl);
	SLPrint(&sl);
}

 运行结果如下:

5.4 顺序表的头删

顺序表的头删步骤如下:

  1.  判断顺序表是否为空;
  2. 将所有的元素向前移动一位;
  3. 让ps->size 减1。

代码实现如下:

void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size > 0);
	int begin = 0;
	while (begin < ps->size - 1)
	{
		ps->a[begin] = ps->a[begin + 1];
		begin++;
	}
	ps->size--;
}

void test3()
{
	SL sl;
	InitSeqList(&sl);
	SLPushFront(&sl, 1);
	SLPushFront(&sl, 2);
	SLPushFront(&sl, 3);
	SLPushFront(&sl, 4);
	SLPushFront(&sl, 5);
	SLPushFront(&sl, 6);
	SLPushFront(&sl, 7);
	SLPrint(&sl);
	SLPopFront(&sl);
	SLPrint(&sl);
}

代码解释如下图:

代码运行结果如下:


6. 顺序表在任意位置的插入和删除

  6.1 顺序表在任意位置的插入

顺序表在任意位置插入步骤如下:

  1.    首先要判断这个位置是否满足:  assert(pos >= 0 && pos <= (ps -> size)
  2.    其次,要将此位置之后的元素全部向后移动一个
  3.    最后,在此位置插入元素,并将size + 1。

代码如下:

void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;
}


void test4()
{
	SL sl;
	InitSeqList(&sl);
	SLInsert(&sl, 0, 1);
	SLInsert(&sl, 0, 2);
	SLPrint(&sl);
}

代码终止条件解释图如下:

代码运行结果如下:

6.2 顺序表在任意位置的删除

顺序表在任意位置的删除步骤如下:

  1. 首先要判断此时顺序表中是否有元素;
  2. 其次要判断删除的位置是否满足:pos >= 0 && pos < ps -> size;
  3. 最后要将此位置之后的所有元素向前移动一个位置,将size--;

具体代码如下:

void SLEras(SL* ps, int pos)
{
	assert(ps);
	assert(ps->size > 0);
	assert(pos >= 0 && pos < ps->size);
	int begin = pos;
	while (begin < ps->size)
	{
		ps->a[begin] = ps->a[begin + 1];
		begin++;
	}
	ps->size--;
}
void test4()
{
	SL sl;
	InitSeqList(&sl);
	SLInsert(&sl, 0, 1);
	SLInsert(&sl, 0, 2);
	SLPrint(&sl);
	SLEras(&sl, 0);
	SLPrint(&sl);
}

代码终止条件图如下:

代码运行结果如下:


7. 顺序表的查找

顺序表的查找直接遍历就好,如果有就返回下标,没有就返回-1。

具体代码如下:

int SLFind(SL* ps, SLDataType x)
{
	assert(ps);
	assert(ps->size > 0);
	int i = 0;
	while (i < ps->size)
	{
		if (ps->a[i] == x)
		{
			return i;
		}
		i++;
	}
	return -1;
}

void test4()
{
	SL sl;
	InitSeqList(&sl);
	SLInsert(&sl, 0, 1);
	SLInsert(&sl, 0, 2);
	SLPrint(&sl);
	SLEras(&sl, 0);
	SLPrint(&sl);
	int ret =SLFind(&sl, 1);
	printf("%d", ret);
}

 代码运行结果如下:


8. 顺序表的释放

顺序表的内存是动态开辟的,因此需要释放,具体代码如下:

void SLDestroy(SL* ps)
{
	assert(ps);
	if (ps->a != NULL)
	{
		free(ps->a);
		ps->a = NULL;
	}
	ps->capacity = 0;
	ps->size = 0;
}

9.  顺序表的优缺点

顺序表作为一种常用的数据结构,具有以下优点:

  1. 存储空间利用率高:顺序表将数据元素按照逻辑顺序连续存储在一块内存中,不需要额外的指针来连接各个元素,因此存储空间利用率高。

  2. 元素访问速度快:由于顺序表中的元素在内存中是连续存放的,可以通过下标快速定位到对应的数据,因此元素的访问速度很快。

  3. 支持随机访问:由于顺序表支持通过下标直接访问元素,因此可以随机访问任意位置的元素,而不需要从头开始遍历。

然而,顺序表也存在一些缺点:

  1. 插入和删除操作较慢:当需要在顺序表中插入或删除元素时,需要进行数据的搬移操作,时间复杂度较高。

  2. 大小固定,需要扩容:顺序表的大小是固定的,一旦存储空间不够,就需要进行扩容操作,这会带来一定的开销。

综上所述,顺序表作为一种简单而高效的数据结构,在实际应用中具有广泛的应用。但在使用时需要注意其插入和删除操作的效率,并合理规划存储空间大小,以充分发挥其优势和避免其限制。


10.  结语

    顺序表的存储空间利用率高、元素访问速度快以及支持随机访问,使其在实际应用中具有重要的作用。然而,顺序表的插入和删除操作较慢,并且大小固定,需要进行扩容。在实际应用中,我们需要根据具体情况来选择合适的数据结构,以提高工作效率。

  • 13
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值