数据结构
3.线性表、顺序表
线性表是逻辑结构,顺序表链表是存储结构
3.1 线性表的概念
线性结构的特点:
- 存在唯一一个被称为“第一个”的数据元素;
- 存在唯一一个被称为“最后一个”的数据元素;
- 除“第一个”元素无前驱外,集合中的每个元素均有且只有一个“直接”前驱;除“最后一个”元素无后继外,集合中每个数据元素均有且只有一个“直接”后继。
1.线性表(Linear_List):是具有相同数据类型的n(n≥0)个数据元素的有限序列,通常记为:
(
a
0
,
a
1
,
…
,
a
i
−
1
,
a
i
,
a
i
+
1
,
…
,
a
n
−
1
)
(a_0,a_1,…,a_{i-1},a_i,a_{i+1},…,a_{n-1})
(a0,a1,…,ai−1,ai,ai+1,…,an−1)
2.前驱、后继、表长、空表、首结点、尾结点
3.位序:
a
i
(
0
≤
i
≤
n
−
1
)
a_i(0\leq i \leq n-1)
ai(0≤i≤n−1)是线性表中的元素,则称i为数据元素
a
i
a_i
ai在线性表中的位序(位置)。
线性表存储分类:【顺序表】、【链表】
1、顺序表(用一维数组实现定长的线性存储结构)
按索引值从小到大存放在一片相邻的连续区域
紧凑结构,存储密度为1
2、链表(用指针实现变长的线性存储结构)
单链表
双链表
循环链表
顺序表需要一块连续的存储空间,可以用一个数组实现。保存一个动态数组,需要三个变量:
data:指向线性表元素类型的指针,
curLength:数组中当前存储的元素个数(表长),
maxSize:数组规模(容量)。
初始化顺序表
maxSize=initSize;//参数
data=new elemType[maxSize];//元素类型
curLength=0;//当前表的长度
3.2 顺序表
1.遍历
if(curLength==0) cout<<"empty"<<endl;
else
{
cout<<"output elements:\n";
for(int i=0;i < curlength;i++)//依次访问元素 cout<<data[i]<<" ";
cout<<endl;
}
时间复杂度为0(n),空间复杂度0(1)
2.查找
算法思想:遍历线性表,将线性表中每一个元素依次与value进行比较。若value==data[i](0<=i<curLength)则查找成功,返回data[i]的位序i,否则查找失败返回-1
for(int i=0;i< curlength;i++)
if(value==data[i]) return i;
return -1;
//查找失败返回-1
时间复杂度为0(n),空间复杂度0(1)
3.插入
要求:位置0-n都是合法的,因为元素紧密排列;否则不合法,插入了就不再是顺序表了
设线性表
L
=
(
a
0
,
a
1
,
…
,
a
i
−
1
,
a
i
,
a
i
+
1
,
…
,
a
n
−
1
)
L=(a_0,a_1,…,a_{i-1},a_i,a_{i+1},…,a_{n-1})
L=(a0,a1,…,ai−1,ai,ai+1,…,an−1),
线性表的插入是指在位序为i的位置上插入一个值为value的新元素,使L变为:
(
a
0
,
a
1
,
…
,
a
i
−
1
,
v
a
l
u
e
,
a
i
,
a
i
+
1
,
…
,
a
n
−
1
)
(a_0,a_1,…,a_{i-1},value,a_i,a_{i+1},…,a_{n-1})
(a0,a1,…,ai−1,value,ai,ai+1,…,an−1)
if(i<0 || i>curlength) throw outOfRange();//报错或抛出异常
//合法的插入位置为[0..curLength]
if(curLength==maxSize) resize();//表满,则报错或扩大表
for(int j=curLength;j>i;j--)//[curlength-1..i]范围的元素后移一步
data[j]=data[j-1];
data[i]=value;//将value置入位序为i的位置
++curLength;//表的突际长度增1
时间复杂度为0(n),空间复杂度0(1)
插入运算平均移动数据元素的次数(期望值)为n/2
4.删除
要求:①不能为空表;②删除位置要合法[0,n-1]
设线性表
L
=
(
a
0
,
a
1
,
…
,
a
i
−
1
,
a
i
,
a
i
+
1
,
…
,
a
n
−
1
)
L=(a_0,a_1,…,a_{i-1},a_i,a_{i+1},…,a_{n-1})
L=(a0,a1,…,ai−1,ai,ai+1,…,an−1),
线性表的删除运算是指将表中位序为的元素从线性表中去掉,L变为:
L
=
(
a
0
,
a
1
,
…
,
a
i
−
1
,
a
i
+
1
,
…
,
a
n
−
1
)
L=(a_0,a_1,…,a_{i-1},a_{i+1},…,a_{n-1})
L=(a0,a1,…,ai−1,ai+1,…,an−1)
if(i<0 || i> curlength-1)
throw outOfRange();//抛出异常
//合法的删除位置为[0..curLength-1]
for(int j=i;j < curlength-1;j++)
data[j]=data[j+1]; //[i+1..curlength-1]范围的元素前移一步
--curLength;/表的实际长度减1
时间复杂度为0(n),空间复杂度0(1)
删除运算平均移动数据元素的次数(期望值)为(n-1)/2
5.逆置
线性表 L = ( a 0 , a 1 , … , a i − 1 , a i , a i + 1 , … , a n − 1 ) L=(a_0,a_1,…,a_{i-1},a_i,a_{i+1},…,a_{n-1}) L=(a0,a1,…,ai−1,ai,ai+1,…,an−1),
线性表的逆置是指调整线性表中元素的顺序,使L变为:
(
a
n
−
1
,
a
n
−
2
,
…
,
a
1
,
a
0
)
(a_{n-1},a_{n-2},…,a_1,a_0)
(an−1,an−2,…,a1,a0)。
可用两两交换元素的方法求解,即
a
0
a_0
a0和
a
n
−
1
a_{n-1}
an−1、
a
1
a_1
a1和
a
n
−
2
a_{n-2}
an−2,共进行[n/2]次(向下取整,例如9个元素,除以2后取4)交换。
elemType tmp;
for(int i=0;i<curlength/2;i++){//控制交换的次数
tmp=data[i];
data[i]=data[curLength-i-1];
data[curLength-i-1]=tmp;
}
时间复杂度为O(n),空间复杂度O(1)
顺序表的特点
(1)顺序表需要预先分配存储空间,很难恰当预留空间。
(2)逻辑次序和物理次序的一致性,能够按序号(下标)直接存取元素,具有随机存取的优点;
(3)顺序表在插入删除时需要移动大量的数据,因此其插入和删除效率低;
(4)难于改变顺序表的大小,需要重新创建一个新的顺序表,把原表里的数据复制到新表,然后释放原表空间。
(5)顺序表比较适合静态的、经常做定位访问的线性表。