資料結構(棧和佇列)

夢碼發表於2021-01-03

1. 數制轉換

【問題描述】

十進位制數N和其他d進位制數的轉換是計算機實現計算的基本問題,其解決方法很多,其中一個簡單演算法基於下列原理:

N = (N div d) * d +(N mod d)

其中div是整除運算,mod是求餘運算。

例如(2018)10= (3742)8= (7E2)16
【輸入形式】一個十進位制數以及d進位制數(2<=d<=16)

【輸出形式】該十進位制數所對應d進位制數

【樣例輸入】2018 16

【樣例輸出】7E2

#include<iostream>
using namespace std;
void output(int num) {
	if(num<10) {
		cout<<num;
	} else {
		char b='A' + num-10;
		cout<<b;
	}
}
void algorithm(int n,int d) {
	if(n<d) {
		output(n);
		return ;
	} else {
		algorithm(n/d,d);
		output(n%d);
	}
}
int main() {
	int d,n;
	cin>>n>>d;
	algorithm(n,d);
	return 0;
}

2. 括號匹配

【問題描述】

假設一個算術表示式中可以包含三種括號:圓括號“(”和“)”,方括號“[”和“]”以及花括號“{”和“}”,且這三種括號可按任意的次序巢狀使用,如4.png。判斷給定表示式中所包含括號是否正確配對。

【輸入形式】一個算術表示式(中間沒有空格)。

【輸出形式】該表示式中的括號是否正確配對。

【樣例輸入】( { [ ] }

【樣例輸出】NO

【樣例說明】當括號不匹配時,有可能左括號和有括號等長度不匹配,也有可能左括號長度大於右括號,還有可能是右括號長度大於左括號,所以出棧操作要考慮全面。

#include<iostream>
#include<stack>
#include<string>
using namespace std;
int main() {
	string temp;
	cin>>temp;
	stack<char>tool; //建立字元棧
	for(int i=0; i<temp.length(); i++) {
		if(temp[i]=='('||temp[i]=='['||temp[i]=='{')//左括號入棧
			tool.push(temp[i]);
		else if(temp[i]=='['&&tool.top()!=']') {
			cout<<"NO"<<endl;
			return 0;
		} else if(temp[i]=='{'&&tool.top()!='}') {
			cout<<"NO"<<endl;
			return 0;
		} else if(temp[i]=='('&&tool.top()!=')') {
			cout<<"NO"<<endl;
			return 0;
		} else {
			tool.pop();
		}
	}
	if(!tool.empty()) {//括號有多餘的未參與匹配
		cout<<"NO"<<endl;
		return 0;
	}
	cout<<"YES"<<endl;//滿足條件
	return 0;
}

3. 合法序列

假設以I和O分別表示入棧和出棧操作,棧的初態和終態均為空,入棧和出棧的操作序列可表示為僅由I和O組成的序列,稱可以操作的序列為合法序列,否則稱為非法序列。

寫出一個演算法,判定所給的操作序列是否合法。若合法,輸出1,否則輸出0。

【輸入形式】由I和O組成的被判定序列。

【輸出形式】若合法,輸出1,否則輸出0。

【樣例輸入】IOIIOIOO

【樣例輸出】1

#include<iostream>
#include<stack>
#include<string>
using namespace std;
int main() {
	stack<char>tool;
	string temp;
	cin>>temp;
	for(int i=0; i<temp.length(); i++) {
		if(temp[i]=='I') //為I入棧
			tool.push(temp[i]);
		else if(tool.empty()) { //棧空
			cout<<"0"<<endl;
			return 0;
		} else { //為 O 出棧 
			tool.pop();
		}
	}
	if(tool.empty()) {
		cout<<"1"<<endl;
	} else {
		cout<<"0"<<endl;
	}
	return 0;
}

4. 中綴表示式轉字尾表示式並求值

【問題描述】在表示式求值時,為了處理方便,通常把中綴表示式轉成等價的字尾表示式,然後計算表示式的結果。
【輸入形式】一箇中綴表示式
【輸出形式】表示式的計算結果
【樣例輸入】3*(4+2)/2-5
【樣例輸出】4
【樣例說明】注意運算子是因為狀態

#include <iostream>
#include <math.h>
#include<stdio.h>
using namespace std;
const int StackSize=100;
template <class DataType>
class SeqStack {
	private:
		DataType data[StackSize];
		int top;
	public:
		SeqStack() {
			top=-1;
		}
		~SeqStack() {}
		void push(DataType x);  //入棧操作,將元素x入棧
		void pop();  //出棧操作
		DataType Gettop();  //取棧頂元素
		int Empty() {
			return top==-1?1:0;   //判斷棧是否為空
		}
		int getSize() {
			return top;   //返回棧的長度
		}
};
template<class DataType>
void SeqStack<DataType>::push(DataType x) {
	top++;
	data[top]=x;

}
template<class DataType>
void  SeqStack<DataType>::pop() {
	top--;

}
template<class DataType>
DataType  SeqStack<DataType>::Gettop() {
	if(top==-1) cout<<"empty";
	else
		return data[top];
}
bool Number(char ch) { //判斷是否為數字,是則返回true
	if((ch-'0')>=0&&(ch-'0')<=9)
		return true;
	return false;

}

void InPut(char*& str) { //接收輸入的中綴表示式的函式,並簡單判斷是否合法
	//cout << "Please Enter What You Want To calculation:" << endl;
	while (1) {
		cin >> str;
		if (Number(str[0])) { //中綴表示式的第一個必定是數字,如果不是,則非法
			break;
		} else {
			cout << "Illegal Input,Please Input Again:";
			delete[] str;
		}
	}
}

int GetPriority(char sy) { //設定各個操作符的優先順序
	switch (sy) {
		case '#':
			return 0;
		case '(':
			return 1;
		case '+':
		case '-':
			return 3;
		case '*':
		case '/':
			return 5;
		case ')':
			return 6;
		default:
			return -1;
	}
}


void AddSpace(char*& arr) { //給轉成的字尾表示式的數字和符號新增空格,區分每個字元
	*arr = ' ';
	arr++;
}

char* GetBack() { //獲取字尾表示式的函式
	char* middle = new char[30];
	char* back = new char[30];
	char* backend = back;
	InPut(middle);
	SeqStack<char> s;
	s.push('#');
	while (*middle) {
		if (Number(*middle) || *middle == '.') { //如果是數字或者小數的話,直接輸出
			*back = *middle;
			back++, middle++;
		} else {
			if (Number(*(back - 1)))//只有他的上一個時數字的話,才繼續給空格
				//否則遇到多個操作符,則輸出域會存在多個空格
			{
				//*back = ' ';
				//back++;
				AddSpace(back);
			}
			if (*middle == ')') { //如果右括號的話,輸出所有操作符直到遇到左括號,並拋棄相對應的一堆括號
				while (s.Gettop() != '(') {
					*back = s.Gettop();
					s.pop();
					back++;
					AddSpace(back);
				}
				middle++;
				s.pop();//拋棄左括號
			} else if (*middle == '(') { //遇到左括號,則進入棧
				s.push(*middle);
				middle++;
			} else if (GetPriority(*middle) > GetPriority(s.Gettop())) { //如果棧內的操作符優先順序高於棧外的優先順序,則入棧
				s.push(*middle);
				middle++;
			} else if (GetPriority(*middle) <= GetPriority(s.Gettop()))
				//如果棧內的操作符優先順序低於或等於棧外的優先順序,輸出棧內的符號,併入棧棧外的符號
			{
				*back = s.Gettop();
				s.pop();
				s.push(*middle);
				back++;
				middle++;
				AddSpace(back);
			}
		}
	}
	while (s.Gettop() != '#') { //中綴表示式遍歷完成,但是=棧中還有符號存在,一一出棧輸出
		AddSpace(back);
		*back = s.Gettop();
		s.pop();
		back++;
	}
	*back = '\0';
	//cout << "The Back Is: " << backend << endl;
	return backend;
}

double GetNumber(char*& arr) {
	//因為輸出為char*,所以可能兩位數以上的數字被拆開,此函式為正確得到數字
	double sum[10] = { 0 };
	int i = 0;
	double result = 0;
	while (*arr != ' ') {
		sum[i] = *arr-48;
		i++;
		arr++;
	}
	int k = i - 1;
	for (int j = 0; j < i; j++,k--) {
		result += (sum[j] * pow(10, k));
	}
	return result;
}

double Cauculate(char ch, double left, double right) { //各個操作符對應的操作
	switch (ch) {
		case '+':
			return left + right;
		case '-':
			return left - right;
		case '*':
			return left * right;
		case '/':
			return left / right;
		default:
			return 0;
			break;
	}
}

double CountBack(char* back) {
	SeqStack<double> s;
	while (*back) {
		if (Number(*back)) { //遇到數字
			s.push(GetNumber(back));//將正確的數字入棧
		} else if (*back == ' ') {
			back++;//遇到空格跳過
		} else {
			double a = s.Gettop();
			s.pop();
			double b = s.Gettop();
			s.pop();
			s.push(Cauculate(*back, b, a));//遇到符號時,取棧頂的第二個數和第一個數求解,併入棧
			back++;
		}
	}
	while (s.getSize() >= 2) { //最終棧記憶體在的數大於2時,繼續計算,直到只剩下一個數
		double a = s.Gettop();
		s.pop();
		double b = s.Gettop();
		s.pop();
		s.push(Cauculate(*back, b, a));
	}
	//返回這個數字,既是最終結果
	return s.Gettop();
}
int main() {
	cout<<CountBack(GetBack())<<endl;;
	return 0;
}


5. 迴圈佇列的入隊和出隊

在迴圈佇列中設定一個標誌flag,當front=rear且flag=0時為隊空,當front=rear且flag=1時為隊滿。編寫相應的入隊和出隊演算法。

【輸入說明】入隊次數m以及m個入隊元素;(中間用空格隔開)

出隊次數n;

【輸出說明】執行m次入隊操作,如果隊滿輸出“overflow”;在執行n次出隊操作後,列印佇列中的元素:如果佇列為空則輸出“Empty”。

【輸入樣例】

3

5 3 6

2

【輸出樣例】

6

#include <iostream>
using namespace std;

const int MaxSize = 5;

template <class DataType>
class cirQueue {
	public:
		cirQueue();
		bool enQueue(DataType elem);
		bool deQueue();
		void printQueue();
	private:
		DataType data[MaxSize];
		int front,rear;
		int flag;//區別佇列空或滿
};

template <class DataType>
cirQueue<DataType>::cirQueue() {
	front = rear = MaxSize-1;
	flag = 0;
}

template <class DataType>
bool cirQueue<DataType>::enQueue(DataType elem) {
	if(!(front==rear&&flag==1)) {//初始flag=0
		rear=(rear+1)%MaxSize;
		data[rear]=elem;
		if(rear==front&&flag==0)//此時剛好隊滿,至flag為1
			flag=1;
		return true;
	} else {
		return false;
	}

}

template <class DataType>
bool cirQueue<DataType>::deQueue() {
	if(front==rear&&flag==0) {//隊空
		return false;
	} else {
		front=(front+1)%MaxSize;
		return true;
	}

}

template <class DataType>
void cirQueue<DataType>::printQueue() {
	if(front == rear && flag == 0)
		cout<<"Empty"<<endl;
	else {
		int i=(front+1)%MaxSize;
		while(i<=rear) {
			cout<<data[i]<<" ";
			if(i==rear)break;
			i = (i+1)%MaxSize;
		}
		cout<<endl;
	}
}
int main() {
	cirQueue<int> Q;
	int m,n;

	cin>>m;
	while(m--) {
		int elem;
		cin>>elem;
		bool res = Q.enQueue(elem);
		if(!res) {
			cout<<"overflow"<<endl;
			return 0;
		}
	}
	cin>>n;
	while(n--) {
		bool res = Q.deQueue();
		if(!res) break;
	}
	Q.printQueue();
	return 0;
}

6. 迴圈佇列重新排列順序棧

設順序棧S中有2n個元素,從棧頂到棧底的元素依次為1.png,要求通過一個迴圈佇列重新排列棧中元素,使得從棧頂到棧底的元素依次為2.png。請設計演算法實現該操作,要求空間複雜度和時間複雜度均是O(n)。

【輸入格式】棧的長度(n/2)以及元素(中間用空格隔開)

【輸出格式】經過佇列相應操作後的序列。

【輸入樣例】

3

1 2 3 4 5 6

【輸出樣例】6 4 2 5 3 1

提示:

操作步驟:

1、將所有元素出棧併入隊;

2、依次將佇列元素出隊,如果是偶數結點,則再入隊,如果是奇數結點,則入棧;

3、將奇數結點出棧併入隊;

4、將偶數結點出隊併入棧;

5、將所有元素出棧併入隊;

6、將所有元素出隊併入棧即為所求;

#include <iostream>
#include <stack>
#include <queue>
using namespace std;

stack<int> seqStack;
queue<int> cirQueue;
int n;
void init(int len) {
	for(int i=0; i<2*len; ++i) {
		int elem;
		cin>>elem;
		seqStack.push(elem);
	}
}
void exchange() {
	// 1、將所有元素出棧併入隊;
	while(!seqStack.empty()) {
		cirQueue.push(seqStack.top());
		seqStack.pop();
	}
	// 2、依次將佇列元素出隊,如果是偶數結點,則再入隊,如果是奇數結點,則入棧;
	for(int i=0; i<2*n; i++) {
		int temp=cirQueue.front();
		cirQueue.pop();
		if(i%2==0) {//偶數結點,則再入隊
			cirQueue.push(temp);
		} else {//是奇數結點,則入棧
			seqStack.push(temp);
		}
	}
	//3、將奇數結點出棧併入隊;
	while(!seqStack.empty()) {
		cirQueue.push(seqStack.top());
		seqStack.pop();
	}
	//4、將偶數結點出隊併入棧;
	for(int i=1; i<=n; i++) {
		int temp=cirQueue.front();
		cirQueue.pop();
		seqStack.push(temp);
	}
	//5、將所有元素出棧併入隊;
	while(!seqStack.empty()) {
		cirQueue.push(seqStack.top());
		seqStack.pop();
	}
	//6、將所有元素出隊併入棧即為所求;
	for(int i=1; i<=2*n; i++) {
		int temp=cirQueue.front();
		cirQueue.pop();
		seqStack.push(temp);
	}

}
void printStack() {
	while(!seqStack.empty()) {
		cout<<seqStack.top()<<" ";
		seqStack.pop();
	}
	cout<<endl;
}
int main() {
	cin>>n;
	init(n);
	exchange();
	printStack();
	return 0;
}

7. 迴圈鏈佇列的入隊和出隊

假設以不帶頭結點的迴圈連結串列表示非空佇列,並且只設一個指標指向隊尾結點,但不設頭指標。試設計相應的入隊和出隊的演算法。

【輸入說明】

入隊次數m以及m個入隊元素;(中間用空格隔開)

出隊次數n;

【輸出說明】執行m次入隊操作和n次出隊操作後,列印佇列中的元素:如果佇列為空則輸出“Empty”。

【輸入樣例】

3

5 3 6

1

【輸出樣例】

3 6
#include <iostream>
using namespace std;

template <class DataType>
struct node {
	DataType data;
	node<DataType>* next;
};

template <class DataType>
class cirLinkQueue {
	public:
		cirLinkQueue();
		cirLinkQueue(DataType a[],int n);
		void enQueue(DataType elem);
		bool deQueue();
		void printQueue();
	private:
		node<DataType>* rear;
};

template <class DataType>
cirLinkQueue<DataType>::cirLinkQueue() {
	rear = NULL;
}

template <class DataType>
cirLinkQueue<DataType>::cirLinkQueue(DataType a[],int n) {
	if(n == 1) {
		rear = new node<DataType>;
		rear->data = a[0];
		rear->next = rear;
	} else {
		rear = new node<DataType>;
		rear->data = a[0];
		rear->next = rear;
		for(int i=1; i<n; ++i) {
			node<DataType>* s = new node<DataType>;
			s->data = a[i];
			s->next = rear->next;
			rear->next = s;
			rear = s;
		}
	}
}

template <class DataType>
void cirLinkQueue<DataType>::enQueue(DataType elem) {
	if(rear==NULL) {// 當前佇列為空 
		node<DataType> *temp=new node<DataType>;
		temp->data=elem;
		temp->next=temp;//形成首尾迴圈 
		rear=temp;
	} else {
		node<DataType> *temp=new node<DataType>;
		temp->data=elem;
		temp->next=rear->next;
		rear->next=temp;
		rear=temp;
	}

}

template <class DataType>
bool cirLinkQueue<DataType>::deQueue() {
	if(rear==NULL)
		return false;
	if(rear->next==rear) {
		delete rear;
		rear=NULL;
		return true;
	} else {
		node<DataType>*s=rear->next;
		rear->next=rear->next->next;
		delete s;
		return true;
	}

}

template <class DataType>
void cirLinkQueue<DataType>::printQueue() {
	if(rear == NULL)
		cout<<"Empty"<<endl;
	else {
		node<DataType>* p = rear->next;
		while(p != rear) {
			cout<<p->data<<" ";
			p = p->next;
		}
		cout<<p->data<<endl;
	}
}
int main() {
	cirLinkQueue<int> Q;
	int m,n;
	cin>>m;
	while(m--) {
		int elem;
		cin>>elem;
		Q.enQueue(elem);
	}
	cin>>n;
	while(n--) {
		bool res = Q.deQueue();
		if(!res) break;
	}
	Q.printQueue();
	return 0;
}

結語

如果你發現文章有什麼問題,歡迎留言指正。
如果你覺得這篇文章還可以,別忘記點個贊加個關注再走哦。
如果你不嫌棄,還可以關注微信公眾號———夢碼城(持續更新中)。
夢碼在這裡感激不盡!!

相關文章