n个同类型数据元素的有限序列,记为
L为表名,i为数据元素在线性表中的位序,n为线性表的表长,n=0时称为空表。
直接前驱和直接后继
ADT List{
数据对象:
数据关系:
一个长度为n的线性表有n-1个数据关系
基本操作:
(1)结构初始化操作
(2)结构销毁操作
(3)访问型操作
(4)加工型操作
}ADTList
以x的存储位置和y的存储位置之间某种关系表示逻辑关系<x,y>
最简单的一种顺序存储方式是:
令y的存储位置和x的存储位置相邻。
用一组地址连续的存储单元依次存放线性表中的数据元素
线性表的起始地址称为线性表的基地址
所有数据元素的存储位置均取决于第一个数据元素的存储位置
将顺序表中的元素逐个与给定值e相比较
时间复杂度为
插入位置以及之后的元素后移
时间复杂度为
删除该元素并且该元素之后的元素前移
时间复杂度为
用一组地址任意的存储单元存放线性表中的数据。
结点(表示数据元素的存储)=元素+地址
结点的序列表示线性表——称作链表
为了操作方便,在第一个结点之前虚加一个“头结点”,以指向头结点的指针为链表的头指针。
p=p->next;
时间复杂度为
寻找第i-1个位置
再进行插入
时间复杂度为
寻找第i-1个位置
再进行删除
时间复杂度为
时间复杂度为
链表是一个动态的结构,它不需要预先分配空间,因此生成链表的过程是一个结点“逐个插入”的过程。
1)双向链表
有两个指针,一个指向前驱,一个指向后继。
2)循环链表
最后一个结点的指针域的指针又指回第一个结点的链表。
和单链表的差别仅在于:判别链表中最后一个结点的条件不再是“后继是否为空”,而是“后继是否为头结点”。
3)双向循环链表
“查询”和单链表相同
“插入”和“删除”时需要同时修改两个方向上的指针。
例如“插入”:
s->next=p->next;
p->next=s;
s->next->prior=s;
s->prior=p;
例如“删除”:
s=p->next;
p->next=p->next->next;
p->next->prior=p;
delete s;
顺序表适合查找操作,而链表适合插入和删除操作。
ADT Stack{
数据对象:
数据关系:
约定端为栈顶,端为栈底。
基本操作:
}ADTStack
指向表尾的指针可以作为栈顶指针
链栈中指针的方向是从栈顶元素往下指
void conversion(){
? ? stack <int> s;
? ? int N;
? ? cin>>N;
? ? while(N){
? ? ? ? s.push(N%8);//余数进栈
? ? ? ? N/=8;
? ? }
? ? while(!s.empty()){
? ? ? ? cout<<s.top();
? ? ? ? s.pop();
? ? }
? ? cout<<endl;
}
表达式由操作数、运算符、界限符组成。
操作数(operand):常数或变量
运算符(operator):算数、关系、逻辑等
界限符(delimiter):左右括号、表达式结束符#等
运算符优先表
算法思想:
使用两个栈,一个运算符栈(OPTR)一个操作数栈(OPND)
1)OPND置为空栈,将#放入OPTR栈
2)依次读入表达式中的每个字符
3)若是操作数,读入OPND栈,若是运算符,则和OPTR栈的栈顶元素进行优先级比较,若栈顶元素优先级高,则执行相应运算,结果存入OPND栈;若栈顶元素优先级低,则将该字符读入OPTR栈;若优先级相同,则做‘##’或‘()’处理。
4)重复上述操作,直到表达式求值完毕(读入的是‘#’,并且OPTR栈顶元素也为‘#’)
回溯法:一种不断试探且及时纠正错误的搜索方法。
回溯法思想:从入口出发,按某一方向向前探索,若能走通(未走过),则到达新点,否则试探下一没有探索过的方向,若所有的方向均没有通路,则沿原点返回前一点,换下一个方向再继续试探,直到所有可能的通路都探索过,或找到一条通路,或无路可走又返回到入口点。
#define M 6? ? ? ? //迷宫的实际行和列
#define N 8
int maze[M+2][N+2];
入口坐标为(1,1),出口坐标为(M,N)
每个点有8个方向可以试探,试探顺序规定为:从正东方向顺时针方向进行。
move数组定义
typedef struct{
int x,y;
}item;
item move[8];
x:行坐标增量
y:列坐标增量
当到达了某点而无路可走时需要返回前一点,再从前一点开始向下一个方向继续试探。因此,压入栈中的不仅是顺序到达的各点的坐标,而且还要有从看一点到达本点的方向。
栈中元素是一个由行、列、方向组成的三元组。
typedef struct{
int x,y,direction;
}datatype;
栈的定义为:
stack <datatype> s;
当到达某点(i,j)时,将maze[i][j]置为-1
1.栈初始化
2.将入口点坐标以及到达该点的方向direction设置为-1,入栈。
3.while(栈不为空)
{
弹栈
求出下一个要试探的方向direction++
while(还有剩余试探方向时){
if(direction方向可走){
则{x,y,direction}入栈,求新点坐标(i,j)
将新点(i,j)切换为当前点(x,y)
if((x,y)==(M,N)){
结束
????????????????}
else{
重制direction=0;
}
}
else
direction++;
}
}
ADT Queue{
数据对象:
数据关系:
约定:其中端为队列头,端为队列尾。
基本操作:
}ADT Queue
#define MAXQSIZE 100
typedef struct{
ElemType base[MAXQSIZE];
int front;//头指针,若队列不为空,指向队列头元素
int resr;//尾指针,若队列不为空,指向队列尾元素的下一个位置
}SqQueue;
为了解决假溢出问题,采用循环队列。
队满和队空时均有sq.front==sq.rear
无法判断队满还是队空。
用一个计数变量来记录队列中元素的个数。
设置一个标志位。
当有元素入队时,标志位为true;有元素出队时,标志位为false。
牺牲一个元素空间,来区别队空或队满。?
队满:(sq.rear+1)%queue_length==sq.front
队空:sq.front==sq.rear
1)判断队列是否满
2)满了,则error
3)没满,Q.base[Q.rear]=e;
Q.rear=(Q.rear+1)%queue_length;
1)判断队列是否为空
2)空,返回error
3)非空,e=Q.base[Q.front];
Q.front=(Q.front+1)%queue_length;
return ((Q.rear-Q.front+queue_length)%queue_length);
typedef struct QNode{
QelemType data;
struct QNode *next;
}*QueuePtr;
typedef struct{
QueuePtr front;
QueuePtr rear;
}LinkQueue;
p=new QNode;
p->data=e;
p->next=NULL;
Q.rear->next=p;
Q.rear=p;?
if(Q.front==Q.rear)
? ? ? ? return error;
p=Q.front->next;
e=p->data;
Q.front->next=p->next;
if(Q.rear==p)
? ? ? ? Q.rear=Q.front;//避免尾指针成为野指针
delete p;
Q.front=Q.rear=new QNode;
Q.front->next=NULL;//带头结点