顺序表(Sequential List)
1. 顺序表的概念
1.1 顺序表的定义
- 顺序表是线性表基于一维数组的顺序存储表示。
1.2 顺序表中各元素的逻辑及存储关系
- 顺序表是按照顺序存储方式存储的线性表, 把线性表中的所有表项按照其逻辑顺序依次存储到从计算机存储中指定存储位置开始的一块连续的存储空间中。
- 顺序表各个表项的逻辑顺序与其存放的物理顺序一致,即第i个表项存储于第i个物理位置(1<=i<=n)。
- 由于顺序表是依次存放的,只要知道了该顺序表的首地址以及每个数据元素所占用的存储长度,就很容易计算出任何一个数据元素(也就是数据结点)的位置。
- 顺序表可以用C++的一维数组来实现。C++的一维数组可以是静态分配的,也可以是动态分配的。
- 对顺序表中所有表项,既可以进行顺序访问,也可以进行随机访问。也就是说,既可以从表的第一个表项开始逐个访问表项,也可以按照表项的序号(亦称为下标)直接访问表项。
顺序表中各元素的逻辑及存储关系示意图:
下标位置 0 1 2 …… i-1 i …… n-1 …… maxSize 顺序表(即数组)存储空间 a1 a2 a3 …… ai a(i+1) …… an …… a(maxSize-1) - 注:假设顺序表A的起始存储位置为Loc(1),即数组中第0个元素位置。第i个表项的存储位置为Loc(i),则有Loc(i)=Loc(1)+(i-1)*sizeof(T)。
2. 顺序表的实现
注:描述顺序表的存储表示有两种方式:静态方式和动态方式。
2.1 顺序表的静态存储
- 顺序表的静态存储:存储数组的大小和空间事先已经固定分配,一旦数据空间占满,再加入新的数据就将产生溢出,此时存储空间不能扩充,就会导致程序停止工作。
顺序表静态存储的实现:
#include <iostream> using namespace std; const int maxSize = 10; template <class T> struct SeqList { T data[maxSize]; //存储数组 int last; //当前已存表项的最后位置(初值为-1) }; int main() { SeqList<int> seq_list; for (int i = 0; i < maxSize; i++) { seq_list.data[i] = i + 1; seq_list.last = i; } for (int i = 0; i < seq_list.last + 1; i++) { cout <<"data[" << i << "] = " << seq_list.data[i] << endl; } system("pause"); return 0; }
2.2 顺序表的动态存储
- 顺序表的动态存储:存储数组的空间是在程序执行过程中通过动态存储分配的语句分配的,一旦数据空间占满,可以另外再分配一块更大的存储空间,用以代换原来的存储空间,从而达到扩充存储数组空间的目的,同时将表示数组大小的常量maxSize放在顺序表的结构内定义,可以动态地记录扩充后数组空间的大小,进一步提高了结构的灵活性。
顺序表动态存储的实现:
(1)顺序表的类定义及其操作的实现:SeqList.h
注:一般顺序表的类都是用泛型编程的,也就是常说的模板。模板类一般都是将声明与实现都写在头文件。否则会编译出错,出错原因是因为当编译器编译SeqList.h的时候,肯定还没有类似SeqList的声明,当然不会去.cpp里面找函数,因此出链接错误。#ifndef SEQ_LIST_H_ #define SEQ_LIST_H_ #include "LinearList.h" #include <iostream> #include <string> #include <strstream> using namespace std; const int defaultSize = 100; template <class T> class SeqList : public LinearList<T> { public: SeqList(int sz = defaultSize); //构造函数 SeqList(const SeqList<T>& L); //拷贝构造函数 virtual ~SeqList(); //析构函数 public: virtual int Size()const; //获取最大表项总数 virtual int Length()const; //计算实际表项总数 virtual int Search(const T& x)const; //搜索数据值为x的表项并返回表项序号 virtual int Locate(int i)const; //获取第i个表项并返回表项序号 virtual bool GetData(int i, T& x)const; //获取第i个表项的数据值保存至x,并返回获取成功与否 virtual bool SetData(int i, const T& x); //修改第i个表项的数据值为x virtual bool Insert(int i, const T& x); //在第i个表项后插入数据值为x的新表项 virtual bool Remove(int i, T& x); //删除第i个表项,并将被删表项的数据值保存至x virtual bool IsEmpty()const; //判断表是否为空 virtual bool IsFull()const; //判断表是否为满 virtual void Sort(); //表排序——冒泡排序 virtual void InputFront(); //前插法建立顺序表 virtual void InputRear(); //后插法建立顺序表 virtual void Output()const; //输出所有表项的数据值 virtual void Reverse(); //顺序表逆置 virtual void MakeEmpty(); //清空顺序表 virtual SeqList<T>& operator=(const SeqList<T>& L); //顺序表间赋值操作——重载等号运算符 public: bool ReSize(int newSize); //改变顺序表的最大表项总数 public: static bool IsNumber(const string& s_num); //判断输入的字符串每个字符是否都是数值0~9 static T StrToTtype(const string& s_num); //类型转换——将string型转为模板类型T public: T *data; //存储数组 int maxSize; //最大可容纳表项的项数 int last; //当前已存表项的最后位置(初值为-1) }; //构造函数 template<class T> SeqList<T>::SeqList(int sz) { cout << "$ 执行构造函数" << endl; if (sz >= 0) { maxSize = sz; last = -1; data = new T[maxSize]; } } //拷贝构造函数 template<class T> SeqList<T>::SeqList(const SeqList<T>& L) { cout << "$ 执行拷贝构造函数" << endl; T value; maxSize = L.Size(); last = L.Length() - 1; data = new T[maxSize]; for (int i = 1; i <= last + 1; i++) { L.GetData(i, value); data[i - 1] = value; } } //析构函数 template<class T> SeqList<T>::~SeqList() { cout << "$ 执行析构函数" << endl; delete[] data; data = NULL; } //获取最大表项总数 template<class T> int SeqList<T>::Size()const { return maxSize; } //计算实际表项总数 template<class T> int SeqList<T>::Length()const { return last + 1; } //搜索数据值为x的表项并返回表项序号 template<class T> int SeqList<T>::Search(const T& x)const { for (int i = 0; i <= last; i++) { if (data[i] == x) { return i + 1; } } return 0; } //获取第i个表项并返回表项序号 template<class T> int SeqList<T>::Locate(int i)const { if ((i >= 1) && (i <= last + 1)) { return i; } return 0; } //获取第i个表项的数据值保存至x,并返回获取成功与否 template<class T> bool SeqList<T>::GetData(int i, T& x)const { if ((i >= 1) && (i <= last + 1)) { x = data[i - 1]; return true; } return false; } //修改第i个表项的数据值为x template<class T> bool SeqList<T>::SetData(int i, const T& x) { if ((i >= 1) && (i <= last + 1)) { data[i - 1] = x; return true; } return false; } //在第i个表项后插入数据值为x的新表项 template<class T> bool SeqList<T>::Insert(int i, const T& x) { if ((last == maxSize - 1) || (i < 0) || (i > last+1)) { return false; } for (int j = last; j >= i; j--) { data[j + 1] = data[j]; } data[i] = x; last++; return true; } //删除第i个表项,并将被删表项的数据值保存至x template<class T> bool SeqList<T>::Remove(int i, T& x) { if ((-1 == last) || (i < 1) || (i > last + 1)) { return false; } x = data[i - 1]; for (int j = i; j <= last; j++) { data[j - 1] = data[j]; } last--; return true; } //判断表是否为空 template<class T> bool SeqList<T>::IsEmpty()const { return (-1 == last) ? true : false; } //判断表是否为满 template<class T> bool SeqList<T>::IsFull()const { return (maxSize - 1 == last) ? true : false; } //表排序——冒泡排序 template<class T> void SeqList<T>::Sort() { for (int i = 0; i < last; i++) { for (int j = 0; j < last - i; j++) { if (data[j] < data[j + 1]) { T temp = data[j]; data[j] = data[j + 1]; data[j + 1] = temp; } } } } //前插法建立顺序表 template<class T> void SeqList<T>::InputFront() { if (0 == maxSize) { cout << "maxSize = " << maxSize << "无法添加元素,请重新设置最大表项总数" << endl; return; } MakeEmpty(); cout << "* 前插法建立顺序表,请输入实际的元素个数:"; string s_last; cin >> s_last; last = atoi(s_last.c_str()); last--; while ((false == IsNumber(s_last)) || (last > maxSize - 1) || (last <= -1)) { cout << "* 输入错误,请重新输入:"; cin >> s_last; last = atoi(s_last.c_str()); last--; } for (int i = last; i >= 0; i--) { cout << "input->data[" << i << "] = "; string s_data; cin >> s_data; data[i] = StrToTtype(s_data); } } //后插法建立顺序表 template<class T> void SeqList<T>::InputRear() { if (0 == maxSize) { cout << "maxSize = " << maxSize << "无法添加元素,请重新设置最大表项总数" << endl; return; } MakeEmpty(); cout << "* 后插法建立顺序表,请输入实际的元素个数:"; string s_last; cin >> s_last; last = atoi(s_last.c_str()); last--; while ((false == IsNumber(s_last)) || (last > maxSize - 1) || (last <= -1)) { cout << "* 输入错误,请重新输入:"; cin >> s_last; last = atoi(s_last.c_str()); last--; } for (int i = 0; i <= last; i++) { cout << "input->data[" << i << "] = "; string s_data; cin >> s_data; data[i] = StrToTtype(s_data); } } //输出所有表项的数据值 template<class T> void SeqList<T>::Output()const { for (int i = 0; i <= last; i++) { cout << "output->data[" << i << "] = " << data[i] << endl; } } //顺序表逆置 template<class T> void SeqList<T>::Reverse() { for (int i = 0; i < (last+1)/2; i++) { T temp = data[i]; data[i] = data[last - i]; data[last - i] = temp; } } //清空顺序表 template<class T> void SeqList<T>::MakeEmpty() { delete[] data; last = -1; data = new T[maxSize]; } //顺序表间赋值操作——重载等号运算符 template<class T> SeqList<T>& SeqList<T>::operator=(const SeqList<T>& L) { cout << "$ 执行赋值操作函数" << endl; T value; maxSize = L.Size(); last = L.Length() - 1; data = new T[maxSize]; for (int i = 1; i <= last + 1; i++) { L.GetData(i, value); data[i - 1] = value; } return *this; } //改变顺序表的最大表项总数 template<class T> bool SeqList<T>::ReSize(int newSize) { if (newSize < last + 1) { cout << "* 无效的数组大小" << endl; return false; } T *newarray = new T[newSize]; for (int i = 0; i <= last; i++) { newarray[i] = data[i]; } delete[] data; data = newarray; maxSize = newSize; return true; } //判断输入的字符串每个字符是否都是数值0~9 template <class T> bool SeqList<T>::IsNumber(const string& s_num) { for (size_t i = 0; i < s_num.size(); i++) { if ((s_num[i] < '0') || (s_num[i] > '9')) { return false; } } return true; } //类型转换——将string型转为模板类型T template <class T> T SeqList<T>::StrToTtype(const string& s_num) { T n_num; strstream ss_num; ss_num << s_num; ss_num >> n_num; return n_num; } #endif /* SEQ_LIST_H_ */
(3)主函数(main函数)的实现:main.cpp
#include "SeqList.h" #define EXIT 0 //退出 #define CONSTRUCT_COPY 1 //拷贝构造顺序表 #define SIZE 2 //获取最大表项总数 #define LENGTH 3 //计算实际表项总数 #define SEARCH 4 //搜索数据值为x的表项并返回表项序号 #define LOCATE 5 //获取第i个表项并返回表项序号 #define GETDATA 6 //获取第i个表项的数据值保存至x,并返回获取成功与否 #define SETDATA 7 //修改第i个表项的数据值为x #define INSERT 8 //在第i个表项后插入数据值为x的新表项 #define REMOVE 9 //删除第i个表项,并将被删表项的数据值保存至x #define ISEMPTY 10 //判断表是否为空 #define ISFULL 11 //判断表是否为满 #define SORT 12 //表排序——冒泡排序 #define INPUTFRONT 13 //前插法建立顺序表 #define INPUTREAR 14 //后插法建立顺序表 #define OUTPUT 15 //输出所有表项的数据值 #define REVERSE 16 //顺序表逆置 #define MAKEEMPTY 17 //清空顺序表 #define OPERATOR_COPY 18 //顺序表间赋值操作——重载等号运算符 #define RESIZE 19 //改变顺序表的最大表项总数 void print_description() { cout << "------------------------------>顺序表<------------------------------" << endl; cout << "功能选项说明:" << endl; cout << "#0: 退出" << endl; cout << "#1: 拷贝构造顺序表" << endl; cout << "#2: 获取最大表项总数" << endl; cout << "#3: 计算实际表项总数" << endl; cout << "#4: 搜索数据值为x的表项并返回表项序号" << endl; cout << "#5: 获取第i个表项并返回表项序号" << endl; cout << "#6: 获取第i个表项的数据值保存至x,并返回获取成功与否" << endl; cout << "#7: 修改第i个表项的数据值为x" << endl; cout << "#8: 在第i个表项后插入数据值为x的新表项" << endl; cout << "#9: 删除第i个表项,并将被删表项的数据值保存至x" << endl; cout << "#10:判断表是否为空" << endl; cout << "#11:判断表是否为满" << endl; cout << "#12:表排序——冒泡排序" << endl; cout << "#13:前插法建立顺序表" << endl; cout << "#14:后插法建立顺序表" << endl; cout << "#15:输出所有表项的数据值" << endl; cout << "#16:顺序表逆置" << endl; cout << "#17:清空顺序表" << endl; cout << "#18:顺序表间赋值操作——重载等号运算符" << endl; cout << "#19:改变顺序表的最大表项总数" << endl; cout << "--------------------------------------------------------------------" << endl; } //输入数组的最大长度 template <class T> int get_maxsize() { cout << "> 请输入数组的最大长度,maxsize = "; string s_maxsize; cin >> s_maxsize; while (false == SeqList<T>::IsNumber(s_maxsize)) { cout << "* 输入有误,请重新输入:"; cin >> s_maxsize; } return atoi(s_maxsize.c_str()); } //输入结点编号 template <class T> int get_item() { cout << "> 请输入结点编号,item = "; string s_item; cin >> s_item; while (false == SeqList<T>::IsNumber(s_item)) { cout << "* 输入有误,请重新输入:"; cin >> s_item; } return atoi(s_item.c_str()); } //输入数据值 template <class T> T get_data() { cout << "> 请输入数据值,data = "; string s_data; cin >> s_data; return SeqList<T>::StrToTtype(s_data); } //构造顺序表 template <class T> SeqList<T>* construct_seqlist() { cout << "\n==> 创建顺序表" << endl; int n_maxsize = get_maxsize<T>(); SeqList<T> *seqList = new SeqList<T>(n_maxsize); return seqList; } //析构顺序表 template <class T> void destory_seqlist(SeqList<T>* seqList) { cout << "\n==> 释放顺序表在堆中申请的空间,并将指向该空间的指针变量置为空" << endl; delete seqList; seqList = NULL; } //拷贝构造顺序表 template <class T> void construct_copy(SeqList<T>* seqList) { cout << "$ 执行拷贝构造顺序表函数" << endl; SeqList<T> cpy_seqlist = *seqList; cpy_seqlist.Output(); } //获取最大表项总数 template <class T> void size(SeqList<T>* seqList) { cout << "$ 执行获取最大表项总数函数,MaxSize = " << seqList->Size() << endl; } //计算实际表项总数 template <class T> void length(SeqList<T>* seqList) { cout << "$ 执行计算实际表项总数函数,Length = " << seqList->Length() << endl; } //搜索数据值为x的表项并返回表项序号 template <class T> void search(SeqList<T>* seqList) { cout << "$ 执行搜索数据值为x的表项并返回表项序号函数" << endl; T data = get_data<T>(); int n_item = seqList->Search(data); if (0 == n_item) { cout << "* 搜索失败" << endl; return; } cout << "* 搜索成功,item = " << n_item << ",data = " << data << endl; } //获取第i个表项并返回表项序号 template <class T> void locate(SeqList<T>* seqList) { cout << "$ 执行获取第i个表项并返回表项序号函数" << endl; int n_item = get_item<T>(); int locate_i = seqList->Locate(n_item); if (0 == locate_i) { cout << "* 获取失败" << endl; return; } cout << "* 获取成功,item = " << locate_i << endl; } //获取第i个表项的数据值保存至x,并返回获取成功与否 template <class T> void getdata(SeqList<T>* seqList) { cout << "$ 执行获取第i个表项的数据值保存至x并返回获取成功与否函数" << endl; T data; int n_item = get_item<T>(); if (false == seqList->GetData(n_item, data)) { cout << "* 获取失败" << endl; return; } cout << "* 获取成功,item = " << n_item << ",data = " << data << endl; } //修改第i个表项的数据值为x template <class T> void setdata(SeqList<T>* seqList) { cout << "$ 执行修改第i个表项的数据值为x函数" << endl; int n_item = get_item<T>(); T data = get_data<T>(); if (false == seqList->SetData(n_item, data)) { cout << "* 修改失败" << endl; return; } cout << "* 修改成功,item = " << n_item << ",data = " << data << endl; } //在第i个表项后插入数据值为x的新表项 template <class T> void insert(SeqList<T>* seqList) { cout << "$ 执行在第i个表项后插入数据值为x的新表项函数" << endl; int n_item = get_item<T>(); T data = get_data<T>(); if (false == seqList->Insert(n_item, data)) { cout << "* 插入失败" << endl; return; } cout << "* 插入成功,item+1 = " << n_item+1 << ",data = " << data << endl; } //删除第i个表项,并将被删表项的数据值保存至x template <class T> void remove(SeqList<T>* seqList) { cout << "$ 执行删除第i个表项并将被删表项的数据值保存至x函数" << endl; T data; int n_item = get_item<T>(); if (false == seqList->Remove(n_item, data)) { cout << "* 删除失败" << endl; return; } cout << "* 删除成功,item = " << n_item << ",data = " << data << endl; } //判断表是否为空 template <class T> void isempty(SeqList<T>* seqList) { cout << "$ 执行判断表是否为空函数,IsEmpty = " << seqList->IsEmpty() << endl; } //判断表是否为满 template <class T> void isfull(SeqList<T>* seqList) { cout << "$ 执行判断表是否为满函数,IsFull = " << seqList->IsFull() << endl; } //表排序——冒泡排序 template <class T> void sort(SeqList<T>* seqList) { cout << "$ 执行表排序——冒泡排序函数" << endl; seqList->Sort(); } //前插法建立顺序表 template <class T> void inputfront(SeqList<T>* seqList) { cout << "$ 执行前插法建立顺序表函数" << endl; seqList->InputFront(); } //后插法建立顺序表 template <class T> void inputrear(SeqList<T>* seqList) { cout << "$ 执行后插法建立顺序表函数" << endl; seqList->InputRear(); } //输出所有表项的数据值 template <class T> void output(SeqList<T>* seqList) { cout << "$ 执行输出所有表项的数据值函数" << endl; seqList->Output(); } //顺序表逆置 template <class T> void reverse(SeqList<T>* seqList) { cout << "$ 执行顺序表逆置函数" << endl; seqList->Reverse(); } //清空顺序表 template <class T> void make_empty(SeqList<T>* seqList) { cout << "$ 执行清空顺序表函数" << endl; seqList->MakeEmpty(); } //顺序表间赋值操作——重载等号运算符 template <class T> void operator_copy(SeqList<T>* seqList) { cout << "$ 执行顺序表间赋值操作——重载等号运算符函数" << endl; SeqList<T> cpy_seqlist; cpy_seqlist = *seqList;//或cpy_seqlist.operator=(*seqList); cpy_seqlist.Output(); } //改变顺序表的最大表项总数 template <class T> void resize(SeqList<T>* seqList) { cout << "$ 执行改变顺序表的最大表项总数函数" << endl; int n_newsize = get_maxsize<T>(); if (false == seqList->ReSize(n_newsize)) { cout << "* 改变顺序表的最大表项总数失败" << endl; return; } cout << "* 改变顺序表的最大表项总数成功" << endl; } //顺序表操作选择 template <class T> void select_operation(SeqList<T>* seqList) { if (NULL == seqList) { cout << "* 没有构造顺序表,请先构造顺序表。" << endl; return; } string s_operation; while (s_operation != "0") { cout << "\n==> 请输入功能选项编号(按\"0\"退出程序):"; cin >> s_operation; while (false == SeqList<T>::IsNumber(s_operation)) { cout << "* 输入有误,请重新输入:"; cin >> s_operation; } int n_operation = atoi(s_operation.c_str()); switch (n_operation) { case EXIT://退出 { cout << "$ 退出程序" << endl; break; } case CONSTRUCT_COPY://拷贝构造顺序表 { construct_copy(seqList); break; } case SIZE://获取最大表项总数 { size(seqList); break; } case LENGTH://计算实际表项总数 { length(seqList); break; } case SEARCH://搜索数据值为x的表项并返回表项序号 { search(seqList); break; } case LOCATE://获取第i个表项并返回表项序号 { locate(seqList); break; } case GETDATA://获取第i个表项的数据值保存至x,并返回获取成功与否 { getdata(seqList); break; } case SETDATA://修改第i个表项的数据值为x { setdata(seqList); break; } case INSERT://在第i个表项后插入数据值为x的新表项 { insert(seqList); break; } case REMOVE://删除第i个表项,并将被删表项的数据值保存至x { remove(seqList); break; } case ISEMPTY://判断表是否为空 { isempty(seqList); break; } case ISFULL://判断表是否为满 { isfull(seqList); break; } case SORT://表排序——冒泡排序 { sort(seqList); break; } case INPUTFRONT://前插法建立顺序表 { inputfront(seqList); break; } case INPUTREAR://后插法建立顺序表 { inputrear(seqList); break; } case OUTPUT://输出所有表项的数据值 { output(seqList); break; } case REVERSE://顺序表逆置 { reverse(seqList); break; } case MAKEEMPTY://清空顺序表 { make_empty(seqList); break; } case OPERATOR_COPY://顺序表间赋值操作——重载等号运算符 { operator_copy(seqList); break; } case RESIZE://改变顺序表的最大表项总数 { resize(seqList); break; } default: { cout << "* 请输入正确的功能选项编号" << endl; break; } } } } int main(int argc, char* argv[]) { print_description(); SeqList<int> *seqList = construct_seqlist<int>(); select_operation(seqList); destory_seqlist(seqList); system("pause"); return 0; }
3. 顺序表的优缺点
3.1 优点
- 存储密度高,存储效率高,存取速度快,可以随机存取结点。
3.2 缺点
- 长度为定值,中途不易扩充。
- 插入或删除需要移动结点,修改效率低。
- 注:顺序表的所有操作实现中,最复杂、最耗时的就是搜索、插入和删除。搜索算法的时间代价用数据比较次数来衡量。插入和删除算法的时间代价用循环内的数据移动次数来衡量。
3.3 顺序表的适用情况
- 适用于需要大量访问元素而较少搜索,增添和删除元素,及可预先确定表的大小的程序。
参考文献:
[1]《数据结构(用面向对象方法与C++语言描述)(第2版)》殷人昆——第二章
[2]《C/C++常用算法手册》秦姣华、向旭宇——第二章
[3] 百度搜索关键字:顺序表的优缺点