棧和隊列



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;
}
發佈了79 篇原創文章 · 獲贊 62 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章