定义: 线性表是一个线性结构,它是一个含有n≥0个结点的有限序列,对于其中的结点,有且仅有一个开始结点没有前驱但有一个后继结点,有且仅有一个终端结点没有后继但有一个前驱结点,其它的结点都有且仅有一个前驱和一个后继结点。
- 线性表是最常用且简单的一种数据结构。
- 一个线性表是n个数据元素的有限序列。
- 线性表中的数据元素可以是各种各样的,但同一线性表中的元素必定具有相同特性。
- 在复杂的线性表中,一个数据元素可以由若干个数据项组成。这种情况下,常把数据元素称为记录。
- 而含大量记录的线性表称为文件。
在数据元素的非空有限集中,线性结构特点:
- 存在唯一的一个被称作“第一个”的数据元素
- 存在唯一的一个被称作“最后一个”的数据元素
- 除第一个之外,集合中的每个数据元素均只有一个前驱
- 除最后一个之外,集合中每个数据元素均只有一个后继
在上图中,a1是a2的前驱,ai+1 是ai的后继,a1没有前驱,an没有后继 ,n为线性表的长度 ,若n==0时,线性表为空表 ,下图就是一个标准的线性表
线性表分为如下几种:
线性表顺序存储的概念:
- 指的是在内存中用一段地址连续的存储单元依次存储线性表中的数据元素。
顺序存储方式线性表 :
顺序存储方式线性表中,存储位置连续,可以很方便计算各个元素的地址
如每个元素占C个存储单元,那么有: Loc(An) = Loc(An-1) + C,于是有: Loc(An) = Loc(A1)+(i-1)*C;
优点:
- 查询很快 ,时间复杂度O(1)
- 可以快速地取出表中任意位置的元素,时间复杂度O(1)
- 开始存数据的时候很快,不需要为表中元素之间的逻辑关系而增加额外的内存空间,时间复杂度O(1)
缺点:
- 插入和删除效率慢,需要移动大量元素,时间复杂度O(n)。
- 当顺序表长度变化较大时,难以确定存储空间的内容。
- 造成存储空间的碎片
- 另外一点:顺序存储要求明确数据的规模用来分配给数据空间。
顺序表中还有一个最好的情况和最坏的情况,:
- 最好的情况是:如果要插入或者删除的位置是最后一个,那么时间复杂度O(1),不需要移动任何元素。
- 最坏的情况是,如果要删除或者插入的元素是第一个呢,时间复杂度O(n)。
- 这就说明顺序表更适合元素个数不太变化,而更多是存取数据的应用。
采用的实现方式:一段地址连续的存储单元可以用固定数组或者动态存储结构来实现,这里采用动态分配存储结构。
下面是用C++ STL 库中的vector 实现的 顺序表:
#include
#include
#include
#include
#include
using std::vector;
using std::cout;
using std::endl;
using std::cin;
class SqList
{
public:
enum State
{
TT_ERROR = 0,
TT_OK = 1
};
SqList(int maxStorageSpaces, int CurrNumOfStoreEleme);
~SqList() { clear(); }
int insertAt(unsigned pos, int addElem);
int removeAt(unsigned pos);
int getAt(unsigned pos);
void show();
bool isEmpty()const;
void clear();
unsigned getLength()const;
private:
vectorm_list;
};
inline unsigned SqList::getLength()const
{
return m_list.size();
}
inline bool SqList::isEmpty()const
{
return m_list.cbegin() == m_list.cend();
}
inline void SqList::clear()
{
m_list.erase(m_list.begin(), m_list.end());
}
int SqList::getAt(unsigned pos)
{
if (pos <= m_list.size())
{
return *(m_list.begin() + pos - 1);
}
return TT_ERROR;
}
int SqList::insertAt(unsigned pos, int addElem)
{
if (pos >= m_list.size()+1 )
{
m_list.resize(pos);
m_list[pos - 1] = addElem;
return TT_OK;
}
else
{
m_list.insert(m_list.begin() + pos - 1, addElem);
return TT_OK;
}
return TT_ERROR;
}
int SqList::removeAt(unsigned pos )
{
if ( m_list.size() < pos)
{
return TT_ERROR;
}
m_list.erase(m_list.begin() + pos - 1);
return TT_OK;
}
SqList::SqList(int maxStorageSpaces, int CurrNumOfStoreEleme)
{
assert(CurrNumOfStoreEleme != 0);
m_list.reserve(maxStorageSpaces); // 分配 至少能容器 maxStorageSpaces 个元素空间
cout << "请输入需要存储的元素:\n";
int storeEleme = 0;
for (std::size_t i = 0; i != CurrNumOfStoreEleme; ++i)
{
cin >> storeEleme;
m_list.push_back(storeEleme);
}
}
void SqList::show()
{
cout << "输出当前容器中的所有元素:\n";
for (auto beg =m_list.begin();beg!=m_list.end();++beg)
{
cout << *beg << " ";
}
cout << endl;
}
int main()
{
cout << "请输入当前需要存储的元素个数:";
int addElemNum = 0;
while (cin >> addElemNum)
{
if (addElemNum > 0)
{
break;
}
else
{
cout << "请重新输入正确的元素个数,不能小于1:" << endl;
continue;
}
}
cout << "请输入需要存储元素最大的内存空间,不可以小于当前需要存储元素个数:";
int maxStorageSpaces = 0;
while (cin >> maxStorageSpaces)
{
if (maxStorageSpaces >= addElemNum)
{
break;
}
else
{
cout << "输入错误,请重新输入正确的存储元素最大的内存空间,否则无法进行!" << endl;
continue;
}
}
SqList mySqList(maxStorageSpaces, addElemNum);
mySqList.show();
cout << "请输入需要添加的元素值:";
int pushElem = 0;
cin >> pushElem;
cout << "请输入添加的位置:";
unsigned addPos = 0;
while (cin >> addPos)
{
if (addPos > 0 )
{
break;
}
else
{
cout << "请重新输入正确的需要添加的位置,不能小于0!:" << endl;
continue;
}
}
if (mySqList.insertAt(addPos, pushElem))
{
cout << "元素 插入成功!" << endl;
mySqList.show();
}
else
{
cout << "元素 插入失败!" << endl;
}
cout << "请输入你想获取的位置值:";
unsigned getPos = 0;
while (cin >> getPos)
{
if (getPos > 0)
{
break;
}
else
{
cout << "请获取正确的位置,请重新输入!:" << endl;
continue;
}
}
if (auto temp = mySqList.getAt(getPos))
{
cout << "获取的元素值为:" << temp << endl;
mySqList.show();
}
else
cout << "获取失败!" << endl;
cout << "请输入你想要删除的位置:";
unsigned removePos = 0;
while (cin >> removePos)
{
if (removePos > 0)
{
break;
}
else
{
cout << "请输入正确的位置,请重新输入!" << endl;
continue;
}
}
if (mySqList.removeAt(removePos))
{
cout << "元素删除成功!" << endl;
mySqList.show();
}
else
cout << "元素删除失败!" << endl;
system("pause");
return 0;
}
下面用C++ 代码实现的顺序表:
头文件:
#include
#include
#ifndef TT_LIST_H
#define TT_LIST_H
#define LIST_NCREMENT 5 // 顺序表存储空间的分配增量
using namespace std;
namespace tt
{
class SqList
{
public:
using ElemType = int;
using Status = void;
enum State
{
TT_ERROR = 0,
TT_OK = 1
};
public:
SqList(ElemType maxSize);
~SqList();
ElemType insertAt(ElemType i, ElemType elem);
ElemType removeAt(ElemType i, ElemType &elemOut);
ElemType getAt(ElemType i, ElemType &elemOut);
ElemType getIndexElem(const ElemType elem);//查找与e相等的元素,返回第一个与e相等元素在线性表中的下标,否则,返回0
ElemType isEmpty()const;
Status getLength()const;
Status clear();
Status show();
ElemType destroy();
ElemType priorElemAt(ElemType cur_e, ElemType &pri_e);//若cur_e是链表的数据元素,且不是第一个,则用pre_e返回它的前驱
ElemType nextElemAt(ElemType cur_e, ElemType &Nex_e); //若cur_e是链表的数据元素,且不是最后一个,则用next_e返回它的后继,
private:
ElemType *m_data;
ElemType m_length; //线性表当前的元素个数
ElemType m_maxLength; //线性表的最大容量
};
inline SqList::ElemType SqList::isEmpty()const
{
return (m_length == 0);
}
inline SqList::Status SqList::getLength()const
{
cout << "当前的顺序表的长度为:" << m_length << endl;
}
}
#endif //TT_LIST_H
源文件:
#include"MySqList.h"
namespace tt
{
SqList::SqList(ElemType maxSize)
{
assert(maxSize > 0);
m_data = new ElemType[maxSize];
assert(m_data != nullptr);
m_length = 0;
m_maxLength = maxSize;
cout << "********************顺序表初始化成功********************" << "\n" << endl;
}
SqList::~SqList()
{
this->destroy();
}
SqList::ElemType SqList::priorElemAt(ElemType cur_e, ElemType &pri_e)
{
int *p = m_data;
for (int i = 0; i < m_length; i++, p++) //顺序表长度已知,故用for循环
{
if ((i == 0) && (*p == cur_e))
{
return TT_ERROR;
}
if (*p == cur_e) //找到了当前元素且不是第一个元素,
{
pri_e = *--p; //将其前驱赋给引用参数
return TT_OK;
}
}
return TT_ERROR;
}
SqList::ElemType SqList::nextElemAt(ElemType cur_e, ElemType &Nex_e)
{
int i = 1;
int *p = m_data;
while ((i < m_length) && (*p != cur_e))
{
++i;
++p;
}
if (i == m_length)
{
return TT_ERROR;
}
else
{
Nex_e = *++p;
return TT_OK;
}
}
SqList::ElemType SqList::insertAt(ElemType i, ElemType elem) //插入元素, 在线性表中第i个位置之前插入新元素elem
{
if ((i < 1) || (i > m_length + 1)) //插入的位置不合理时,返回错误
{
return TT_ERROR;
}
if ((m_length == m_maxLength)) //表已满
{
/*m_allocateMemory = new ElemType[m_data, (m_maxLength + LIST_NCREMENT) * sizeof(ElemType)]; // 用allocateMemory指针是为了保护m_data
assert(m_allocateMemory != nullptr);
m_maxLength += LIST_NCREMENT; //增加存储容量*/
return TT_ERROR;
}
if (i <= m_length) //若插入的位置不在表尾,否则直接插入新元素,只有插入第11个位置才算是表尾
{
for (int k = m_length; k >= i; --k) /*元素i及其后面的元素都往后移一下以腾出i处的空间存放要插入的元素,*/
m_data[k] = m_data[k - 1]; //假如m_length=10,m_maxSize=20;那么第一次就是把下标为9的元素变成下标为10的元素
//相当于元素向后移,每次循环都是如此,k为为线性表当前长度,每次循环减一
}
m_data[i - 1] = elem; //将新元素插入第i个位置之前
++m_length;
return TT_OK;
}
SqList::ElemType SqList::removeAt(ElemType i, ElemType &elemOut) //删除元素线性表中第i个数据元素,并用elem返回其值,表长减一
{
if ((i < 1) || (i > m_length))/*假如m_length=10,m_maxSize=20;线性表是是第一个位置数,没有第0个位置,
删除的线性表的位置不能大于当前线性表的长度,或者当前没有元素,说白了就是删除位置
不合理*/
{
return TT_ERROR;
}
elemOut = m_data[i - 1]; //把当前下标的元素用elemOut取出
if (i < m_length) //如果要删除的不是最后位置
{
for (int k = i; k < m_length; ++k) //将删除位置后的元素向前移动, 要删除的位置不能大于表长
m_data[k - 1] = m_data[k]; //那么第一次循环就是把下标为10的元素变成下标为9的元素,每次循环向前移动
}
--m_length;
return TT_OK;
}
SqList::ElemType SqList::getAt(ElemType i, ElemType &elemOut) //获得元素的操作,将线性表中第i个位置的元素的值返回给e
{
if ((i<1 )|| (i>m_length)) //当你获取的元素不在线性表中的范围,就错误呗
{
return TT_ERROR;
}
elemOut = m_data[i - 1];
return TT_OK;
}
SqList::ElemType SqList::getIndexElem(const ElemType elem)//在线性表中查找与elem的相等的元素,返回第一个与elem相等元素在线性表中的第几个位置
{
for (int i = 0; i < m_length; ++i) //假如m_length=10,m_maxSize=20;那么下标最大到9
{
if (m_data[i] == elem) /*把elem相等的元素返回,因为线性表是从1开始的,数组的下标是0开始的,于是线性表第i个元素存储在
数组下标i-1的位置,返回的时候要加1*/
return i + 1;
}
return TT_ERROR;
}
SqList::Status SqList::clear()
{
m_length = 0;
if (!m_length )
{
cout << "顺序表已清空!" << endl;
}
else
cout << "顺序表清空失败!" << endl;
}
SqList::Status SqList::show()
{
if (!m_length)
{
cout << "错误!当前线性表没有元素,无法显示!" << endl;
}
else
{
cout << "输出线性表的所有元素为:";
for (int k = 0; k < m_length; ++k)
{
cout << m_data[k]<< " ";
}
cout << endl;
}
}
SqList::ElemType SqList::destroy()
{
delete[] m_data;
m_data = nullptr;
m_length = m_maxLength = 0;
return TT_OK;
}
}
//测试代码
void testMySqList()
{
int sizeCapacity(0);
cout << "输入顺序表的最大容量:";
cin >> sizeCapacity;
tt::SqList mySqList(sizeCapacity);
while (true)
{
{
cout << "*************************************************************" << endl
<< "******************* 顺序表的基本功能展示 *******************" << endl
<< "*****************************************************************" << endl
<< "******************** 选择1——数据插入. **********************" << endl
<< "******************** 选择2——数据删除. **********************" << endl
<< "******************** 选择3——获取元素. **********************" << endl
<< "******************** 选择4——查找元素. **********************" << endl
<< "******************** 选择5——是否为空. **********************" << endl
<< "******************** 选择6——获取表的长度. **********************" << endl
<< "******************** 选择7——清空元素. **********************" << endl
<< "******************** 选择8——显示所有元素. **********************" << endl
<< "********************* 选择9——销毁线性表. ************************" << endl
<< "********************* 选择10——获得元素的前驱. *****************" << endl
<< "********************* 选择11——获得元素的后继. *****************" << endl
<< "******************* 选择12——清屏! ************************" << endl
<< "****************** 选择0——退出程序! ************************" << endl
<< "***********************************************************************" << endl
<< "***********************************************************************" << endl;
}
cout << "\n******************** 请输入你想要使用的顺序表功能的序号 ***************" << endl;
cout << "请输入你的选择:";
int userChoice(0);
cin >> userChoice;
if (!userChoice)
{
cout << "程序已退出,感谢您的使用!" << "\n" << endl;
break;
}
switch (userChoice)
{
case 1:
{
int pushDatas(0); //要插入的元素
int indexLocition(0); //要插入的位置
cout << "请输入你想要插入的元素位置:";
cin >> indexLocition;
cout << "请输入你想要插入的元素:";
cin >> pushDatas;
if (mySqList.insertAt(indexLocition, pushDatas))
{
cout << "数据" << pushDatas << "插入成功!" << "\n" << endl;
mySqList.getLength();
mySqList.show();
}
else
cout << "当前顺序表已满或者插入位置不合理,数据" << pushDatas << "插入失败!" << endl;
break;
}
case 2:
{
int popDatas(0); //要删除的元素
int indexLocition(0); //要删除的位置
cout << "请输入你想要删除的元素的位置:";
cin >> indexLocition;
if (mySqList.removeAt(indexLocition, popDatas))
{
cout << "数据" << popDatas << "删除成功!" << "\n" << endl;
mySqList.getLength();
mySqList.show();
}
else
cout << "当前顺序表为空或者删除位置不合理,数据删除失败!"<< endl;
break;
}
case 3:
{
int getElem(0); //要获取的元素
int indexLocition(0);
cout << "请输入你想要获取元素的位置:";
cin >> indexLocition;
if (mySqList.getAt(indexLocition, getElem))
{
cout << "获取的元素为:" << getElem << "\n" << endl;
mySqList.getLength();
mySqList.show();
}
else
cout << "当前顺序表为空或者获取的位置不合理,数据获取失败!" << endl;
break;
}
case 4:
{
int findElem(0); //查找元素
cout << "请输入你想要查找的元素的值:";
cin >> findElem;
if (int indexLocition = mySqList.getIndexElem(findElem))
{
cout << "所要查找的元素位于表中第" << indexLocition << "个位置" << endl;
mySqList.getLength();
mySqList.show();
}
else
{
cout << "当前表中无此元素" << endl;
mySqList.getLength();
}
break;
}
case 5:
if (mySqList.isEmpty()) //判断是否为空
{
cout << "目前该顺序表为空!" << endl;
mySqList.getLength();
}
else
{
cout << "目前顺序表非空!" << endl;
mySqList.getLength();
mySqList.show();
}
break;
case 6:
mySqList.getLength(); //获取线性表的长度
break;
case 7:
mySqList.clear();//清空所有元素
mySqList.getLength();
break;
case 8:
mySqList.show();//显示所有元素
mySqList.getLength();
break;
case 9:
{
cout << "你确定要销毁该顺序表吗?(若销毁请输入输入(Y/y))";
char yesOrNo;
cin >> yesOrNo;
if ((yesOrNo == 'Y') || (yesOrNo == 'y'))
{
if (mySqList.destroy())
{
cout << "顺序表已被销毁." << "\n" << endl;
}
else
cout << "顺序表没有被销毁." << "\n" << endl;
}
break;
}
case 10:
{
int priorElem(0);
int getElem(0);
cout << "输入你想要获得哪一个元素的前驱?(注意:不能获取第一个元素的):";
cin >> priorElem;
if (mySqList.priorElemAt(priorElem, getElem))
{
cout << "数据元素" << priorElem << "的,前驱元素是:" << getElem << endl;
mySqList.show();
}
else
{
cout << "获取前驱元素失败,不能获取第一个元素的前驱或者链表中没有你输入的元素!" << endl;
mySqList.show();
}
break;
}
case 11:
{
int backElem(0);
int getElem(0);
cout << "输入你想要获得哪一个元素的后继?(注意:不能获取最后一个元素的):";
cin >> backElem;
if (mySqList.nextElemAt(backElem, getElem))
{
cout << "数据元素" << backElem << "的,后继元素是:" << getElem << endl;
mySqList.show();
}
else
{
cout << "获取后继元素失败!不能获取最后一个元素的后继或者链表中没有你输入的元素!" << endl;
mySqList.show();
}
break;
}
case 12:
system("cls");
cout << "屏幕已经清屏,可以重新输入!" << "\n" << endl;
break;
default:
cout << "输入的序号不正确,请重新输入!" << "\n" << endl;
}
}
}
int main()
{
testMySqList();
system("pause");
return 0;
}
下面使用的是C#代码实现的顺序表:
namespace DateStructure
{
interface IListDS
{
int GetLength();
void Clear();
bool IsEmpty();
void AddToTail(T item);
void InsertAt(T item, int pos);
T RemoveAt(int pos);
T GetEle(int pos);
int LocateElem(T value);
void Show();
void CreateList(params T[] arrElem);
}
class MySqList : IListDS
{
private T[] m_data;
private int m_count;
private int m_maxLength;
public MySqList(int maxSize)
{
if(maxSize != 0)
{
m_data = new T[maxSize];
if (m_data == null)
{
return;
}
m_maxLength = maxSize;
m_count = 0;
}
return;
}
public int GetLength()
{
return m_count;
}
public void Clear()
{
m_count = 0;
}
public bool IsEmpty()
{
return m_count == 0;
}
public void AddToTail(T item)
{
if (m_count == m_maxLength) //当前数组已经存满
{
Console.WriteLine("当前顺序表已经存满,不允许再存入");
return;
}
else
{
m_data[m_count] = item;
++m_count;
}
}
public void InsertAt(T item, int pos)
{
if(pos < 1 || pos > m_count + 1) //插入的位置不合理时,返回错误
{
WriteLine("插入的位置不正确,无法插入元素!");
return;
}
if(m_count == m_maxLength)
{
WriteLine("当前的顺序表存储已满,无法插入元素!");
return;
}
if(pos <= m_count) //若插入的位置不在表尾,否则直接插入新元素
{
for (int k = m_count; k >= pos; --k) /*元素 index 及其后面的元素都往后移一下以腾出 index 处的空间存放要插入的元素,*/
{
m_data[k] = m_data[k - 1];
}
}
m_data[pos - 1] = item; //将新元素插入第index个位置之前
++m_count;
}
public T RemoveAt(int pos)
{
if (m_count == 0 || pos < 1 || pos > m_count)
{
WriteLine("删除的位置不合理,或者该顺序表没有元素!");
return default(T);
}
T temp = m_data[pos - 1]; // 把需要删除的元素用临时变量取出
if (pos < m_count) // 若不是直接删除表尾的元素
{
for (int k = pos; k < m_count; ++k)
{
m_data[k - 1] = m_data[k]; // 把数据往前移动
}
}
--m_count;
return temp;
}
public T GetEle(int pos) // 返回 数组中 pos 序号位置的元素
{
if(pos < 1 || m_count == 0 || pos > m_count)
{
WriteLine("获取元素的位置不正确,或者该顺序表为空!");
return default(T);
}
return m_data[pos - 1];
}
public int LocateElem(T value) // 查找一个值是否在数组中,如存在,返回该值在数组中序号
{
if(m_count !=0 )
{
for(var t=0;t!=m_count;++t)
{
if(m_data[t].Equals(value))
{
return t + 1;
}
}
}
return -1;
}
public void Show()
{
Write("输出所有元素:");
for(var t =0; t!=m_count; ++t)
{
Write($"{m_data[t]},");
}
WriteLine();
}
public void CreateList(params T[] arrElem)
{// 只要 arrElem 不等于 null , 并且是有元素的, 并且传入的元素不能超过数组的最大容量
if ((arrElem != null) && (arrElem.Length != 0))
{
for(var t =0; t < arrElem.Length; ++t)
{
m_data[t] = arrElem[t];
++m_count;
}
}
else
{
WriteLine("顺序表整表创建失败!");
}
}
}
class Program
{
static void Main(string[] args)
{
WriteLine("输入顺序表的最大容量:");
int sizeCapacity = Convert.ToInt32(Console.ReadLine()) ;
var myList = new MySqList (sizeCapacity);
const int arraySize = 5;
int[] myIntArray = new int[arraySize] { 0, 1, 2, 3, 4 };
myList.CreateList(myIntArray);
myList.Show();
myList.AddToTail(55);
myList.AddToTail(546);
myList.AddToTail(150);
myList.AddToTail(4110);
myList.Show();
WriteLine($"该元素的位置序号是:{myList.LocateElem(150)}");
WriteLine("请输入你想获取数组中哪一个位置的元素:");
int getArrElem = Convert.ToInt32(Console.ReadLine());
WriteLine($"该元素在数组的位置下标是:{myList.GetEle(getArrElem)}");
WriteLine("请输入你想插入的元素值:");
int pushElem = Convert.ToInt32(Console.ReadLine());
WriteLine("请输入你想插入的位置:");
int pushLocition = Convert.ToInt32(Console.ReadLine());
myList.InsertAt(pushElem, pushLocition);
myList.Show();
WriteLine("请输入你想删除哪一个位置的元素:");
int removePosElem = Convert.ToInt32(Console.ReadLine());
myList.RemoveAt(removePosElem);
myList.Show();
if(myList.IsEmpty())
{
WriteLine("当前的顺序表为空!");
}
else
WriteLine("当前的顺序表非空!");
WriteLine($"获取当前顺序表的总个数:{myList.GetLength()}");
myList.Clear();
WriteLine($"获取当前顺序表的总个数:{myList.GetLength()}");
}
}
}