动静态顺序表


前言

大家都知道要学好编程,数据结构是十分重要的一门课,而线性表是一种在实际中广泛使用的数据结构,那么线性表是什么呢,线性表是n个具有相同特性(数据类型)的数据元素的有限序列,常见的线性表有:顺序表、链表、栈、队列、字符串…但不要误会的是线性表在逻辑上是线性结构,也就是一条连续的直线,但在物理结构上不一定是连续的,所以线性表在物理上存储时,通常以数组和链式结构形式存储,下面我们就来学习一下线性表中的顺序表吧。


一、什么是顺序表

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

二、顺序表的分类

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

代码如下(示例):

//静态顺序表
struct SArrayList
{
	DataType array[MAX];//存储数据的数组
	int size; // 有效的数据的个数
};

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

代码如下(示例):

//动态顺序表
struct DArrayList
{
	DataType* pointer;//指向动态开辟的数组
	int size;//有效数据的个数
	int capacity;//最大容量
};

3.动静态顺序表的区别

  1. 从能存储最大容量的角度分析:静态顺序表的容量大小依靠于我们数组的大小,数组定义多大,我们容量就多大。而动态顺序表的最大容量是由capacity这个字段来标志的。
  2. 从存储数据的方式分析:静态顺序表是靠数组存储数据的,而动态顺序表是凭借指针来存储数据。

4.动静态顺序表的相同点

  1. 它们存储的有效数据的个数都是可以直接凭借size字段获取的,因此它们的初始化是一样的,是一开始都是空表状态,所以对于顺序表一开始的初始化一定要保证size为0。

5.动态顺序表比静态顺序表好在哪些方面

  1. 我们知道动态顺序表的存储数据是靠指针来存储的,那么这里就涉及到开辟空间大小的问题,所以才有了capacity这个字段,这个字段主要控制的是指针开辟的最大容量,如果说存储的数据过多,甚至超过了capacity,那么就需要扩大capacity,因而动态顺序表相比静态顺序表增添了扩容的功能,这样就弥补了静态顺序表的缺陷:1. 存储数据少,但开辟的数组容量大造成的空间内存浪费问题。2. 存储数据多,但开辟的数组容量要比存储的数据小的多,造成许多数据存不上的问题。所以我认为动态顺序表比静态顺序表好在有扩容的方面,存储数据多的话呢,就扩容,少的话,就存储,这样极大的减少了空间浪费问题。

三、(动态)顺序表的实现

1.动态顺序表的结构体

//动态顺序表
typedef struct SeqList
{
	SLDateType* array;
	int size;
	int capacity;
}SeqList;

2.对数据的管理(增,删,查,改)完整代码

  1. SeqList.h
//SeqList.h
#pragma once
#include<stdio.h>
#include<iostream>
#include<assert.h>
#include<stdlib.h>
using namespace std;
typedef int SLDataType;
typedef struct SeqList 
{
	SLDataType* array; // 动态开辟数组
	int size;//有效数据个数
	int capacity;//最大容量
}SeqList;
//初始化
void SeqListInit(SeqList* psl);
// 检查空间,如果满了,进行增容
void CheckCapacity(SeqList* psl);
// 顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x);
// 顺序表尾删
void SeqListPopBack(SeqList* psl);
// 顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x);
// 顺序表头删
void SeqListPopFront(SeqList* psl);
// 顺序表查找
int SeqListFind(SeqList* psl, SLDataType x);
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* psl, size_t pos);
// 顺序表销毁
void SeqListDestory(SeqList* psl);
// 顺序表打印
void SeqListPrint(SeqList* psl);
  1. SeqList.cpp
//SeqList.cpp
#include"SeqList.h"
//初始化
void SeqListInit(SeqList* psl)
{
	psl->array = NULL;
	psl->size = 0;
	psl->capacity = 0;
}

// 检查空间,如果满了,进行增容
void CheckCapacity(SeqList* psl)
{
	assert(psl);
	if (psl->size == psl->capacity)
	{
		//增容
		int newCapacity = (psl->capacity == 0) ? 4 : (2 * psl->capacity);
		SLDataType* tmp = NULL;
		tmp = (SLDataType*)realloc(psl->array,sizeof(SLDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		psl->capacity = newCapacity;
		psl->array = tmp;
	}
}
// 顺序表尾插  时间复杂度O(1)
void SeqListPushBack(SeqList* psl, SLDataType x)
{
	assert(psl);
	CheckCapacity(psl);
	//尾插
	psl->array[psl->size] = x;
	psl->size++;
}

// 顺序表打印 时间复杂度O(N)
void SeqListPrint(SeqList* psl)
{
	assert(psl);
	for (int i = 0; i < psl->size; i++)
	{
		cout << psl->array[i] << " ";
	}
}

// 顺序表尾删 时间复杂度O(1)
void SeqListPopBack(SeqList* psl)
{
	assert(psl);
	if (psl->size == 0)
	{
		cout << "已经是空表" << endl;
		return;
	}
	psl->size--;
}
// 顺序表头插 时间复杂度O(N)
void SeqListPushFront(SeqList* psl, SLDataType x)
{
	assert(psl);
	CheckCapacity(psl);
	for (int i = psl->size - 1; i >= 0; i--)
	{
		psl->array[i + 1] = psl->array[i];
	}
	psl->array[0] = x;
	psl->size++;
}
// 顺序表头删 时间复杂度O(N)
void SeqListPopFront(SeqList* psl)
{
	assert(psl);
	if (psl->size == 0)
	{
		cout << "已经是空表" << endl;
		return;
	}
	for (int i = 1; i < psl->size; i++)
	{
		psl->array[i - 1] = psl->array[i];
	}
	psl->size--;
}
// 顺序表查找 时间复杂度O(N)
int SeqListFind(SeqList* psl, SLDataType x)
{
	assert(psl);
	for (int i = 0; i < psl->size; i++)
	{
		if (psl->array[i] == x)
		{
			return i;
		}
	}
	return -1;
}
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{
	assert(psl);
	assert(pos <= psl->size);
	CheckCapacity(psl);
	//注意:这里i要用size_t类型,如果用int型,在比较i>=pos的时候会把int型转化为size_t类型再进行比较
	for (size_t i = psl->size - 1; i >= pos; i--)
	{
		psl->array[i + 1] = psl->array[i];
	}
	psl->array[pos] = x;
	psl->size++;
}
// 顺序表删除pos位置的值
void SeqListErase(SeqList* psl, size_t pos)
{
	assert(psl);
	assert(pos <= psl->size-1);
	if (psl->size == 0)
	{
		cout << "已经是空表" << endl;
		return;
	}
	for (size_t i = pos + 1; i < psl->size; i++)
	{
		psl->array[i - 1] = psl->array[i];
	}
	psl->size--;
}

// 顺序表销毁
void SeqListDestory(SeqList* psl)
{
	assert(psl);
	free(psl->array);
	psl->capacity = 0;
	psl->size = 0;
	
}
  1. test.cpp
//test.cpp
#include"SeqList.h"
void SeqListTest1()
{
	SeqList sl;
	SeqListInit(&sl);
	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 2);
	SeqListPushFront(&sl, 7);
	SeqListPushFront(&sl, 7);
	SeqListPushFront(&sl, 7);
	SeqListInsert(&sl, 2, 9);
	SeqListErase(&sl, 2);
	SeqListErase(&sl, 2);
	SeqListErase(&sl, 2);
	SeqListErase(&sl, 2);
	SeqListErase(&sl, 2);
	SeqListErase(&sl, 2);
	SeqListErase(&sl, 2);
	SeqListPrint(&sl);
	SeqListDestory(&sl);
	free(&sl);
	SeqListPrint(&sl);
}
int main()
{
	SeqListTest1();
	return 0;
}

四、顺序表存在的问题

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

五、总结

学完顺序表我们发现,虽然顺序表对于存取数据较快,但是同时也存在着许多问题,例如中间头部的插入删除时间复杂度较高,扩容的时候需要申请新的空间,拷贝数据,释放旧空间,这会造成大量的消耗,而且扩容一般是呈2倍的增长,会有一定的空间浪费,那么就没有更好的数据结构能够解决这些问题了吗,答案是有的,它就是我们数据结构中的链表,我会在下次博客中揭晓链表的结构与顺序表相比其优缺点。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
静态顺序是一种使用数组实现的线性,它的大小在编译时确定,不可态改变。下面是静态顺序的C语言实现: ```c #define MAX_SIZE 100 // 定义静态顺序的最大容量 typedef struct { int data[MAX_SIZE]; // 用数组存储数据元素 int length; // 当前顺序的长度 } SeqList; // 初始化顺序 void init(SeqList *list) { list->length = 0; } // 插入元素 int insert(SeqList *list, int index, int value) { if (index < 0 || index > list->length || list->length == MAX_SIZE) { return 0; // 插入位置不合法或顺序已满,插入失败 } for (int i = list->length - 1; i >= index; i--) { list->data[i + 1] = list->data[i]; // 将插入位置后的元素后移一位 } list->data[index] = value; // 在插入位置插入新元素 list->length++; // 长度加1 return 1; // 插入成功 } // 删除元素 int remove(SeqList *list, int index) { if (index < 0 || index >= list->length) { return 0; // 删除位置不合法,删除失败 } for (int i = index + 1; i < list->length; i++) { list->data[i - 1] = list->data[i]; // 将删除位置后的元素前移一位 } list->length--; // 长度减1 return 1; // 删除成功 } // 获取元素 int get(SeqList *list, int index) { if (index < 0 || index >= list->length) { return -1; // 获取位置不合法,返回-1示失败 } return list->data[index]; } // 修改元素 int modify(SeqList *list, int index, int value) { if (index < 0 || index >= list->length) { return 0; // 修改位置不合法,修改失败 } list->data[index] = value; return 1; // 修改成功 } ``` 以上是静态顺序的简单实现,包括初始化、插入、删除、获取和修改等基本操作。你可以根据需要进行调用和扩展。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值