DS004-C++模板类-顺序表-超级数组-占用连续空间-随机存取-动态扩展空间-尾部插入效率高

一、适用顺序表的例子

例1:输入100个整数,按非降序排序并输出;

例2:输入100个字符,按非降序排序并输出;

例3:输入100个字符串,按非降序排序并输出;

例4:编写一个通信录程序,能够存储联系人的姓名、手机号、备注信息,要按照姓名的拼音非降序排序

例5:2020年全国参加高考人数1071万,河南就有115.8万人,占了全国考生的十分之一。共有132957人分数超过一本线,比例大约为11.48%。在对115.8万人的成绩统计一分一段表时,需要按照成绩排序

分析以上例子,排序后,各个数据元素就是一个前后关系或者说大小关系,

第一个元素没有前驱;除第一个元素外,其他每一个元素有且仅有一个直接前驱。

最后一个元素没有后继;除最后一个元素外,其他每一个元素有且仅有一个直接后继。

这些数据可以存储在顺序表中处理。

二、顺序表三要素

顺序表要占用内存中连续的空间,因此其三要素为:

1、连续空间的首地址elem;

2、连续空间的大小listsize;

3、已经存储的元素的个数length。

而且,一定要注意,length恰巧跟最后一个元素的下一个位置的下标相同,

因此常常用[0,length-1]表示表中元素的下标范围。

为了让顺序表具有泛化能力,即能处理多种数据类型,用模板类最好不过了。

template<typename T>class vec
{
	T *elem;//连续空间的首地址
	int listsize;//连续空间的大小
	int length;//实际元素的个数
	public:
    
    //下面放置成员函数,表示对顺序表的操作
}

让顺序表三要素作为模板类的成员变量,用成员函数作为顺序表的基本操作。

三、顺序表的基本操作

1、初始化操作

初始化就是对顺序表的三要素赋初值。

    void init()
	{//对线性表三要素进行初始化
		elem=new T[1];//初始时,占用一个空间,这个可以自己设置,一般是2的n次方
		listsize=1;
		length=0;		
	}

 2、构造函数

类通常会有构造函数,和类同名,在用类定义对象时,构造函数会自动执行,

因此,通常让构造函数调用初始化操作函数。

vec()
{//构造函数
	init();
}

3、在顺序表尾部插入数据x

 在插入数据之前,要判断顺序表空间是否已满,如果已满,需要向操作系统申请更大的空间

顺序表的动态扩展空间就体现在这个地方

当然这个动态调整是由代码控制的

如果空间未满,那么length位置就是插入位置

void push_back(T x)
	{//在顺序表尾部插入数据
		if(length==listsize)
		{//空间不足 
			enlarge();
		}
		
		elem[length]=x;
		length++;
	}

4、动态扩展空间

 原理:

(1)、新申请一块更大的空间newbase;

(2)、把老空间elem中数据拷贝到newbase空间

(3)、释放到elem空间

(4)、让elem指向新空间

(5)、更改listsize为新的大小

int enlarge()
	{//当L的空间不足时,本函数功能:将L的空间翻倍
	//如果成功返回1,失败返回0
	
		T *newbase=new T[listsize*2];//开辟新空间
		if(0==newbase)
			return 0;
		for(int i=0;i<length;i++)//把老空间已有数据拷贝到新空间
			newbase[i]=elem[i];
		delete []elem;//释放掉老空间,还给操作系统
		elem=newbase;//elem指向新空间首地址
		listsize=listsize*2;//空间大小翻倍
		return 1;		
		
	}

5、在下标为i的位置插入数据

(1)i的范围必须是[0,length],其他值不合法;

(2)插入数据前判断空间是否已满

(3)[i,length-1]数据后移一位

(4)把数据插入到i下标处,表长增一

int insert(int i,T x)
	{//在顺序表的第i个位置插入元素x
	//如果成功,返回1,否则返回0

		if(i<0||i>length) return 0;
		if(length==listsize)
		{//如果空间已满,申请更大空间
			int flag=enlarge();
			if(flag==0)return 0;
		}
		int j;
		for(j=length-1;j>=i;j--)//第i到length-1个数据后移一位
			elem[j+1]=elem[j];
		elem[i]=x;//插入数据
		length++;//表长增一
		return 1;
		
	}

6、删除下标i处的数据

(1)i的范围必须是[0,length-1],其他不合法;

(2)[i+1,length-1]数据逐个前移一位

(3)表长减一

int erase(int i)
	{//删除下标为 i 的数据 
		if(i<0 || i>=length)//如果删除位置不合法
			return -1;
		else
		{
			for(int j=i+1;j<=length-1;j++)
				elem[j-1]=elem[j];//元素前移 一位
			length--;
			return 1;
		}
		
	}

四、用模板类表示的顺序表的完整代码(可运行)

#include<iostream> 
using namespace std;
//模板类,T是泛化类型
template<typename T>class vec
{
	T *elem;//连续空间的首地址
	int listsize;//连续空间的大小
	int length;//实际元素的个数
	public:
	vec()
	{//构造函数
		init();
	}
	void init()
	{//对线性表三要素进行初始化
		elem=new T[1];//初始时,占用一个空间,这个可以自己设置,一般是2的n次方
		listsize=1;
		length=0;		
	}
	int enlarge()
	{//当L的空间不足时,本函数功能:将L的空间翻倍
	//如果成功返回1,失败返回0
	
		T *newbase=new T[listsize*2];//开辟新空间
		if(0==newbase)
			return 0;
		for(int i=0;i<length;i++)//把老空间已有数据拷贝到新空间
			newbase[i]=elem[i];
		delete []elem;//释放掉老空间,还给操作系统
		elem=newbase;//elem指向新空间首地址
		listsize=listsize*2;//空间大小翻倍
		return 1;		
		
	}
	void push_back(T x)
	{//在顺序表尾部插入数据
		if(length==listsize)
		{//空间不足 
			enlarge();
		}
		
		elem[length]=x;
		length++;
	}
	
	int insert(int i,T x)
	{//在顺序表的第i个位置插入元素x
	//如果成功,返回1,否则返回0

		if(i<0||i>length) return 0;
		if(length==listsize)
		{//如果空间已满,申请更大空间
			int flag=enlarge();
			if(flag==0)return 0;
		}
		int j;
		for(j=length-1;j>=i;j--)//第i到length-1个数据后移一位
			elem[j+1]=elem[j];
		elem[i]=x;//插入数据
		length++;//表长增一
		return 1;
		
	}
	int erase(int i)
	{//删除下标为 i 的数据 
		if(i<0 || i>=length)//如果删除位置不合法
			return -1;
		else
		{
			for(int j=i+1;j<=length-1;j++)
				elem[j-1]=elem[j];//元素前移 一位
			length--;
			return 1;
		}
		
	}
	int find(T e)
	{//在顺序表中查找元素e是否存在,
		//如果存在返回对应的下标
		//否则返回-1
		for(int i=0;i<=length-1;i++)
		{
			if(elem[i]==e)
				return i;
		}
		return -1;
	} 
		
	
	void print() 
	{
		int i;
		for(i=0;i<length;i++)
		{
			cout<<elem[i]<<" ";
		}
		cout<<"\n";
	}
	~vec()
	{
		delete []elem;
	}
};

int main()
{
	vec<int> v;
	//v.init();
	v.push_back(1);v.push_back(2);
	v.push_back(4);v.push_back(5);
	v.print();
	v.insert(2,3);
	v.print();
	v.erase(4);
	v.print();
	cout<<v.find(3)<<endl;
//同理可以改变顺序表存储数据的类型,进行测试
    vec<char> v2;
    v2.push_back('a');v2.push_back('c');
	v2.push_back('e');v2.push_back('g');
	v2.print();
	v2.insert(2,'d');
	v2.print();
	v2.erase(4);
	v2.print();
    
	return 0;
}

运行结果:

五、分析

顺序表的优缺点:

1、按照下标随机存取数据,方便,效率高;

2、能够动态扩展空间,空间效率高;

3、在顺序表尾部插入数据,不需要移动其他元素,效率高;

4、在顺序表头部及其他位置插入数据,需要移动数据,效率最低

因此,看待一个事物,要采用二分法或者说矛盾分析法,不能只看到好处,还要看到坏处,做好最坏打算。

猜你喜欢

转载自blog.csdn.net/weixin_43917370/article/details/108388823