首页 > 编程语言 >数据结构初阶--顺序表(讲解+C++类模板实现)

数据结构初阶--顺序表(讲解+C++类模板实现)

时间:2022-11-23 11:37:00浏览次数:42  
标签:初阶 const -- pos C++ int ElementType data size

顺序的概念与结构

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

静态顺序表

#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倍的增长,势必会有一定的空间浪费,不能完全实现按需所取

标签:初阶,const,--,pos,C++,int,ElementType,data,size
From: https://www.cnblogs.com/yzsn12138/p/16917698.html

相关文章

  • 嵌入式操作系统内核原理和开发(内存分配算法)
       内存分配是操作系统必须面对的一个环节,除非这个系统本身不需要内存安排,所有业务可以通过全局数据和堆栈搞定。内存分配其实不困难,但是由内存引申出来的东西就比较复......
  • 嵌入式操作系统内核原理和开发(基于链表节点的内存分配算法)
      链接节点的内存分配方法,其实就是一种按需分配的内存分配方法。简单一点说,就是你需要多少内存,我就给你多少内存。当然,为了把分配的内存都连起来,我们还需要对分配节点进......
  • 嵌入式操作系统内核原理和开发(最快、最优、最差内存分配算法)
       前面我们说到了基于​​链表的内存分配​​算法。但是之前我们也说过,其实内存分配一般有三个原则,最快、最优和最差。最快比较好理解,就是寻找到合适的节点就立即分配......
  • 随想录(写给自己的C++编程规范)
       对于我这样一个C语言的程序员来说,编写C++的机会其实不太多。但是我还是比较喜欢写C++语言,原因主要有几个方面:(1)自己学C++语言的时间比较长了,也比较了解,如果从大一的时......
  • 随想录(png的读取和显示)
       之前在阅读FTK代码的时候,发现工程本身用到了PNGLIB的代码。虽然网上关于pnglib的描述文件很多,但是真正好用、可以用的却没有多少。所以,为了学习的方便,我自己做了一个......
  • 前端项目通过‘URL 重写’部署在 IIS 中,访问 WebAPI 接口
    〇、前言在前端项目开发时,我们可以通过配置代理proxy来访问本地或者是远程接口,但是部署后代理就失效了。如果项目部署在IIS上,就没法去对应到指定接口,此时就需要IIS......
  • 随想录(公司程序员的九层楼)
        就IT公司而言,都希望自己的程序员在单位时间内生产出效率最高的代码。但是,不同的人有不同的开发效率。至于说效率之间的差别究竟有多少,还真不得而知。这里写了几个......
  • SCADA系统架构、类型和应用
    智能仪表和远程终端单元(RTU)/可编程逻辑控制器(PLC)的进步使得许多行业的过程控制都可以利用SCADA系统的优势轻松管理和操作。SCADA在多种应用中很受欢迎,如加工工业、石油......
  • 随想录(软件中的bug)
       软件由于其特殊性,始终和bug紧密地联系在一起。没有bug的软件是不存在的。为什么这么说呢?我们知道,软件是由很多人完成的,不同的人完成代码的水平是不一样的,一旦沟通不......
  • 随想录(锁的来由和使用)
       对于开发系统级别软件的朋友来说,无论你是主动的还是被动的,锁的应用都是少不了的。很多人用锁,可是却未必知道锁的前世今生,什么时候用锁,什么时候不用锁?该用什么样的锁?......