特殊的线性表——栈、队列
从数据结构角度看,栈和队列是操作受限的线 性表,他们的逻辑结构相同。
栈的逻辑结构
空栈:不含任何数据元素的栈。
(a1, a2, ……, an)
栈:限定仅在表尾进行插入和删除操作的线性表。
栈顶
栈的操作特性:后进先出(LIFO)
注意:栈只是对表插入和删除操作的位置进行了限 制,并没有限定插入和删除操作进行的时间。
栈的抽象数据类型定义 特殊线性表——栈 特殊线性表——栈 ADT Stack Data
栈中元素具有相同类型及后进先出特性, 相邻元素具有前驱和后继关系 Operation InitStack
DestroyStack 前置条件:栈已存在 输入:无 功能:销毁栈 输出:无 后置条件:释放栈所占用的存储空间
Push 前置条件:栈已存在 输入:元素值x 功能:在栈顶插入一个元素x 输出:如果插入不成功,抛出异常 后置条件:如果插入成功,栈顶增加了一个元素 前置条件:栈不存在 输入:无 功能:栈的初始化 输出:无 后置条件:构造一个空栈
Pop 前置条件:栈已存在 输入:无 功能:删除栈顶元素 输出:如果删除成功,返回被删元素值,否则,抛出异常
后置条件:如果删除成功,栈减少了一个元素
GetTop 前置条件:栈已存在 输入:无 功能:读取当前的栈顶元素 输出:若栈不空,返回当前的栈顶元素值 后置条件:栈不变
Empty 前置条件:栈已存在 输入:无 功能:判断栈是否为空 输出:如果栈为空,返回1,否则,返回0 后置条件:栈不变 endADT
栈的顺序存储结构——顺序栈
确定用数组的哪一端表示栈底。
特殊线性表——栈 特殊线性表——栈
如何改造数组实现栈的顺序存储?
- 确定用数组的哪一端表示栈底。
- 附设指针top指示栈顶元素在数组中的位置。附设指针top指示栈顶元素在数组中的位置。
进栈:top加1 出栈:top减1
栈空:top= -1 栈满:top= StackSize-1
顺序栈类的声明
const intStackSize=100;
template
class SeqStack
{ public:
SeqStack( ){top=-1;} ;
~SeqStack( );
void Push ( T x );
T Pop ( );
T GetTop( ) {if (top!=-1) return data[top];}
bool Empty ( ) { if(top==-1) return 1;else return 0;}
private: T data[StackSize]; int top; } ;
顺序栈的实现——入栈
template
void SeqStack::Push( T x)
{ if (topStackSize-1)
throw “溢出”; top++;
data[top]=x; }
顺序栈的实现——出栈
template
T SeqStack:: Pop ( )
{ if (top-1) throw “溢出”;
x=data[top–]; return x; }
两栈共享空间
解决方案1: 直接解决:为每个栈开辟一个数组空间。
在一个程序中需要同时使用具有相同数据类型的 两个栈,如何顺序存储这两个栈?
解决方案2: 顺序栈单向延伸——使用一个数组来存储两个栈
特殊线性表——栈 特殊线性表——栈
会出现什么问题?如何解决?
**两栈共享空间:**使用一个数组来存储两个栈,让一个 栈的栈底为该数组的始端,另一个栈的栈底为该数组 的末端,两个栈从各自的端点向中间延伸。
栈1的底固定在下标为0的一端; 栈2的底固定在下标为StackSize-1的一端。 top1和top2分别为栈1和栈2的栈顶指针; StackSize为整个数组空间的大小
什么时候栈 1为空?top1= -1
栈2为空?top2= StackSize
什么时候栈满?top2= top1+1
两栈共享空间类的声明
const intStackSize=100;
template
classBothStack
{ public: BothStack( )
{top1= -1; top2=StackSize;} ~BothStack( );
void Push(inti, T x);
T GetTop(inti);
bool Empty(inti);
private: T data[StackSize]; inttop1, top2; };
- 如果栈满,则抛出上溢常; 2. 判断是插在栈1还是栈2; 2.1 若在栈1插入,则 2.1.1 top1加1; 2.1.2 在top1处填入x; 2.2 若在栈2插入,则 2.2.1 top2减1; 2.2.2 在top2处填入x;
template
void BothStack::Push(inti, T x )
{ if (top1top2-1) throw “上溢”;
if (i1) data[++top1]=x;
if (i2) data[–top2]=x; }
两栈共享空间的实现——删除 操作接口:T Pop(inti);
若是在栈1删除,则 1.1 若栈1为空栈,抛出下溢异常; 1.2 删除并返回栈1的栈顶元素; 2. 若是在栈2删除,则 2.1 若栈2为空栈,抛出下溢异常; 2.2 删除并返回栈2的栈顶元素;
T BothStack::Pop(inti)
{ if (i1) //将栈1的栈顶元素出栈
{ if (top1== -1) throw “下溢”;
return data[top1–];} if (i2) //将栈2的栈顶元素出栈
{if (top2StackSize) throw “下溢”; return data[top2++]; } }
两栈共享空间的实现——读取栈i当前的栈顶元素 操作接口:T GetTop(inti);
template
T BothStack::GetTop(inti)
{ if(i1) { if (top1!=-1) return data[top1];}
if(i2) { if(top2!=StackSize) return data[top2]; } }
两栈共享空间的实现——判断栈i是否为空栈 操作接口:boolEmpty(inti);
template
boolBothStack::Empty(inti)
{ if(i1) { if(top1-1) return 1;
else return 0;} if(i2) {
if(top2StackSize) return 1;
else return 0;} }
栈的链接存储结构及实现 栈的链接存储结构——链栈
链 栈 的 类 声 明
template
class LinkStack
{ public: LinkStack(){top=NULL;} ~LinkStack( ); void Push(T x); T Pop( ); T GetTop( ){if(top!=NULL)returntop->data;} boolEmpty ( ){return(topNULL? 1: 0);} private: Node *top; }
template
void LinkStack::Push(Tx)
template
T LinkStack::Pop( )
{ if (topNULL) throw “下溢”;
x=top->data;
p=top; top=top->next;
delete p; return x; }
{ s=new Node;
s->data=x; s->next=top; top=s; }
顺序栈和链栈的比较
时间性能:相同,都是常数时间O(1)。 空间性能: ¾顺序栈:有元素个数的限制和空间浪费的问题。 ¾链栈:没有栈满的问题,只有当内存没有可用空 间时才会出现栈满,但是每个元素都需要一个指针 域,从而产生了结构性开销。
总之,当栈的使用过程中元素个数变化较大时,用 链栈是适宜的,反之,应该采用顺序栈。
1 递归的定义
子程序(或函数)直接调用自己或通过一系列调 用语句间接调用自己,是一种描述问题和解决问 题的基本方法。
2 递归的基本思想
问题分解:把一个不能或不好解决的大问题转化 为一个或几个小问题,再把这些小问题进一步分 解成更小的小问题,直至每个小问题都可以直接 解决。
3 递归的要素
⑴递归边界条件:确定递归到何时终止,也称 为递归出口;
⑵递归模式:大问题是如何分解为小问题的, 也称为递归体。
队列的逻辑结构
空队列:不含任何数据元素的队列。
队列:只允许在一端进行插入操作,而另一端进行 删除操作的线性表。
允许插入(也称入队、进队)的一端称为队尾,允 许删除(也称出队)的一端称为队头。
(a1, a2, ……, an)
队列的抽象数据类型定义
ADT Queue Data
队列中元素具有相同类型及先进先出特性, 相邻元素具有前驱和后继关系
Operation InitQueue 前置条件:队列不存在 输入:无 功能:初始化队列 输出:无 后置条件:创建一个空队列
DestroyQueue 前置条件:队列已存在 输入:无 功能:销毁队列 输出:无 后置条件:释放队列所占用的存储空间 EnQueue 前置条件:队列已存在 输入:元素值x 功能:在队尾插入一个元素 输出:如果插入不成功,抛出异常 后置条件:如果插入成功,队尾增加了一个元素
DeQueue 前置条件:队列已存在 输入:无 功能:删除队头元素 输出:如果删除成功,返回被删元素值 后置条件:如果删除成功,队头减少了一个元素 GetQueue 前置条件:队列已存在 输入:无 功能:读取队头元素 输出:若队列不空,返回队头元素 后置条件:队列不变
Empty 前置条件:队列已存在 输入:无 功能:判断队列是否为空 输出:如果队列为空,返回1,否则,返回0 后置条件:队列不变 endADT
Empty 前置条件:队列已存在 输入:无 功能:判断队列是否为空 输出:如果队列为空,返回1,否则,返回0 后置条件:队列不变 endADT
如何确定不同的队空、队满的判定条件? 为什么要将队空和队满的判定条件分开?
方法一:附设一个存储队列中元素个数的变量num, 当num=0时队空,当num=QueueSize时为队满; 方法二:修改队满条件,浪费一个元素空间,队满时 数组中只有一个空闲单元; 方法三:设置标志flag,当front=rear且flag=0时为队 空,当front=rear且flag=1时为队满。
const intQueueSize=100;
template
class CirQueue
{ public: CirQueue( )
{front=rear=QueueSize-1;}
~ CirQueue( );
void EnQueue(Tx);
T DeQueue( );
T GetQueue( );
bool Empty(
){return(frontrear? 1: 0);}
private: T data[QueueSize];
int front, rear; };
template
void CirQueue::EnQueue(Tx )
{ if ((rear+1)% QueueSizefront)
throw “上溢”;
rear=(rear+1)% QueueSize;
data[rear]=x; }
template
T CirQueue::DeQueue( )
{ if (rearfront)throw “下溢”;
front=(front+1)% QueueSize;
return data[front]; }
template
T CirQueue::GetQueue( )
{ if (rearfront)throw “下溢”;
i=(front+1)% QueueSize;
return data[i]; }
链 队 列 类 的 声 明
template
class LinkQueue
{ public: LinkQueue( );
~LinkQueue( );
void EnQueue(Tx);
T DeQueue( );
T GetQueue( );
intEmpty( )
{return(frontrear?1:0);}
private: Node *front, *rear; };
LinkQueue( );
template
LinkQueue::LinkQueue( )
{ front=new Node;
front->next=NULL; rear=front; }
链队列的实现——入队
template
void LinkQueue::EnQueue(Tx )
{ s=new Node; s->data=x; s->next=NULL; rear->next=s; rear=s; }
链队列的实现——出队
template
T LinkQueue::DeQueue( )
{ if (rearfront) throw “下溢”;
p=front->next;
x=p->data;
front->next=p->next;
if (p->nextNULL) rear=front;
delete p; return x; }
时间性能: 循环队列和链队列的基本操作都需要常数时间O (1)。
空间性能:
循环队列:必须预先确定一个固定的长度,所以有 存储元素个数的限制和空间浪费的问题。
链队列:没有队列满的问题,只有当内存没有可用 空间时才会出现队列满,但是每个元素都需要一个 指针域,从而产生了结构性开销。
括号匹配的检验
分析可能出现的不匹配的情况: ™到来的右括号并非是所“期待”的; [ ( ]) ¾到来的是“不速之客”; [ ( ) ] ) 或) ƒ直到结束,也没有到来所“期待”的括号。 ( ( 这三种情况对应到栈的操作即为: ™和栈顶的左括号不相匹配; ¾栈中并没有左括号等在哪里; ƒ栈中还有左括号没有等到和它相匹配的右括号。
算法思想:
1)凡出现左括号,则进栈;
2)凡出现右括号,首先检查栈是否空 若栈空,则表明该“右括号”多余, 否则和栈顶元素比较, 若相匹配,则“左括号出栈”, 否则表明不匹配。
3)表达式检验结束时, 若栈空,则表明表达式中匹配正确, 否则表明“左括号”有余。
boolmatching(charexp[])
{ // 检验表达式中所含括号是否正确嵌套,
//若是,则返回TRUE,否则返回FALSE. ‘#’ 为表达式的结束符 intstate = 1,i=0; char ch,e; ch= exp[i++]; SeqStack S; // 构造空栈 while (ch!=’\0’ && state) { if(ch’(’ || ch==’[’) S.Push(ch); // 凡左括号一律入栈 else if(ch== ‘)’) if (!S.Empty() && S.GetTop()’(’) e=S.Pop(); else state = 0; else if(ch’]’) if (!S.Empty() && S.GetTop()==’[’) e=S.Pop(); else state = 0; ch= exp[i++]; } // while if ( state && S.Empty() ) return 1; else return 0; }//matching