1.1 栈
//初始化操作
#define maxszie 10
typedef struct{
elemtype data[maxsize];
int top;
}sqstack;
//初始化栈
s.top=-1;
//判栈空
if(s.top==-1)
return true;
//进栈,先判栈满,再进栈
if(S.top==maxsize-1)//到达栈顶
return false;
S.top=S.top+1;
S.data[S.top]=x;//S.data[++top]=x;
return true;
//出栈,先判栈是否空
if(s.top==-1)
return false;
x=S.data[S.top];
s.top=s.top-1;//x=S.data[S.top--],top后减
return true;
若栈顶指针一开始指向栈底第一个空元素,S.top=0;
则,后面的入栈和出栈需要相应的,top指针应该后修改,x先操作,当top=maxsize-1||S.data[S.top]!=null时,栈满。
1.2 共享栈
typedef struct{
elemtype data;
int top0;//栈顶
in top1;//栈底
}shstack
//栈满条件:top1+1==top1;
采用共享栈的好处是,节省存储空间,降低发生上溢的可能。
[09真题]
栈s和队列Q的初始状态都为空,元素adcdefg依次进入栈中,若每个元素出栈后立即进入Q中,且已知元素出队顺序为bdcfeag,则栈s的容量至少是?
注意:看清s栈和队列Q,已知出队,因此可知入队序列和s的出栈序列一致。
s栈内 | 出栈 |
---|---|
ab | b |
acd | dc |
aef | fe |
a | a |
g | g |
显然,由图表可知s栈的容量至少为3。
2.队列
顺序实现
#define maxsize 10
typedef struct{
elemtype data;
int front;
int rear;
}sqQuene;
2.1循环队列
初始时:Q.front=Q.rear=0;
入队:Q.rear=(Q.rear+1)%maxsize
出队:Q.front=(Q.front+1)%maxsize
队列长度:(Q.rear+maxsize-Q.front)%maxsize;
队满:
1)(Q.rear+1)%maxsize=Q.front;
2)设置tag,入队置1,出队置0;
3设置size,记录队列长度
//循环队列初始化:以下皆以第一种判队满方式为例
Q.front=Q.rear=0;
//判队空
Q.front==Q.rear;
//入队:在队尾入队
if((Q.rear+1)%maxsize==Q.front)
return false;
Q.data[Q.rear]=x;
Q.rear=(Q.rear+1)%maxsize;
return true;
//出队:队头
if(Q.front==Q.rear)
return false;
x=Q.data[Q.front];
Q.front=(Q.front+1)%maxsize;
return true;
链式存储:链队
分为带头结点和不带头结点
1)带头结点
typedef struct //结点定义
{
elemtype data;
struct linknode *next;
}Linknode;
typedef struct
{
linknode *front,*rear;
}linkqueue;//链队队列
//初始化队列
void InitQueue(Linkqueue &Q)
{
Q.front=Q.rear=(linknode *)malloc(sizeof(linknode));
Q.front->next=null;
}
//入队:链队不存在队满,除非内存空间不足
void enQueue(linkqueue &Q,elemtype x)
{
linknode *s=(linknode *)malloc(sizeof(linknode));
s->data=x;
s->next=null;//容易遗漏
Q.rear->next=s;
Q.rear=s;
}
//出队
bool deQueue(linkqueue &Q,elemtype x)
{
if(Q.rear==Q.front)//or Q.front=null;
return false;//队空
x=Q.front->data;
linknode *p=Q.front->next;//指向出队结点
Q.front->next=p->next;
if(Q.rear==p)
Q.rear=Q.front;
free(p);
return true;
}
2)不带头结点
//入队
void enQueue(linkqueue &Q,elemtype x)
{
//初始化
linknode *s=(linknode *)malloc(sizeof(linknode));
s->data==x;
if(Q.front==null)
{
Q.front=Q.rear=s;
s->next=null;
}
else{
s->next=null;//容易遗漏
Q.rear->next=s;
Q.rear=s;
}
}
//出队
bool deQueue(linkqueue &Q,elemtype x)
{
if(Q.rear==Q.front)//or Q.front=null;
return false;//队空
x=Q.front->data;
linknode *p=Q.front;
Q.font=p->next;//若为最后一个元素,p=Q.rear,Q.rear->next=null 这句执行完以后,Q.front=null
if(Q.rear==p)
Q.rear=Q.front=null;
return true;
}
2.2双端队列
3.栈和队列的应用
栈:括号匹配中缀表达式求值,函数调用栈(斐波那契数列、求阶乘),
队列:树和图的遍历、cpu的分时处理和多用户任务处理(打印机)
注:并不是所有的递归都要用到栈,还可以使用迭代。递归的主要特点:效率低,但是代码理解简单。
4.数组
普通二维数组的按存储方式可分为行优先和列优先,存储在一个线性表中,为了便于随机存取,也因此需要考虑两个问题,一个是使用多大的空间,另一个是二维数组存到一维线性表里采用怎么样的映射关系。
- 对于普通二维数组而言,如果采用行优先,目标元素A(i,j)在线性表里的位置应该是:表的起始地址+【(i-1)*二维数组的长度+j列】 * 数组单个元素所占存储空间;
- 如果采用列优先,相应的有:表的起始地址+【(j-1)*二维数组高度+i】 * 数组单个元素所占存储空间;
4.1 对称矩阵
A[i][j]=A[j][i],由此特点可以得知我们只需要存储A中一半的元素即可,按一半可分为上三角或者下三角,那一共就是(1+n)*n/2个空间大小,那按划分三角的方式和按行和按列,但是由于对称矩阵具有对称的性质,因此,当得知了当i和j互换以后,就可以实现从上三角到下三角的转换,因此,主要讨论行优先和列优先即可(以下都以下三角为例,并且数组从下标0开始,n阶)
- 下三角+行优先:A[i][j]的位置应该是(j-1)*j/2+i-1;
- 下三角+列优先:(i-1)(n+n-1+…+n-i+2)/2+j-i=>(i-1)(2n-i+2)/2+(j-i);
4.2 三角阵
三角阵在对称矩阵的基础上需要多一个空间,因此容易知道三角阵的空间大小应该是(1+n)*n/2+1,而假设数组下标从0开始的话,可以将多余的元素刚好存在下标为(1+n)*n/2的位置处,而映射关系应该和对称阵采用列优先的一致
具体来说的话:
- 上三角+行优先=下三角+列优先;上三角+列优先=下三角+行优先,具体问题具体分析
4.3 三对角矩阵
带状的矩阵;除下标|j-i|<=1外,其他都为0;只有第一行和最后一行只有两个数据元素,其余行元素个数都一致(在假设存入数组下标为1开始的情况下)。
- 采用行优先,前i-1行应该有3(i-1)-1个元素,那么第i行A[i][j]前面应该有j-i+2个元素why?,所以两者相加就是2i+j-2;
4.4 稀疏矩阵
采用三元组或者十字链表法存储
注意:看题要仔细,注意看下标是规定1还是0开始!!!