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;
}