线性表为n个数据元素的一个有限的序列。线性表的第一个表项称为表头,最后一个表项称为表尾。
线性表是一个有限序列,意味着表中各个表项是相继排列的,且两个相邻表项之间都有直接前驱和直接后继的关系。邻接关系是1对1的。
线性表的存储表示有两种:顺序存储方式和链表存储方式。
顺序表
用顺序存储方式实现的线性表,是用数组作为表的存储结构的。
定义:把线性表中的所有表项按照其逻辑顺序依次存储到从计算机指定存储位置开始的一块连续的存储空间中。
特点:各个表项的逻辑顺序和存放的物理顺序一致;所有表项即可以顺序访问,也可以直接访问。
描述顺序表的存储表示方式:静态方式和动态方式。{
# define maxSize 100;
typedef int T;
typedef struct{//静态存储
T data[maxSize];
int n;
} SeqList;
typedef struct{//动态存储
T *data;
int n;
} SeqList;
顺序表的类声明,利用数组作为顺序表的存储结构。
#include<iostream.h>
#include<stdlib.h>
#include "linearList.h"
const int defaultSize = 100;
template<class T>
class SeqList: public linearList<T>{
protect:
T *data;//存放元素的数组
int maxSize;//最大可容纳的项数
int last;//当前已存表项的最后位置(从0开始)
void reSize(int newSize);//改变data数组空间大小
public:
SeqList(int sz=defaultSize);//构造函数
SeqList(SeqList<T>&L);//复制构造函数,对于初始化对象
~SeqList(){delete[] data;}//析构函数,因为用new进行数组的动态内存分配,所以用delete[]
int Size()const{ return maxSize};//返回表的最大容纳个数
int Length() const{return last+1;}//返回实际表项的个数
int Search(T& x) const;//查找元素x在表中的位置,返回表项序号
int Locate(int i)const;//定位第i个表项,返回表项序号(表项序号就是i,估计是看是否在表的范围内)
bool getData(int i,T&x)const{
if(i>0 && i<=last+1){//去第i个表项的值
x=data[i-1];
return true;}
else
return false;}
void setData(int i, T& x){
if(i>0 && i<=last+1)
data[i-1]=x;}
bool Insert(int i,T& x);//在第i个表项插入x
bool Remove(int i,T& x);//删除第i个表项,通过x返回表项的值
bool IsEmpty(){return (last == -1}? true:false;}
bool IsFull(){return (last == maxSize-1)? true:false;}
void input()
void output()
SeqList<T> operator=(SeqList<T>&L);//表整体赋值
}
顺序表的插入和删除的平均比较次数(ACN)分别是n/2和 (n-1)/2
单链表
表示线性表时,用指针表示结点间的逻辑关系,单链表的一个存储结点包含两个部分:
data:存储数据元素;link:存放一个指针。
链表的第一个结点的地址可以通过链表的头指针first找到。
头指针:链表中的第一个结点的存储位置可以通过头指针找到,即a1的存储位置。
头结点和头指针
头结点:
- 头结点是加在单链表之前附设的。
- 头结点的数据域一般不存储任何信息,也可以存放一些关于线性表的长度的附加信息。
- 头结点的指针域存放指向第一个结点的指针。
- 头结点不一定是链表的必要元素。
头指针:
- 头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。
- 头指针具有标识作用,所以常用头指针冠以链表的名字(指针变量的名字)。
- 无论链表是否为空,头指针均不为空。
- 头指针是链表的必要元素。
单链表的类定义
数据结构设计:用struct定义LinkNode类
struct LinkNode{
int data;
LinkNode *link;
};
class List{
private:
LinkNode *first;//first为私有成员中,使得LinkNode不为外界直接访问
public:
//链表操作
...
};
单链表的插入算法:
bool List::Insert(int i, int& x){//将新元素x插入到第i结点之后。i从1开始,i=0表示插入到第一个结点之前
if(first==NULL || i==0){//插入空表或者非空表第一个结点之前
LinkNode *newNode = new LinkNode(x);
if(newNode == NULL) {cout<<"存储分配错误!\n";exit(1);}
newNode->link = first;
first = newNode;
}else{
LinkNode *current = first;
for(int k=1; k<i; ++k){//找到第i个结点,这里current指向第i个结点
if(current == NULL) break;
else current = current->link;}
if(current == NULL){cout<<"无效的插入位置!\n";return false;}
else{
LinkNode *newNode = new LinkNode(x);
if(newNode == NULL) {cout<<"存储分配错误!\n";exit(1);}
newNode->link = current->link;
current->link = newNode;}
}
return true;
}
单链表的删除算法:
bool List::Remove(int i, int& x){
LinkNode *del,*current;//del保存要删除的结点
if(i==1){del = first; first = first->link; }
else{
current = first;
for(int k=1; k<i-1; ++k){//找到第i-1个结点
if(current==NULL) break;
else current = current->link;}
if(current==NULL||current->link==NULL){cout<<"无效的插入位置!\n";return false;}
del = current->link;//保存需要删除的结点以及进行拉链
current->link = del->link;}
x = del->data;
delete del;
return true;
}
循环链表
表尾结点的link域中存放了一个指向链表开始结点的指针,这样只要知道表中任何一个结点的地址,就能遍历表中其他任一结点。
双向链表
每个结点中应有两个链接指针作为它的数据成员:lLink指示它的前驱结点,rLink指示它的后继结点。
静态链表
为数组中每个元素附加一个链接指针,就形成静态链表结构。允许我们不改变各元素的物理位置,只要重新链接就能够改变这些元素的逻辑顺序。因为用数组定义,所以存储空间大小不会变化,因此称为静态链表。
链表的附加表头在A[0],A[0].link表示第一个访问的结点位置,把A[1]取出来,A[k].link=-1,表示链表终止。
静态链表定义:
const int maxSize = 100;
template<class T>
struct SLinkNode{
T data;
int link;
};
template<class T>
class StaticList{
SlinkNode<T>elem[maxSize];//数组的元素是结构
int avil;//当前可分配空间首地址
public:
void InitList();
int Length();
int Search(T x);
int Locate(int i);
bool Append(T x);
bool Insert(int i, T x);
bool Remove(int i);
bool IsEmpty():
}