基本數據結構 - 棧和隊列
摘要
本章介紹了幾種基本的數據結構,包括棧、隊列、鏈表以及有根樹,討論了使用指針的簡單數據結構來表示動態集合。本章的內容對於學過數據結構的人來說,沒有什麽難處,簡單的總結一下。
1、棧和隊列
棧和隊列都是動態集合,元素的出入是規定好的。棧規定元素是先進後出(FILO),隊列規定元素是先進先出(FIFO)。棧和隊列的實現可以采用數組和鏈表進行實現。在標準模塊庫STL中有具體的應用,可以參考http://www.cplusplus.com/reference/。
棧的基本操作包括入棧push和出棧pop,棧有一個棧頂指針top,指向最新如棧的元素,入棧和出棧操作操作都是從棧頂端進行的。
隊列的基本操作包括入隊enqueue和出隊dequeue,隊列有隊頭head和隊尾tail指針。元素總是從隊頭出,從隊尾入。采用數組實現隊列時候,為了合理利用空間,可以采用循環實現隊列空間的有效利用。
關於棧和隊列的基本操作如下圖所示:
采用數組簡單實現一下棧和隊列,實現隊列時候,長度為n的數組最多可以含有n-1個元素,循環利用,這樣方便判斷隊列是空還是滿。
棧的C++程序如下所示:
#include<iostream>
#include<cstdlib>
using namespace std;
typedef struct stack
{
int *s;
int stacksize;
int top;
}stack;
void init_stack(stack *s,int n)
{
s->stacksize=n;
s->s=(int*)malloc(sizeof(int)*s->stacksize);
s->top=0;
}
bool stack_empty(stack *s)
{
if(s->top==0)
return true;
else
return false;
}
bool stack_full(stack *s)
{
if(s->top==s->stacksize)
return true;
else
return false;
}
void push(stack *s,int x)
{
if(stack_full(s))
{
cout<<"overflow"<<endl;
exit(1);
}
s->top=s->top+1;
s->s[s->top]=x;
}
int pop(stack *s)
{
int x;
if(stack_empty(s))
{
cout<<"underflow"<<endl;
exit(1);
}
x=s->s[s->top];
s->top--;
return x;
}
int top(stack *s)
{
return s->s[s->top];
}
int main()
{
stack s;
init_stack(&s,100);
push(&s,19);
push(&s,23);
push(&s,34);
push(&s,76);
push(&s,65);
cout<<"top is "<<top(&s)<<endl;
pop(&s);
cout<<"top is "<<top(&s)<<endl;
}
隊列的C++代碼實現:
#include<iostream>
#include<cstdlib>
using namespace std;
typedef struct queue
{
int *q;
int head,tail;
int queuesize;
}queue;
void init_queue(queue *q,int n)
{
q->queuesize=n;
q->q=(int*)malloc(sizeof(int)*q->queuesize);
q->tail=q->head=0;
}
bool queue_empty(queue *q)
{
if(q->head==q->tail)
return true;
else
return false;
}
bool queue_full(queue *q)
{
if(q->tail+1==q->head)
return true;
else
return false;
}
int queue_length(queue *q)
{
return (q->tail-q->head+q->queuesize)%q->queuesize;
}
void enqueue(queue *q,int x)
{
if(queue_full(q))
{
cout<<"queue overflow"<<endl;
exit(1);
}
q->q[q->tail]=x;
q->tail=(q->tail+1)%q->queuesize;
}
int dequeue(queue *q)
{
int x;
if(queue_empty(q))
{
cout<<"queue underflow"<<endl;
exit(1);
}
x=q->q[q->head];
q->head=(q->head+1)%q->queuesize;
return x;
}
int main()
{
queue q;
init_queue(&q,100);
enqueue(&q,10);
enqueue(&q,30);
cout<<"head: "<<q.head<<" tail: "<<q.tail<<endl;
cout<<"value="<<dequeue(&q)<<endl;
cout<<"value="<<dequeue(&q)<<endl;
cout<<"head: "<<q.head<<" tail: "<<q.tail<<endl;
enqueue(&q,10);
exit(0);
}
問題:
(1)說明如何用兩個棧實現一個隊列,並分析有關隊列操作的運行時間。(始終用一個棧做為出,一個棧作為入隊)
解答:棧中的元素是先進後出,而隊列中的元素是先進先出。現有棧s1和s2,s1中存放隊列中的結果,s2輔助轉換s1為隊列。
入隊時,將元素壓入s1。
出隊時,判斷s2是否為空,如不為空,則直接彈出頂元素;如為空,則將s1的元素逐個“倒入”s2,把最後一個元素彈出並出隊。
(如果s1滿了,s2既沒有裝滿也不是非空,此時就不能繼續入隊了;如果s1滿了,但s2是空的,則可以先將s1中的元素壓人s2中,然後再進隊。)
#include<iostream>
#include<stack>
#include<cstdlib>
using namespace std;
template <typename T>
class StackToQueue
{
public:
T dequeue();
void enqueue(const T &node);
StackToQueue(){}
~StackToQueue(){}
private:
stack<T> stack1;
stack<T> stack2;
};
template <typename T>
void StackToQueue<T>::enqueue(const T &node)
{
stack1.push(node);
}
template <typename T>
T StackToQueue<T>::dequeue()
{
if(stack2.empty()&&stack1.empty())
{
cout<<"underflow"<<endl;
exit(1);
}
if(stack2.empty()&&!stack1.empty())
{
while(!stack1.empty())
{
T temp=stack1.top();
stack1.pop();
stack2.push(temp);
}
}
T data=stack2.top();
stack2.pop();
return data;
}
int main()
{
StackToQueue<int> sq;
sq.enqueue(1);
sq.enqueue(2);
sq.enqueue(3);
cout<<sq.dequeue()<<endl;
cout<<sq.dequeue()<<endl;
cout<<sq.dequeue()<<endl;
cout<<sq.dequeue()<<endl;
}
(2)說明如何用兩個隊列實現一個棧,並分析有關棧操作的運行時間。(始終保證有一個隊列是空的)
需要註意的一點是,如果每次入棧都選則將元素插入到第一個隊列中,出隊時先將前n-1個元素移交到第二個隊列中,然後返回隊列一中剩余的唯一一個元素,再將隊列二中的元素又依次移交回隊列一中,這樣做未免效率過於低下。
事實上這樣的思路我們可以看作是一直選用隊列一作為棧元素的存儲容器,而使用隊列二作為一個出隊時的輔助工具,這樣每次出棧時來回復制元素容器元素的操作,效率確實低下。因為兩個隊列完全一樣的,所以我們完全有理由讓兩個隊列輪流作為棧元素的存儲容器,這樣每次出棧時,只需將所有前n-1個元素從一個隊列移交到另一個隊列中,然後返回最後一個元素作為出棧結果,這樣始終至少有一個隊列是空的,而每次進棧時,只需將進占元素放在那個非空隊列的末尾(如果兩個隊列均為空,則隨便放在那個裏面都行)。這樣減少了一半的復制操作。
#include<iostream>
#include<queue>
#include<cstdlib>
using namespace std;
template <typename T>
class QueueToStack
{
public:
QueueToStack(){}
~QueueToStack(){}
T pop();
void push(const T &node);
private:
queue<T> queue1;
queue<T> queue2;
};
template <typename T>
T QueueToStack<T>::pop()
{
T data;
if(queue1.empty()&&queue2.empty())
{
cout<<"underflow"<<endl;
exit(1);
}
if(!queue1.empty())//對工作的隊列進行操作
{
//出隊到隊列中只剩下一個元素,就可以出棧了
while(queue1.size()>1)
{
T temp=queue1.front();
queue1.pop();
queue2.push(temp);
}
data=queue1.front();
queue1.pop();
}
else if(!queue2.empty())
{
while(queue2.size()>1)
{
T temp=queue2.front();
queue2.pop();
queue1.push(temp);
}
data=queue2.front();
queue2.pop();
}
return data;
}
template <typename T>
void QueueToStack<T>::push(const T &node)
{
if(!queue1.empty())//每次都壓入到工作的隊列中
queue1.push(node);
else
queue2.push(node);
}
int main()
{
QueueToStack<int> queue;
queue.push(1);
queue.push(2);
queue.push(3);
cout<<queue.pop()<<endl;
cout<<queue.pop()<<endl;
cout<<queue.pop()<<endl;
}
上面的top沒有寫
#include <cstdlib>
#include <iostream>
#include <assert.h>
#include <deque>
using namespace std;
/*兩個隊列模擬一個堆棧*/
/*隊列A、B
入棧:將元素依次壓入到非空的隊列,第一個元素壓倒對列A
出棧:把隊列A的前n-1個元素倒到隊列B,把第n個元素去掉。此時數據在B中,下次操作,則對B操作。
棧頂:把隊列A的前n-1個元素倒到隊列B,把第n個元素作為棧頂*/
template <typename T>
class MyStack
{
public:
//入棧,第一個元素進到隊列deque1,以後每個元素進到非空的隊列
void push(T element)
{
if (deque1.empty() && deque2.empty())
{
deque1.push_back(element);
}
else if (!deque1.empty() && deque2.empty())
{
deque1.push_back(element);
}
else if (deque1.empty() && !deque2.empty())
{
deque2.push_back(element);
}
}
//出棧,將非空隊列的前n-1個元素轉移到另一個空的隊列,刪除非空隊列的第n個元素
void pop()
{
if (!deque1.empty())
{
int size = deque1.size();
for (int i=0; i<size-1; i++)
{
deque2.push_back(deque1.front());
deque1.pop_front();
}
deque1.pop_front();
}
else
{
int size = deque2.size();
for (int i=0; i<size-1; i++)
{
deque1.push_back(deque2.front());
deque2.pop_front();
}
deque2.pop_front();
}
}
//棧頂元素,將非空隊列的前n-1個元素轉移到另一個空的隊列,將非空隊列的第n個元素返回
T top()
{
if (!deque1.empty())
{
int size = deque1.size();
for (int i=0; i<size-1; i++)
{
deque2.push_back(deque1.front());
deque1.pop_front();
}
T temp = deque1.front();
deque1.pop_front();
deque2.push_back(temp);
return temp;
}
else
{
int size = deque2.size();
for (int i=0; i<size-1; i++)
{
deque1.push_back(deque2.front());
deque2.pop_front();
}
T temp = deque2.front();
deque2.pop_front();
deque1.push_back(temp);
return temp;
}
}
//棧是否為空
bool empty()
{
return (deque1.empty()&&deque2.empty());
}
private:
deque<T> deque1;
deque<T> deque2;
};
int main(int argc, char *argv[])
{
MyStack<int> my;
for (int i=0; i<10; i++)
{
my.push(i);
}
while (!my.empty())
{
cout<<my.top()<<" ";
my.pop();
}
cout<<endl;
}
基本數據結構 - 棧和隊列