线性表—概述、顺序表

前言

本笔记是自己根据网上课程学习一起学知识「一起学知识」的知识服务平台,由小鹅通一键生成https://appd872nnyh9503.pc.xiaoe-tech.com/detail/p_60f24709e4b08f7ad23db8ef/6所做的笔记,基于C++类模板实现的数据结构,如在笔记中有误,欢迎批评指出。

概述

线性表:线性结构,保存的数据象线一样按顺序排列,数据之间一对一的关系。

线性表是具有相同数据类型的n(n>=0)个数据的有限序列。n:线性表长度。

  • 一般线性表表示为:(a1,a2,......ai,a i+1,....,an);
  • 每个元素都有直接前趋和直接后继(首元素只有直接后继,尾元素只有直接前趋)

数组、链表、栈、队列等都属于线性表的一种(或者理解为 数组,链表,栈,队列都可以用来表达线性表)。

非线性表:就是一对多的关系,如树、图、堆等。

顺序表

线性表的顺序存储:指的是用一段连续的内存空间一次存储线性表中的数据。

线性表的顺序存储一般就会采用一维数组来实现。STL,vecotr。

采用一维数组实现的线性表也被称为顺序表。

随机访问:通过数组首地址和所给元素下标可以最快的找到任意数组元素。而且这种通过下标随机访问数组元素的时间复杂度仅仅O(1)。

  • a[i]的地址 = 数组元素的首地址+下标*sizeof(整型);
  • 插入或者删除数据时效率会很低。
线性表一般可以用静态数组或者动态数组两种方式实现。
typedef struct
	{
		int m_data[10]; //静态数组来保存顺序表中的元素,一共10个位置(最多存入10个元素)
		int m_length;  //顺序表中当前实际长度(当前顺序表中已经存入了多少个元素)
	}SeqList;

	typedef struct
	{
		int* m_data;  //顺序表中的元素保存再m_data所指向的动态数组内存中。
		int m_length;  //顺序表中当前实际长度
		int  m_maxsize; //动态数组最大容量,因为动态数组可以扩容,因此要记录该值。
	}SeqList;

C++模板类实现顺序表及常用接口

#define InitSize 10 //动态数组的初始尺寸
#define IncSize  5   //当动态数组存满数据后每次扩容所能多保存的数据元素数量

template <typename T> //T代表数组中元素的类型
class SeqList
{
public:
	SeqList(int length = InitSize); //构造函数,参数可以有默认值
	~SeqList();      //析构函数

public:
	bool ListInsert(int i, const T& e); //在第i个位置插入指定元素
	bool ListDelete(int i);  //删除第i个位置的元素
	bool GetElem(int i, T& e);  //获得第i个位置的元素值
	int LocateElem(const T& e); //按元素值查找其在顺序表中第一次出现的位置

	void DispList(); //输出顺序表中的所有元素
	int ListLength(); //获取顺序表长度
	void ReverseList();  //翻转顺序表

private:
	void IncreaseSize();  //当顺序存满数据后可以调用此函数为顺序表扩容

private:
	T* m_data;  //存放顺序表中的元素
	int m_length;  //顺序表中当前实际长度(当前有几个元素)
	int  m_maxsize; //动态数组最大容量
};

 顺序表的初始化和释放操作

//通过构造函数对顺序表进行初始化
	template <typename T>
	SeqList<T>::SeqList(int length)
	{
		m_data = new T[length]; //为一维数组动态分配内存
		m_length = 0; //顺序表当前实际长度为0,表示还未向其中存入任何数据元素
		m_maxsize = length; //顺序表最多可以存储m_maxsize个数据元素
	}

	//通过析构函数对顺序表进行资源释放
	template <typename T>
	SeqList<T>::~SeqList()
	{
		delete[] m_data;
		m_data = nullptr;
		m_length = 0; //非必须
	}

 顺序表的常用接口:插入、删除、翻转.......

//在第i个位置(位置编号从1开始)插入指定元素e,时间复杂度O(n),时间开销主要是缘于元素的移动。
	template <typename T>
	bool SeqList<T>::ListInsert(int i, const T& e)
	{
		//如果顺序表已经存满数据,则不允许再插入数据了
		if (m_length >= m_maxsize)
		{
			cout << "顺序表已满,不能再进行插入操作了!" << endl;
			return false;
		}
		//判断插入位置i是否合法,i的和合法值应该是从1到m_length+1之间
		if (i < 1 || i >(m_length + 1))
		{
			cout << "元素" << e << "插入位置" << i << "不合法,合法的位置是1到" << m_length+1 << "之间!" << endl;
			return false;
		}
		//从最后有一个元素 开始向前遍历到要插入新元素的第i个位置,分别将这些位置中原有的元素向后移动一个位置
		 //后移次数平均值=(1+2+3+...+n)/(n+1) = (n(n+1)/2)/(n+1),平均时间复杂度O(n)
		for (int j = m_length; j >= i; --j) //时间复杂度最好是O(1),最坏是O(n)                             
		{
			m_data[j] = m_data[j - 1];
		}
		m_data[i - 1] = e; //在指定位置i处插入元素e,因为数组下标从0开始,所以这里用i-1表示插入位置所对应的数组下标。
		cout << "成功在位置为" << i << "处插入元素" << m_data[i - 1] << "!" << endl;
		m_length++; //表长度+1
		return true;
	}

//删除第i个位置的元素
	template <typename T>
	bool SeqList<T>::ListDelete(int i)
	{
		if (m_length < 1)
		{
			cout << "当前顺序表为空,不能删除任何数据!" << endl;
			return false;
		}
		if(i < 1 || i > m_length)
		{
			cout << "删除的位置" << i << "不合法,合法的位置是1到" << m_length << "之间!" << endl;
			return false;
		}
		cout << "成功删除位置为" << i << "的元素,该元素的值为" << m_data[i-1] << "!" << endl;
		//从数组中第i+1个位置开始向后遍历所有元素,分别将这些位置中原有的元素向前移动一个位置
		for (int j = i; j < m_length; ++j)
		{
			m_data[j - 1] = m_data[j];
		}
		m_length--; //实际表长-1
		return true;
	}

	//获取第i个位置的元素值
	template <typename T>
	bool SeqList<T>::GetElem(int i, T& e) //参数e是引用类型参数,确保将该值带回调用者
	{
		if (m_length < 1)
		{
			cout << "当前顺序表为空,不能获取任何数据!" << endl;
			return false;
		}
		if (i < 1 || i > m_length)
		{
			cout << "获取元素的位置" << i << "不合法,合法的位置是1到" << m_length << "之间!" << endl;
			return false;
		}
		e = m_data[i - 1];
		cout << "成功获取位置为" << i << "的元素,该元素的值为" << m_data[i - 1] << "!" << endl;
		return true;
	}
	//按元素值查找其在顺序表中第一次出现的位置
	template <typename T>
	int SeqList<T>::LocateElem(const  T& e)
	{
		for (int i = 0; i < m_length; ++i)
		{
			if (m_data[i] == e)
			{
				cout << "值为" << e << "的元素在顺序表中第一次出现的位置为" << i + 1 << "!" << endl;
				return i + 1; //返回位置应该用数组下标值+1
			}
		}
		cout << "值为" << e << "的元素在顺序表中没有找到!" << endl;
		return -1; //返回-1表示查找失败
	}

	template <typename T>
	void  SeqList<T>::DispList()
	{
		for (int i = 0; i < m_length; ++i)
		{
			cout << m_data[i] << " "; //每个数据之间以空格分隔
		}
		cout << endl; //换行
	}

	//获取顺序表的长度
	template <typename T>
	int SeqList<T>::ListLength()
	{
		return m_length;
	}

	//翻转顺序表,时间复杂度为O(n)
	template <typename T>
	void  SeqList<T>::ReverseList()
	{
		if (m_length <= 1)
		{
			//如果顺序表中没有元素或者只有一个元素,那么就不用做任何操作
			return;
		}
		T temp;
		for (int i = 0; i < m_length / 2; ++i)
		{
			temp = m_data[i];
			m_data[i] = m_data[m_length - i - 1];
			m_data[m_length - i - 1] = temp;
		}
	}

	//当顺序表存满数据后可以调用此函数为顺序表扩容,时间复杂度O(n)
	template <typename T>
	void  SeqList<T>::IncreaseSize()
	{
		T* p = m_data;
		m_data = new T[m_maxsize + IncSize]; //重新为顺序表分配更大的内存空间
		for (int i = 0; i < m_length; ++i)
		{
			m_data[i] = p[i]; //将数据复制到新区域
		}
		m_maxsize = m_maxsize + IncSize; //顺序表最大长度增加IncSize
		delete[] p; //释放原来的内存空间
	}

顺序表的特点

  • 通过下标访问数据元素的时间复杂度仅为O(1);
  •  存储的数据紧凑,无须为维持表中的元素之间的前后关系而增加额外的存储空间。
  •  插入和删除操作可能会移动大量元素导致这两个动作效率不高。
  •  需要大片连续的内存空间来存储数据。
  •  扩容操作所扩展的空间大小不好确定。

    STL中基于数组的容器——vector——reserve-capacity。预留空间(m_maxsize)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心之所向便是光v

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值