顺序的概念与结构

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

一般分为两种:静态顺序表和动态顺序表

静态顺序表

#define N 100
typedef int SLDataType;//以便可以存储不同类型的数据 typedef struct SeqList
{
SLDataType a[N];
int size;
}SL;

顺序表最大的缺点就是存储空间大小被固定了,空间有限。所以实际上我们不怎么会用这种静态的,所以我们这里不做实现。只实现下面的动态顺序表。

动态顺序表

typedef int SLDataType;//以便可以存储不同类型的数据

typedef struct SeqList
{
SLDataType* a;
int size;
int capacity;
}SL;

动态顺序表的函数接口

//无参构造,初始化顺序表
MyList();
//有参构造,初始化线性表
MyList(int length);
//销毁线性表
~MyList();
void MyListPushFront(const ElementType& e);//头插
void MyListPopFront();//头删
void MyListPushBack(const ElementType& e);//尾插
void MyListPopBack();//尾删
//插入操作,在pos位置插入元素e,并且返回线性表
MyList<ElementType>& Insert(int pos,const ElementType& e);
//删除操作,删除第pos个元素,并保存在在e中,返回删除后的线性表
MyList<ElementType>& DeleteByIndex(int pos, ElementType& e);
//判断线性表是否是空表
bool isEmpty()const;
//返回线性表的长度
int GetSize() const;
//返回pos位置的元素
bool GetElement(int pos, ElementType& e);
//修改pos位置的元素
bool ModifyData(int pos, const ElementType& e);
//返回元素e的位置
int Find(const ElementType& e);
//打印顺序表
void PrintMyList();

动态顺序表(C++实现)

顺序表的小框架

#define InitSize 10 //初始化时候顺序表的大小
class MyList
{
//全局函数作为友元
friend ostream& operator<<(ostream& os, MyList<ElementType>& L);
private:
ElementType* data;//数据
int capacity;//最大的容量
int size;//当前的大小
public:
};

初始化顺序表

//无参构造,初始化顺序表
MyList()
{
this->data = new ElementType[InitSize];
this->capacity = 10;
this->size = 0;
}
//有参构造,初始化线性表
MyList(int size) {
this->data = new ElementType[size];
this->capacity = 10;
this->size = size;
}

首先我们要对顺序表进行初始化,为了后面的增删查改做准备。

打印顺序表

//打印顺序表
void PrintMyList()
{
int i = 0;
for (i = 0; i < this->size; i++)
{
cout << this->data[i];
cout << endl;
}
}

销毁顺序表

为了防止内存泄漏,我们需要手动释放空间,我们用析构函数销毁线性表。

//销毁线性表
~MyList()
{
//销毁指针
delete[] this->data;
this->capacity = 0;
this->size = 0;
this->data = nullptr;
}

尾插

尾插当然就是在顺序表的尾部进行插入数据,插入数据的同时我们需要考虑到扩容,否则会导致空间不够,当capacity的大小和size的大小相同时,就说明顺序表容量已经满了,所以我们要对顺序表进行扩容操作,考虑到后面的头插也要可能扩容,所以封装一个函数CheckCapacity来检查顺序表容量,并且看是否需要扩容。实现如下:

//检查是否需要扩容
void CheckCapacity()
{
if (this->size == this->capacity)
{
this->capacity = this->capacity == 0 ? 4 : 2 * this->capacity;
ElementType* tmp = NULL;
tmp = (ElementType*)realloc(this->data, this->capacity * sizeof(ElementType));
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
this->data = tmp;
}
}

实现这个之后,我们就可以对顺序表进行尾插了,尾插唯一一个要注意的就是要检查顺序表容量,否则会导致程序崩溃。

尾插实现如下:

//尾插
void MyListPushBack(const ElementType& e)
{
//检查是否需要扩容
CheckCapacity();
data[size++] = e;
}

尾删

尾删要注意的一点:当顺序表中没有数据时,我们不应该再对顺序表进行删除了,为了保证程序不崩溃

//尾删
void MyListPopBack()
{
if (isEmpty())
{
return;
}
else
{
size--;
}
}

头插

头插值得我们考虑的点就是要对顺序表进行扩容,上面我们也提到了CheckCapacity这个函数,所以这里我们也要用到他,用他来检查顺序表容量。

void MyListPushFront(const ElementType& e)
{
//检查是否需要扩容
CheckCapacity();
for (int i = size; i > 0; i--)
{
data[i] = data[i - 1];
}
data[0] = e;
size++;
}

头删

头删也要注意顺序表中是否还有数据,如果没有就不进行删除操作,否则会导致程序崩溃。

//头删
void MyListPopFront()
{
//所有元素前移
for (int i = 1; i < this->size; i++)
{
data[i - 1] = data[i];
}
this->size--;
}

顺序表查找

//返回元素e的位置
int Find(const ElementType& e)
{
for (int i = 0; i < this->size; i++)
{
if (this->data[i] == e)
return i + 1;
}
cout << "未找到!" << endl;
return 0;
}

任意位置插入

看到插入两个字,我们就要考虑是否需要扩容。这一点很重要。还有我们要多pos这个参数进行判断,看是否在顺序表指定的范围中,因为顺序表是连续的,我们任意位置插入要合理,所以要对参数进行合理性判断

//插入操作,在pos位置插入元素e,并且返回线性表
MyList<ElementType>& Insert(int pos,const ElementType& e) {
//后移元素
if (pos<1 || pos>capacity + 1)
{
cout << "位置错误!" << endl;
return *this;
}
//检查是否需要扩容
CheckCapacity();
for (int j = this->size; j >= pos; j--)
this->data[j] = this->data[j - 1];
this->data[pos - 1] = e;
this->size++;
return *this;
}

任意位置删除

首先要对参数进行判断,顺序表不能为空,pos的位置要合理

//删除操作,删除第pos个元素,并保存在在e中,返回删除后的线性表
MyList<ElementType>& DeleteByIndex(int pos, ElementType& e)
{
if (pos<1 || pos>size) {
cout << "位置非法" << endl;
return *this;
}
e = data[pos - 1];
for (pos; pos < this->size; pos++)
{
this->data[pos - 1] = this->data[pos];
}
this->size--;
return *this;
}

修改任意位置的值

//修改pos位置的元素
//int tmp = 10; const int& ref = tmp;栈区的临时变量
bool ModifyData(int pos, const ElementType& e)
{
if (pos<1 || pos>this->size)
return false;
this->data[pos - 1] = e;
return true;
}

返回任意位置的元素

//返回pos位置的元素
bool GetElement(int pos, ElementType& e)
{
if (pos<1 || pos>this->size)
return false;
e = this->data[pos - 1];
return true;
}

零碎的操作

//判断线性表是否是空表
bool isEmpty()const
{
return this->size == 0;
}
//返回线性表的长度
int GetSize() const
{
return this->size;
}

完整的代码以及测试代码

#define _CRT_SECURE_NO_WARNINGS
#include<iostream> //引入头文件
#include<string>//C++中的字符串
using namespace std; //标准命名空间
#define InitSize 10 //初始化时候顺序表的大小
template<class ElementType>
class MyList
{
//全局函数作为友元
friend ostream& operator<<(ostream& os, MyList<ElementType>& L);
private:
ElementType* data;//数据
int capacity;//最大的容量
int size;//当前的大小
public:
/*
MyList();//无参构造
MyList(int size);//有参构造
~MyList();//析构函数
void MyListPushFront(const ElementType& e);//头插
void MyListPopFront();//头删
void MyListPopFront();
void MyListPushBack(const ElementType& e);//尾插
void MyListPopBack();//尾删
MyList<ElementType>& Insert(int pos, ElementType& e);//pos位置插入
MyList<ElementType>& DeleteByIndex(int pos, ElementType& e);//删除pos位置的元素
bool isEmpty()const;//判断是否是空表
int GetSize() const;//线性表的长度
bool GetElement(int pos, ElementType& e);//返回pos位置的元素
bool ModifyData(int pos, ElementType& e);//修改pos位置的元素
int Find(ElementType& e);//返回元素e的位置
void PrintMyList();//打印顺序表
*/
//无参构造,初始化顺序表
MyList()
{
this->data = new ElementType[InitSize];
this->capacity = 10;
this->size = 0;
}
//有参构造,初始化线性表
MyList(int size) {
this->data = new ElementType[size];
this->capacity = 10;
this->size = size;
}
//销毁线性表
~MyList()
{
//销毁指针
delete[] this->data;
this->capacity = 0;
this->size = 0;
this->data = nullptr;
}
//检查是否需要扩容
void CheckCapacity()
{
if (this->size == this->capacity)
{
this->capacity = this->capacity == 0 ? 4 : 2 * this->capacity;
ElementType* tmp = NULL;
tmp = (ElementType*)realloc(this->data, this->capacity * sizeof(ElementType));
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
this->data = tmp;
}
}
//头插
void MyListPushFront(const ElementType& e)
{
//检查是否需要扩容
CheckCapacity();
for (int i = size; i > 0; i--)
{
data[i] = data[i - 1];
}
data[0] = e;
size++;
}
//头删
void MyListPopFront()
{
//所有元素前移
for (int i = 1; i < this->size; i++)
{
data[i - 1] = data[i];
}
this->size--;
}
//尾插
void MyListPushBack(const ElementType& e)
{
//检查是否需要扩容
CheckCapacity();
data[size++] = e;
}
//尾删
void MyListPopBack()
{
if (isEmpty())
{
return;
}
else
{
size--;
}
}
//插入操作,在pos位置插入元素e,并且返回线性表
MyList<ElementType>& Insert(int pos,const ElementType& e) {
//后移元素
if (pos<1 || pos>capacity + 1)
{
cout << "位置错误!" << endl;
return *this;
}
//检查是否需要扩容
CheckCapacity();
for (int j = this->size; j >= pos; j--)
this->data[j] = this->data[j - 1];
this->data[pos - 1] = e;
this->size++;
return *this;
}
//删除操作,删除第pos个元素,并保存在在e中,返回删除后的线性表
MyList<ElementType>& DeleteByIndex(int pos, ElementType& e)
{
if (pos<1 || pos>size) {
cout << "位置非法" << endl;
return *this;
}
e = data[pos - 1];
for (pos; pos < this->size; pos++)
{
this->data[pos - 1] = this->data[pos];
}
this->size--;
return *this;
}
//判断线性表是否是空表
bool isEmpty()const
{
return this->size == 0;
}
//返回线性表的长度
int GetSize() const
{
return this->size;
}
//返回pos位置的元素
bool GetElement(int pos, ElementType& e)
{
if (pos<1 || pos>this->size)
return false;
e = this->data[pos - 1];
return true;
}
//修改pos位置的元素
//int tmp = 10; const int& ref = tmp;栈区的临时变量
bool ModifyData(int pos, const ElementType& e)
{
if (pos<1 || pos>this->size)
return false;
this->data[pos - 1] = e;
return true;
}
//返回元素e的位置
int Find(const ElementType& e)
{
for (int i = 0; i < this->size; i++)
{
if (this->data[i] == e)
return i + 1;
}
cout << "未找到!" << endl;
return 0;
}
//打印顺序表
void PrintMyList()
{
int i = 0;
for (i = 0; i < this->size; i++)
{
cout << this->data[i];
cout << endl;
}
}
};
template<class ElementType>
ostream& operator<<(ostream& os, MyList<ElementType>& L)
{
for (int i = 0; i < L.GetSize(); i++)
os << L.data[i] << "\t";
os << endl;
return os;
}
/*
MyList();//无参构造
MyList(int size);//有参构造
~MyList();//析构函数
void MyListPushFront(const ElementType& e);//头插
void MyListPopFront();//头删
void MyListPopFront();
void MyListPushBack(const ElementType& e);//尾插
void MyListPopBack();//尾删
MyList<ElementType>& Insert(int pos, ElementType& e);//pos位置插入
MyList<ElementType>& DeleteByIndex(int pos, ElementType& e);//删除pos位置的元素
bool isEmpty()const;//判断是否是空表
int GetSize() const;//线性表的长度
bool GetElement(int pos, ElementType& e);//返回pos位置的元素
bool ModifyData(int pos, ElementType& e);//修改pos位置的元素
int Find(ElementType& e);//返回元素e的位置
void PrintMyList();//打印顺序表
*/
int main()
{
MyList<int> list;
//测试头插
list.MyListPushFront(1);
list.MyListPushFront(2);
list.MyListPushFront(3);
list.PrintMyList();
cout << list.GetSize() << endl;
cout << "====================" << endl;
//测试头删
list.MyListPopFront();
list.MyListPopFront();
list.PrintMyList();
cout << list.GetSize() << endl;
cout << "====================" << endl;
//测试尾插
list.MyListPushBack(2);
list.MyListPushBack(3);
list.MyListPushBack(4);
list.PrintMyList();
cout << list.GetSize() << endl;
cout << "====================" << endl;
//测试尾删
list.MyListPopBack();
list.MyListPopBack();
list.PrintMyList();
cout << list.GetSize() << endl;
cout << "====================" << endl;
//测试任意位置删除
list.MyListPushFront(1);
list.MyListPushFront(2);
list.MyListPushFront(3);
list.PrintMyList();
int b = 0;
list.DeleteByIndex(2,b);
cout << b << endl;
list.PrintMyList();
cout << "====================" << endl;
//测试任意位置插入
list.Insert(1,10);
list.PrintMyList();
cout << "====================" << endl;
//返回Pos位置的元素
int c = 0;
list.GetElement(1, c);
cout << c << endl;
cout << "====================" << endl;
//修改pos位置的元素
list.ModifyData(1, 30);
list.PrintMyList();
cout << "====================" << endl;
//返回元素e的位置
cout << list.Find(30) << endl;
system("pause");
return EXIT_SUCCESS;
}

初学的小伙伴可以自己动手写一写以及优化以下~

顺序表的问题及思考

  1. 中间/头部的插入删除,时间复杂度为O(N)
  2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  3. 增容一般是呈2倍的增长,势必会有一定的空间浪费,不能完全实现按需所取

数据结构初阶--顺序表(讲解+C++类模板实现)的更多相关文章

  1. 五种编程语言解释数据结构与算法——顺序表3(JavaScript与Python语言实现)

    7.JavaScript语言实现 7.1.用ES6语法编写顺序表类 //1.创建类 class MyList { //1. initList(&L):初始化表.构造一个空的线性表.放回值应该是 ...

  2. 五种编程语言解释数据结构与算法——顺序表2(java与C++语言实现)

    5.java实现方式: 5.1.顺序表的抽象结构 package com.xgp.顺序表; public interface MyList<T> { //1. initList(& ...

  3. C++ 数据结构学习一(顺序表)

    //SequentialList.h 顺序表模板类 #ifndef SEQUENTIAL_LIST_HXX#define SEQUENTIAL_LIST_HXX using std::cout; us ...

  4. C++数据结构学习之顺序表

    顺序表是数据结构中最基本也是应用相当广泛的一种数据结构类型.它通常包含三个私有成分,即指向数据数组的头指针.当前表长以及表的实际容量.表的头指针通常指向数据数组的基地址,通过数组的形式进行访问数据数组 ...

  5. 数据结构 单链表&顺序表

    顺序表: 一般使用数组(C语言中的数组采用顺序存储方式.即连续地址存储)来描述. 优点:在于随机访问元素, 缺点:插入和和删除的时候,需要移动大量的元素. 链表: 优点:插入或删除元素时很方便,使用灵 ...

  6. 数据结构——Java实现顺序表

    一.分析 什么是顺序表?顺序表是指用一组地址连续的存储单元依次存储各个元素,使得在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中的线性表.一个标准的顺序表需要实现以下基本操作: 1.初始化顺序表 ...

  7. 数据结构之线性顺序表ArrayList(Java实现)

    一.ListMe接口: import java.util.ArrayList; //实现线性表(顺序表和链表)的接口://提供add get isEmpty size 功能public interfa ...

  8. 【c++版数据结构】之顺序表的实现

    SeqList.h #ifndef SEQLIST_H #define SEQLIST_H #include<iostream> using namespace std; typedef ...

  9. 【数据结构】之顺序表(Java语言描述)

    之前总结过使用C语言描述的顺序表数据结构.在C语言类库中没有为我们提供顺序表的数据结构,因此我们需要自己手写,详细的有关顺序表的数据结构描述和C语言代码请见[我的这篇文章]. 在Java语言的JDK中 ...

  10. 【数据结构】之顺序表(C语言描述)

    顺序表是线性表的一种,它将元素存储在一段连续的内存空间中,表中的任意元素都可以通过下标快速的获取到,因此,顺序表适合查询操作频繁的场景,而不适合增删操作频繁的场景. 下面是使用 C语言 编写的顺序表的 ...

随机推荐

  1. Zookeeper及基于Zookeeper的分布式锁总结

    1. Zookeeper ZooKeeper 内部存储的数据结构 / +-- node1 +-- node2 | +-- sub_node21 -> "I am sub_node21& ...

  2. 从Spring中学到的【1】--读懂继承链

    最近看了一些 Spring 源码,发现源码分析的文章很多,而底层思想分析的文章比较少,这个系列文章准备总结一下Spring中给我的启示,包括设计模式思想.SOLID设计原则等,涉及一些编程的基本原则, ...

  3. 详解字符编码与 Unicode

    人类交流使用 A.B.C.中 等字符,但计算机只认识 0 和 1.因此,就需要将人类的字符,转换成计算机认识的二进制编码.这个过程就是字符编码. ASCII 最简单.常用的字符编码就是 ASCII(A ...

  4. 微信小程序-坑,wxml里wx:if 判断 数字 是否在一个数组中。

    <view wx:if="{{item.index}} in {{vote_list}}"> 已赞 <image src="/static/zan_y. ...

  5. vue开发组件开发中的小技巧

    声明:以下随笔由博主自主编写,也有部分引用网友的,引用部分版权归原作者所有,其他博主原创部分禁止转载.复制全部或部分用以重新发布! vue递归组件事件阻止冒泡 其实这里主要还有递归组件的自定义事件不生 ...

  6. 在UniApp的H5项目中,生成二维码和扫描二维码的操作处理

    在我们基于UniApp的H5项目中,需要生成一些二维码进行展示,另外也需要让用户可以扫码进行一定的快捷操作,本篇随笔介绍一下二维码的生成处理和基于H5的扫码进行操作.二维码的生成,使用了JS文件wea ...

  7. 微服务系列之Api文档 swagger整合

    1.前言 微服务架构随之而来的前后端彻底分离,且服务众多,无论是前后端对接亦或是产品.运营翻看,一个现代化.规范化.可视化.可尝试的文档是多么重要,所以我们这节就说说swagger. Swagger是 ...

  8. linux软链接的创建、修改和删除

    创建 ln -s [源文件或目录] [目标文件或目录] 修改 ln –snf [新的源文件或目录] [目标文件或目录] 删除 rm –rf 软链接名称 注意,上面这种形式可能会让人产生担忧,害怕删除的 ...

  9. 如何在linux下检测(自身)IP冲突

    最近遇到一个需求,或者说是一个用户现场问题. 我们设备先安装,设置dhcp模式获取ip进行联网,后来又安装了其他设备,但该设备是手动设置的静态ip地址,正好与我们设备冲突,造成网络故障. 那我们就需要 ...

  10. 关于AWS-EC2或者多个资源的tag的批量添加-基于Resource Groups & Tag Editor 和 命令处理

    今天收到一个请求,需要对公司所有的ec2-添加上两个成本IO标签,因为机器太多了 想到了如下两种方案去批量处理 方案一:利用aws的 [Management Tools]下的 Resource Gro ...