C++ 顺序表实现及分析

文章目录

一、头文件声明部分

#ifndef __SEQLIST_H__
#define __SEQLIST_H__

#include<iostream>
#include<cassert>
using namespace std;
typedef int DataType;

class Seqlist
{
public:
	Seqlist();//初始化工作
	~Seqlist();//收尾工作
	void SeqlistPrint();//打印出顺序表中元素
	void AdjustCapacity();//调整容量
	//增删插查改
	void SeqlistPushBack(int val);//尾插
	void SeqlistPushFront(int val);//头插
	void SeqlistInsert(int pos, int val);//在特定位置插入元素
	
	void SeqlistPopBack();//尾删
	void SeqlistPopFront();//头删
	void SeqlistErase(int pos);//删除特定位置的元素

	int SeqlistFind(int val);//查找元素

	void SeqlistModify(int pos, int val);//修改特定位置的元素
private:
	DataType* m_arr;
	int m_size;
	int m_capacity;
};
#endif

二、源文件实现部分

#include "Seqlist.h"

Seqlist::Seqlist() //初始化工作
{
	this->m_arr = NULL;
	this->m_capacity = 0;
	this->m_size = 0;
}
Seqlist::~Seqlist()//收尾工作
{
	delete []this->m_arr;
	this->m_arr = NULL;
	this->m_capacity = 0;
	this->m_size = 0;
}
void Seqlist::SeqlistPrint()//打印出顺序表中元素
{
	for (int i = 0; i < this->m_size; i++)
	{
		cout << this->m_arr[i] << ' ';
	}
}
void Seqlist::AdjustCapacity()//调整容量
{
	if (this->m_capacity == this->m_size)
	{
		int newCapacity = this->m_capacity == 0 ? 4 : this->m_capacity << 1;
		try
		{
			DataType* tmp = new DataType[newCapacity];
			this->m_capacity = newCapacity;
			for (int i = 0; i < this->m_size; i++)
			{
				tmp[i] = this->m_arr[i];
			}
			delete []this->m_arr;
			this->m_arr = tmp;
		}
		catch (bad_alloc& e)
		{
			cout << "bad_alloc caught: " << e.what() << endl;
		}

		//或者像下面这样,开辟内存失败后,返回空指针
		//DataType* tmp = new(nothrow) DataType[newCapacity];
		//if (NULL == tmp)
		//{
		//	cout << "开辟内存失败" << endl;
		//	exit(-1);
		//}
		//this->m_capacity = newCapacity;
		//for (int i = 0; i < this->m_size; i++)
		//{
		//	tmp[i] = this->m_arr[i];
		//}
		//delete[]this->m_arr;
		//this->m_arr = tmp;
	}
}
//增删插查改
void Seqlist::SeqlistPushBack(int val)//尾插
{
	AdjustCapacity();
	this->m_arr[this->m_size] = val;
	this->m_size++;
	//this->SeqlistInsert(this->m_size, val);
}
void Seqlist::SeqlistPushFront(int val)//头插
{
	AdjustCapacity();
	int end = this->m_size - 1;
	for (int i = end; i >= 0; i--)
	{
		this->m_arr[i + 1] = this->m_arr[i];
	}
	this->m_arr[0] = val;
	this->m_size++;
	//this->SeqlistInsert(0, val);
}
void Seqlist::SeqlistInsert(int pos, DataType val)//在特定位置插入元素
{
	AdjustCapacity();
	assert(pos <= this->m_size && pos >= 0);

	int end = this->m_size - 1;
	for (int i = end; i >= pos; i--)
	{
		this->m_arr[end - 1] = this->m_arr[end];
	}
	this->m_arr[pos] = val;
	this->m_size++;
}

void Seqlist::SeqlistPopBack()//尾删
{
	assert(this->m_size > 0);
	this->m_size--;
	//this->SeqlistErase(this->m_size - 1);
}
void Seqlist::SeqlistPopFront()//头删
{
	assert(this->m_size > 0);
	for (int i = 0; i < this->m_size; i++)
	{
		this->m_arr[i] = this->m_arr[i + 1];
	}
	this->m_size--;
	//this->SeqlistErase(0);
}
void Seqlist::SeqlistErase(int pos)//删除特定位置元素
{
	assert(pos < this->m_size && pos >= 0);
	for (int i = pos; i < this->m_size; i++)
	{
		this->m_arr[i] = this->m_arr[i + 1];
	}
	this->m_size--;
}

int Seqlist::SeqlistFind(DataType val)//查找元素
{
	for (int i = 0; i < this->m_size; i++)
	{
		if (val == this->m_arr[i])
		{
			return i;
		}
	}
	return -1;
}

void Seqlist::SeqlistModify(int pos, DataType val)//修改特定位置元素
{
	assert(pos < this->m_size&& pos >= 0);
	this->m_arr[pos] = val;
}

三、分析

1、初始化工作

完成m_arr、m_capacity、m_size的初始化;

2、收尾工作

释放m_arr后即置空。实际上,我认为,后面将m_capacity和m_size都置为0其实没有必要,因为实际上销毁这个顺序表对象后,我要用顺序表时,会重新实例化一个的。并且销毁对象,一般来说就是delete对象指针时,或函数结束时。

3、打印出顺序表中元素

依次打出即可。

4、调整容量

Ⅰ该环节在全局规划的分析

在增加和插入元素的环节中,当容量与实际现有大小将要(解释一下“将要”:还有一个元素之差时。因为增加和插入环节一次只能插入一个)相等时,需要调整容量。直接封装出去。

Ⅱ环节内容分析

  • 首先要注意new的使用,new存在失败的情况,于是我做了相关处理,有两个解决方案,第一个是代码处未注释掉的内容,new在失败后会扔出一个异常;第二个是代码处注释掉的内容,通过使用语句“std::nothrow”让new在失败后返回一个空指针而不是异常,回顾:在C语言中malloc失败后,返回的就是空指针。
  • 其次注意在new成功后的扩容操作,我们创建了一个临时的容量,转移数据,释放原容量,指针替换。

5、尾插、头插、特定位置插入

  • 尾插:正因为数组是从0开始的,而数组的大小是从1开始的,于是我们可以在最后数组m_size的位置插入新元素。最后m_size加一。
  • 头插:先有一个原地移动数组(出了第一个元素外)的操作,在数组第一个元素的位置上覆盖上新元素。
  • 特定位置插入:同样是先原地移动数组后,覆盖上新元素。使用断言来防止输入的pos不规范。
  • 补充:完全可以通过限制特定位置插入的功能来实现尾插和头插。注释部分即是这样实现的。

6、尾删、头删、特定位置删除

  • 尾删:直接使用m_size减一的举措来达到目的,这都要归功于我们通过m_size来限制数组的访问范围的操作。
  • 头删:首先通过原地向前移动数组来覆盖掉第一个元素,然后通过m_size减一来限制访问数组权限。
  • 特定位置删除:原地移动数组覆盖。使用断言来防止输入的pos不规范。
  • 补充:同“尾插、头插、特定位置插入”环节中的补充思想一样。

7、查找元素

如果用二分查找效率更高,但是我们需要先对数组进行排序,排序在不同的情境下会起到截然相反的效果,有时是使数据更有序的行为,但有时是破环原来数据的顺序的行为。并且实际上,我对二分查找的边界问题还是处于不解状态,于是选择使用遍历进行查找元素的操作,找到返回位置,没找到返回-1;并且使用断言来防止输入的pos不规范。

8、特定位置修改

首先用断言对输入的pos进行检查,在pos这个位置进行覆盖修改。

上一篇:2021-08-06


下一篇:数据结构——02线性表