1.栈
(1)后进先出;
实现:栈顶指针指向栈顶元素,插入时先修改指针再插入,删除时先取栈顶元素再修改指针;
栈顶元素S[S.top],栈底元素S[1];
只能在栈顶上插入和删除;
(2)栈数据结构(用vector实现也行,更简单):
int top; //栈顶指针
int stackLen;//栈长度
int *data;//指向栈数组
(3)在栈上实现的操作:
STACK-EMPTY(S)//判断栈是否为空
PUSH(S, x) //把x压入到栈顶
POP(S) //取出并返回栈顶元素
下面是实现代码:
/*
栈的实现:数组为基础数据结构
*/
class Stack{
private:
int top;//栈顶指针
int dataLen;//栈数组大小
int *data;
Stack(){}
public:
Stack(int stackSize){
dataLen = stackSize;
top = -1;
data = new int[stackSize];
}
~Stack(){delete []data;}
bool IsEmpty();
int Pop();
void Push(int x);
void printStack();
};
bool Stack::IsEmpty()
{
return top<0 ;
}
void Stack::Push(int x)
{
if(top>=dataLen-1){
cerr<<"栈上溢!"<<endl;
return;
}
data[top+1] = x;
top++;
}
int Stack::Pop()
{
if(top<0){
cerr<<"栈下溢!"<<endl;
return INT_MIN;
}
int val = data[top];
top--;
return val;
}
void Stack::printStack()
{
//---栈底到栈顶顺序输出
for(int i=0 ; i<dataLen ; i++)
cout<<data[i]<<" ";
cout<<endl;
}
补充说明:
1.最先进栈的元素是不是就只能是最后出栈呢?
答:不一定,栈只是对入栈出栈的位置作了限制,并没有对元素的进出时间进行限制。如1、2、3依次进栈,可能的出栈顺序有:
1进1出2进2出3进3出---123序;1进1出2进3进3出2出---132序;1进2进2出1出3进3出---213序;1进2进3进3出2出1出---321序;1进2进2出3进3出1出---231序。最先进栈的元素1可以最先出栈,出栈顺序与时间无关的。
2.双向栈:两栈共享存储空间(数组),前提是对于两个栈中数据类型相同,可以让栈A栈顶从0开始,栈B栈顶从M-1开始。于是二者的判空条件分别为topA==-1与topB==M;判满条件是topA+1==topB。代码很简单,不需要作过多讲解。Push与Pop的时候用一个标志位进行标记是对哪个栈进行操作即可。
/*
双向栈的实现
*/
typedef int ElemType;
#define MAXSIZE 100
struct SqDoubleStack
{
SqDoubleStack():topA(-1),topB(MAXSIZE){}
ElemType data[MAXSIZE];
int topA;
int topB;
bool Push(ElemType e , int stackNumber);
bool Pop(ElemType *e , int stackNumber);
void printStack(int stackNumber);
};
bool SqDoubleStack::Push(ElemType e , int stackNumber)
{
if(topA+1 == topB) //栈已满,不能再push新元素了
return false;
if(stackNumber == 1) //栈A有元素进栈
data[++topA] = e; //若栈1则先top+1后给数组元素赋值
else if(stackNumber == 2) //栈B有元素进栈
data[--topB] = e; //若栈2则先top2-1后给数组元素赋值
return true;
}
//若栈不空,则删除s的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
bool SqDoubleStack::Pop(ElemType *e , int stackNumber)
{
if(stackNumber == 1)
{
if(topA == 1)
return false; //说明栈1已经是空栈,溢出
*e = data[topA--]; //将栈1的栈顶元素出栈
}
else if(stackNumber == 2)
{
if(topB == MAXSIZE)
return false; //说明栈2已经是空栈,溢出
*e = data[topB++]; //将栈2的栈顶元素出栈
}
return true;
}
//---由栈底到栈顶打印元素
void SqDoubleStack::printStack(int stackNumber)
{
if(stackNumber == 1)
{
if(topA == 1)//栈A是空栈
return;
for(int i=0 ; i<=topA ; i++)
cout<<data[i]<<" ";
cout<<endl;
}
else if(stackNumber == 2)
{
if(topB == MAXSIZE)//栈B是空栈
return ;
for(int i=MAXSIZE-1 ; i>=topB ; i--)
cout<<data[i]<<" ";
cout<<endl;
}
}
int main()
{
SqDoubleStack stack;
for(int i=0 ; i<9 ; i++)
stack.Push(i+1,1);
for(int i=0 ; i<9 ; i++)
stack.Push(i+1,2);
stack.printStack(1);
stack.printStack(2);
return 0;
}
3.栈的链式存储结构:
由于链栈的栈顶可以作为链表的头结点,因此链栈不需要头结点。链栈基本不存在栈满的情况,除非内存已经没有可用的空间,如果真是如此操作系统已近面临死机崩溃的问题,而不是链栈是否溢出的问题。链栈判空条件就是top==NULL,链栈绝大部分操作都与单链表类似,只在插入和删除上特殊一些。
/*
链栈的实现
*/
typedef int ElementType;
struct StackNode{
StackNode():next(NULL){}
ElementType data;
StackNode *next;
};
class LinkStack{
private:
StackNode* top;//栈顶指针
int count;//链栈内元素个数
void destory();//释放链栈申请内存
public:
LinkStack():top(NULL){}
~LinkStack();
bool Push(ElementType e);
bool Pop(ElementType *e);
bool StackEmpty();
void printLinkStack();
};
LinkStack::~LinkStack()
{
destory();
top = NULL;
}
bool LinkStack::StackEmpty()
{
return (NULL == top);
}
void LinkStack::destory()
{
StackNode *p = top;
StackNode *t;
while(p){
t = p;
p = p->next;
delete t;
t = NULL;
}
}
bool LinkStack::Push(ElementType e)
{
//---无需判满
StackNode* t = new StackNode;
t->data = e;
t->next = top;
top = t;
count++;
return true;
}
bool LinkStack::Pop(ElementType *e)
{
if(StackEmpty())//空栈
return false;
*e = top->data;
StackNode *t = top;
top = top->next;
delete t;
count--;
return true;
}
//---从顶到底打印元素
void LinkStack::printLinkStack()
{
if(StackEmpty())//空栈
return;
StackNode *p = top;
while(p){
cout<<p->data<<" ";
p = p->next;
}
cout<<endl;
}
int main()
{
LinkStack stack;
for(int i=0 ; i<9 ; i++)
stack.Push(i+1);
stack.printLinkStack();
return 0;
}
2.队列
(1)先进先出;
实现:队列的头指针指向队列首元素,删除时先取队列首元素再修改指针,队列的尾指针指向队尾元素的下一个元素,插入时先插入再修改指针;
队头元素Q[head],队尾Q[tail];
只能在队头出队,队尾入队。
(2)数组队列的结构:
int tail; //队尾,指向最新进入的元素
int head; //队头,指向最先出的元素
int length;//队列的长度
int *data; //指向队列数组
(3)在队列上实现的操作:
ENQUEUE(Q, x) //把x插入到队列尾
DEQUEUE(Q) //取出队列首元素并返回
说明:1.对于数组实现的队列,可能会出现“假溢出”的情况,于是常常实现为循环队列,对于循环队列有两种判定是否为空可以用flag==0&&front==rear,为满可以用flag==1&&front==rear;还可以预留一个空位,如果为空rear==front,为满(rear+1)%QueueSize==front。
2.对于队列自然也有链队列,此时对头指针指向链队列的头结点队尾指针指向终端节点。
/*
链队列的实现
*/
//链式队列的结点类型定义
typedef int ElemType;
struct QNode{
QNode():next(NULL){}
ElemType data;
QNode *next;
};
//链式队列
class LinkQueue{
private:
QNode* front;
QNode* rear;
public:
LinkQueue():front(NULL),rear(NULL){
QNode *t = new QNode;
front = rear = t;
front->next = rear->next = NULL;
}
~LinkQueue(){
while(front)
{
rear = front->next;
delete(front);
front = rear;
}
}
int QueueLength();
void EnQueue(ElemType e);
bool DeQueue(ElemType &e);
};
//求队列的长度
int LinkQueue::QueueLength()
{
QNode *p = front; //p指向头结点
int count = 0;
while (p->next != NULL)
{
count++;
p = p->next;
}
return count;
}
//队尾入队
void LinkQueue::EnQueue(ElemType e)
{
QNode *p = new QNode;
if(!p)
exit(OVERFLOW);
p->data = e;
p->next=NULL; //生成一个数据为X的结点
rear ->next = p ; //将结点P插入队尾
rear = p; //修改队尾指针,令其指向P结点
}
//队首(头结点的后继)出队
bool LinkQueue::DeQueue(ElemType &e)
{
//链队列Q为空
if(front->next = NULL)
return false;
QNode* p = front->next; //令p指向队列Q的第一个有效节点
e = p->data;
front->next = p->next;//修改队头指针
if(rear == p) //被删除的是队尾结点,修改rear为front
rear = front;
delete p;
return true;
}
下面是普通队列的实现代码:
/*
队列的实现:数组为基础数据结构
*/
class Queue
{
public:
Queue(){}
Queue(int QSize):dataLen(QSize),front(0),rear(0){
data = new int[QSize];
}
~Queue(){delete []data;}
void printQueue();
void EnQueue(int x);
int DeQueue();
bool isEmpty();
bool isFull();
private:
int *data;
int dataLen;//有效数据为dataLen-1,留空作为判空和判满标识
int front;//对头指针,指向第一个有效元素位置
int rear;//队尾指针,最后一个有效元素的下一个位置
};
bool Queue::isEmpty()
{
return front==rear;
}
bool Queue::isFull()
{
return (rear+1)%dataLen==front;
}
//从头到尾输出元素
void Queue::printQueue()
{
if (isEmpty()){
cout<<"队列已空!"<<endl;
return;
}
if(front<rear){//对头在队尾前面
for(int i=front ; i<rear ; i++)
cout<<data[i]<<" ";
cout<<endl;
}else{//对尾在队头前面
for(int i=front ; i<=dataLen-1 ; i++)
cout<<data[i]<<" ";
for(int i=0 ; i<rear ; i++)
cout<<data[i]<<" ";
cout<<endl;
}
}
void Queue::EnQueue(int x)
{
if(isFull()){
cout<<"队列已满!"<<endl;
return;
}
data[rear] = x;
if(rear == dataLen-1)//队尾指向最后位置
rear = 0;//循环数组
else
rear++;
}
int Queue::DeQueue()
{
if(isEmpty()){
cout<<"队列已空!"<<endl;
return INT_MIN;
}
int val = data[front];
if(front == dataLen-1)//对头指向最后位置
front = 0;
else
front++;
return val;
}