<数据结构>NO2.顺序表

本文介绍了顺序表的概念,它是一种以数组形式在内存中存储线性表的数据结构。顺序表分为静态和动态两种,文中主要讨论动态顺序表的实现,包括初始化、扩容、打印、插入、删除、查找等基本功能。在扩容时,顺序表将容量扩大一倍以减少频繁操作。文章还提到了顺序表的缺点,如中间插入删除的时间复杂度高以及可能的空间浪费。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

🌟1. 顺序表概念

顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。

将数据一个一个连续的存放在数组中,这种存储结构是顺序结构,采用顺序结构的线性表简称为顺序表
顺序表一般可以分为

  1. 静态顺序表 :使用定长数组存储数据
    在这里插入图片描述
  2. 动态顺序表(本章实现):动态开辟内存来存放数据
    在这里插入图片描述

🌟2. 框架

较大程序分3个及以上文件来写,这里分三个文件

  • SeqList.c
  • SeqList.h
  • Text.c

SeqList.c文件用来实现与顺序表有关的函数
SeqList.h文件用来声明函数和结构体
Text.c文件用来测试代码是否正确

3. 基本功能

顺序表需要实现的基本功能有

  • 头删
  • 尾删
  • 头插
  • 尾插
  • 随机删
  • 随机插
  • 查找
  • 打印
  1. 在正式实现顺序表功能之前,我们先对顺序表执行初始化,将顺序表的初始容量置为0,初始化函数SeqListInit
  2. 当我们每次插入数据时,需要检查Capacity是否满了,若满了,则需要扩容,扩容函数SeqListCheckCapacity

3.1 头文件

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLtype;
typedef struct
{
	SLtype* a;	//a是动态开辟的数组
	size_t size;	//有效数据的个数
	size_t capcity;//动态开辟数组的容量
}SeqList;

void SeqListInit(SeqList* ps);
//扩容
void SeqListCheckCapacity(SeqList* ps);
//打印
void SeqListPrint(SeqList* ps);
//尾插
void SeqListPushBack(SeqList* ps, SLtype pos);
//尾删
void SeqListPopBack(SeqList* ps);
//头插
void SeqListPushFront(SeqList* ps, SLtype pos);
//头删
void SeqListPopFront(SeqList* ps);
//随机插入
void SeqListInsert(SeqList* ps, int pos, SLtype data);
//随机删除
void SeqListDelecate(SeqList* ps, int pos);

以上函数的形参均是SeqList*类型的,因为我是将上述函数放在测试函数中进行测试,因此需要指针作为形参,如果形参是SeqList类型,则形参的改变不会影响实参,即顺序表的数据不会改变

⭐️3.2 初始化

初始化函数形参定义为顺序表类型的指针SeqList* ps

为了让顺序表类型SL变量有意义,先将SL的a成员赋值为NULLcapacity size成员赋值为0

void SeqListInit(SeqList* ps)
{
	ps->a = NULL;
	ps->capcity = ps->size = 0;

⭐️3.3 扩容

当容量满了后,顺序表选择将容量扩成原来最大容量的2倍,这样既不会浪费太多空间,也不会扩容太频繁,事实上,随着扩容的次数逐渐增多,一次扩容所产生的空间逐渐增加
扩容需要使用realloc函数

在这里插入图片描述

注:当memblock是NULL时,realloc会进行malloc的操作

void SeqListCheckcapacity(SeqList* ps)
{
	assert(ps);
	if (ps->size == ps->capacity) //有效数据的个数和最大容量相等时,需要扩容
	{
		ps->capacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//一开始将最大容量设置为4,后面扩成2倍
		ps->a = (SeqListDataType*)realloc(ps->a, sizeof(SeqListDataType) *  ps->capacity);
		assert(ps);
	}
}

⭐️3.4 打印

注意打印的个数是有效数据的个数,而不是最大容量

void SeqListPrint(SeqList* ps)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++) //打印有效数据
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

⭐️3.5 尾插

因为size标记的就是顺序表的尾部,所以尾插较简单,只需要将a[size]赋值为插入的元素
在这里插入图片描述

void SeqListPushBack(SeqList* ps, int pos)
{
	assert(ps);
	//当顺序表的容量满了
	SeqListCheckcapacity(ps);
	ps->a[ps->size] = pos;
	ps->size++;//有效数据+1
}

⭐️3.6 头插

头插需要将所有的数据从前往后移动,并且保证挪动方向是[size-1]->[size],[size-2]->[size-1]……

void SeqListPushFront(SeqList* ps, int pos)
{
	assert(ps);
	SeqListCheckcapacity(ps);
	size_t cnt = 0;
	while (cnt < ps->size)//有多少个有效数据,移动多少次
	{
		ps->a[ps->size - cnt] = ps->a[ps->size - 1 - cnt];
		cnt++;
	}
	ps->a[0] = pos;
	ps->size++;
}

⭐️3.7 尾删

尾删很简单,只需要将有效数据的个数-1即可,即使该数据在动态开辟的数组中,但是不在有效数据范围内最后也不会打印

void SeqListPopBack(SeqList* ps)
{
	assert(ps);
	assert(ps->size >= 0);//注意当有效数据为0时不能够删数据
	ps->size--;
}

⭐️3.8 头删

头删和头差类似,都需要挪动其他的有效数据,头删时挪动方向是从后往前挪,并且保证最开始是[1]->[0],[2]->[1]……

void SeqListPopFront(SeqList* ps)
{
	assert(ps);
	assert(ps->size >= 0);
	size_t begin = 1;
	while (begin <= ps->size - 1)//size个有效数据移动size-1次
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}

⭐️3.9 指定插入

有效数据元素为4时,插入到下标pos为2的位置,需要挪动数据3次
在这里插入图片描述
size越大,挪动的次数越多,pos越小,挪动的次数越多,因此在有效数据为size的数组中,将数据差到pos位置,需要挪动数据size-pos+1次,挪动方向[size-1]->[size],[size-2]->[size-1]……

void SeqListInsert(SeqList* ps, int pos, SeqListDataType data)
{
	assert(ps);
	assert(pos >= 1 && pos <= ps->capacity);//判断插入的位置是否合法
	SeqListCheckcapacity(ps);
	size_t cnt = 0;
	//一共循环size+1-pos次
	while (cnt < ps->size - pos + 1)
	{
		ps->a[ps->size - cnt] = ps->a[ps->size - 1 - cnt];
		cnt++;
	}
	ps->a[pos - 1] = data;
	ps->size++;//有效数据+1
}

⭐️3.10 指定删除

4个有效数据删除位置为2的元素需要挪动数据2次
在这里插入图片描述
推测size个有效数据删除位置为pos的元素需要挪动数据size-pos次,挪动方向[pos]->[pos-1],[pos+1]->[pos]……

void SeqListDelecate(SeqList* ps, int pos)
{
	assert(ps && ps->size);
	assert(pos >= 0 && pos <= ps->size);
	//一共循环size-pos次
	while (pos < ps->size)
	{
		ps->a[pos - 1] = ps->a[pos];
		pos++;
	}
	ps->size--;
}

⭐️3.11 查找

查找很简单,直接遍历

void SeqListFind(SeqList* ps, int key)
{
	for (size_t i = 0; i < ps->size; i++)
	{
		if (key == ps->a[i])
		{
			printf("The pos is %d\n", i + 1);
			return;
		}
	}
	printf("Fail Find!\n");
}

🌟3.12 注意事项

  1. 凡是插入时,需要使用assert来判断插入位置是否合法
  2. 插入数据时,有效数据的个数需要+1,删除数据时,有效数据的个数需要-1

4. 顺序表的缺点

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

针对顺序表的缺陷,我们研究出了链表,链表可以弥补顺序表得缺点,我们下次内容来研究如何实现链表

好的,我可以帮你完成这个任务。首先,你需要在 Vue 实例中定义几个数据属性,分别表示第一个输入框的值、第二个输入框的值、运算符和计算结果: ```javascript new Vue({ el: '#app', data: { no1: '', no2: '', operator: '+', result: '' }, methods: { calc: function() { // 计算逻辑 } } }) ``` 然后,在 HTML 中使用 `v-model` 指令将这些数据属性与输入框绑定起来: ```html <input type="text" class="no1" v-model="no1"> <select id="ysf" v-model="operator"> <option>+</option> <option>-</option> <option>*</option> <option>/</option> <option>%</option> </select> <input type="text" class="no2" v-model="no2"> = <input type="text" class="jg" v-model="result"> <input type="button" value="计算" @click="calc"> ``` 最后,在 `calc` 方法中编写计算逻辑,并将结果赋值给 `result` 数据属性: ```javascript calc: function() { var num1 = parseFloat(this.no1); var num2 = parseFloat(this.no2); if (isNaN(num1) || isNaN(num2)) { this.result = ''; return; } switch (this.operator) { case '+': this.result = num1 + num2; break; case '-': this.result = num1 - num2; break; case '*': this.result = num1 * num2; break; case '/': if (num2 === 0) { this.result = '除数不能为0'; } else { this.result = num1 / num2; } break; case '%': this.result = num1 % num2; break; } } ``` 这样就完成了双向绑定和计算功能。完整的代码如下: ```html <div id="app"> <input type="text" class="no1" v-model="no1"> <select id="ysf" v-model="operator"> <option>+</option> <option>-</option> <option>*</option> <option>/</option> <option>%</option> </select> <input type="text" class="no2" v-model="no2"> = <input type="text" class="jg" v-model="result"> <input type="button" value="计算" @click="calc"> </div> <script> new Vue({ el: '#app', data: { no1: '', no2: '', operator: '+', result: '' }, methods: { calc: function() { var num1 = parseFloat(this.no1); var num2 = parseFloat(this.no2); if (isNaN(num1) || isNaN(num2)) { this.result = ''; return; } switch (this.operator) { case '+': this.result = num1 + num2; break; case '-': this.result = num1 - num2; break; case '*': this.result = num1 * num2; break; case '/': if (num2 === 0) { this.result = '除数不能为0'; } else { this.result = num1 / num2; } break; case '%': this.result = num1 % num2; break; } } } }) </script> ```
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值