2.1 线性表的逻辑结构
2.1.1 线性表的定义
线性表简称表,是n(n>=0)个具有相同类型的数据元素的有限序列,线性表中数据元素的个数称为线性表的长度。长度等于零时称为空表,一个非空表通常记为:L=(a1,a2,......,an) 其中,ai(1>=i<=ni)称为数据元素,下脚标i表示该元素在线性表中的位置或序号,称元素ai位于表的第i个位置,或称ai是表中的第i个元素。
2.1.2 线性表的抽象数据类型定义
2.2 线性表的顺序存储结构及实现
2.2.1线性表的顺序存储结构-----顺序表
线性表的顺序存储结构称为顺序表。
顺序表是用一段地址连续的存储单元依次存储线性表的数据元素。
C++中数组的下标是从0开1始的,而线性表中元素的序号是从1开始的,也就是说,线性表中第i个元素存储在数组中下标为i-1的位置。
线性表中数据元素的存储地址是其序号的线性函数,只要确定了存储顺序表的起始地址,计算任意一个元素的存储地址的时间是相等的,具有这一特点的存储结构称为随机存取。
2.2.2 顺序表的实现
顺序表的声明:
const int MaxSize=100;
template <class DataType> //模板类
class SeqList
{
public:
SeqList( ) ; //构造函数
SeqList(DataType a[ ], int n);
~SeqList( ) ; //析构函数
int Length( );
DataType Get(int i);
intLocate(DataType x );
void Insert(int i, DataType x);
DataType Delete(int i);
private:
DataType data[MaxSize];
int length;
};
算法设计的一般步骤:
第一步:确定入口(已知条件)、出口(结果);
第二步:根据一个小实例画出示意图;
第三步:① 正向思维:选定一个思考问题的起点,逐步提出问题、解决问题;② 逆向思维:从结论出发分析为达到这个结论应该先有什么;③正逆结合;
第四步:写出顶层较抽象算法,分析边界情况;
第五步:验证第四步的算法;
第六步:写出具体算法;
第七步:进一步验证,手工运行
1.构造函数
template <class DataType>
SeqList<DataType>::SeqList(DataType a[ ], int n)
{
if (n > MaxSize) throw "参数非法";
for (i = 0; i < n; i+ +)
data[i] = a[i];
length = n;
}
2.按位查找算法
template <class DataType>
DataTypeSeqList<DataType>::Get( inti )
{
if (i >= 1 && i <= length)return a[i-1];
}
3.按值查找
template <class DataType>
intSeqList<DataType>::Locate(DataType x)
{
for (i = 0; i < length; i++)
if (data[i] == x) return i + 1;
return 0;
}
4.插入算法
template<class DataType>
void SeqList<DataType>::Insert(inti, DataType x)
{
if (length >= MaxSize) throw "上溢";
if (i < 1 || i > length + 1) throw "位置";
for (j = length; j >= i; j--)
data[j] = data[j-1];
data[i-1] = x;
length++;
}
2.3线性表链接存储结构及实现
2.3.1单链表
1.单链表的存储方法
单链表是用一组任意的存储单元存放线性表的元素。
存储思想:用一组任意的存储单元存放线性表的元素。
存储特点:
(2) 元素之间的逻辑关系用指针表示。
头指针:指向第一个结点的地址。
尾标志:终端结点的指针域为空。
2.单链表的实现
单链表类的声明:
template<class DataType>
classLinkList
{
public:
LinkList( );
LinkList(DataType a[ ], int n);
~LinkList( );
int Length( );
DataType Get(inti);
int Locate(DataType x);
void Insert(inti, DataType x);
DataType Delete(inti);
void PrintList( );
private:
Node<DataType>*first;
};
3.遍历操作
核心操作(关键操作):工作指针后移。
template <class DataType>
void LinkList<DataType> :: PrintList( )
{
p =first->next;
while (p != NULL)
{
cout << p->data;
p = p->next;
}
}
4.按值查找
(1)工作指针p初始化; 累加器count初始化;
(2) 重复执行下述操作,直到p为空:
i工作指针p后移;
iicount++;
(3)返回累加器count的值;
template <class DataType>
intLinkList<DataType>:: Locate(DataType x)
{
p =first->next; count = 1;
while (p != NULL)
{
if (p->data== x) return count; //查找成功,返回序号
p = p->next;
count++;
}
return 0; //退出循环表明查找失败
}
5.插入操作
(1)工作指针p初始化;
(2)查找第i-1个结点并使工作指针p指向该结点;
(3)若查找不成功,则插入位置不合理,抛出插入位置异常;
否则,
i 生成一个元素值为x的新结点s;
ii 将新结点s插入到结点p之后;
template <class DataType>
void LinkList<DataType> :: Insert(inti, DataType x)
{
p = first ; count= 0; //工作指针p应指向头结点
while (p != NULL && count < i - 1) //查找第i – 1个结点
{
p = p->next;
count++;
}
if (p == NULL)throw "位置"; //没有找到第i – 1个结点
else {
s = newNode; s->data = x; //申请一个结点s
s->next =p->next; p->next = s; //结点s插入结点p之后
}
}
6.构造函数
template <class DataType>
LinkList<DataType> :: LinkList(DataType a[ ], int n)
{
first = newNode; //生成头结点
r = first; //尾指针初始化
for (i = 0; i < n; i++)
{
s = new Node;s->data = a[i];
r->next = s; r = s;
}
r->next =NULL;
}
7.删除操作
(1)工作指针p初始化;
(2)查找第i-1个结点并使工作指针p指向该结点;
(3)若p不存在或p不存在后继结点,则抛出位置异常;
否则,
i暂存被删结点和被删元素值;
ii 摘链,将结点p的后继结点从链表上摘下;
iii 释放被删结点;
iiii 返回被删元素值;
算法为:
template <class DataType>
DataTypeLinkList<DataType>:: Delete(inti)
{
p = first ; count= 0;
while (p != NULL && count < i - 1)
{
p = p->next;
count++;
}
if (p == NULL ||p->next == NULL) throw "位置";
else {
q =p->next; x = q->data;
p->next =q->next;
delete q;return x;
}
}
8.析构函数
template <class DataType>
LinkList<DataType> :: ~LinkList( )
{
while (first !=NULL)
{
q =first;
first = first->next;
delete q;
}
}
2.3.2 循环链表
在单链表中,如果将终端结点的指针域由空指针为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为循环单链表,简称循环链表。
2.3.3 双链表
双链表:在单链表的每个结点中再设置一个指向其前驱结点的指针域。
定义结点结构:
template <class DataType>
structDulNode
{
DataType data;
DulNode<DataType> *prior, *next;
};
2.4顺序表和链表的比较
结论:
⑴若线性表需频繁查找却很少进行插入和删除操作,或其操作和元素在表中的位置密切相关时,宜采用顺序表作为存储结构;若线性表需频繁插入和删除时,则宜采用链表做存储结构。
⑵当线性表中元素个数变化较大或者未知时,最好使用链表实现;而如果用户事先知道线性表的大致长度,使用顺序表的空间效率会更高。
总之,线性表的顺序实现和链表实现各有其优缺点,不能笼统地说哪种实现更好,只能根据实际问题的具体需要,并对各方面的优缺点加以综合平衡,才能最终选定比较适宜的实现方法。