首页 > 代码库 > 数据结构与算法系列研究二——栈和队列
数据结构与算法系列研究二——栈和队列
栈和队列的相关问题分析
一、栈和队列定义
栈和队列是两种重要的数据结构。从结构特性角度看,栈和队列也是线性表,其特殊性在于它们的基本操作是线性表的子集,是操作受限的线性表,可称为限定性的数据结构;从数据类型角度看,其操作规则与线性表大不相同,是完全不同于线性表的抽象数据类型。
图1 栈的结构 图2 队列的结构
1.1、栈是限定在表的一端进行插入和删除操作的线性表。
插入:称为进栈(或压栈)。
删除:称为出栈(或退栈)。
栈顶(top):允许进行插入和删除操作的一端。
栈底(bottom):不允许插入和删除操作的一端。
基本操作:
Initstack(&s): 栈初始化
Destroystack(&s): 撤消栈
Clearstack(&s): 置栈空
Stackempty(s) : 判栈空
Stacklength(S): 求栈中元素个数
Gettop(S,&e): 取栈顶元素
Push(&S,e): 入栈
Pop(&S,&e): 出栈
用线性链表表示栈的存储结构,称为链栈。链表头指针始终指向栈顶,同样用top表示。 无必要加头结点,栈空时:top=NULL入栈:每当一个元素进栈时,申请一个新结点,插入到表头。出栈:每当一个元素出栈时,从表头释放一个结点。
1.2、队列是限定在表的一端进行插入,在另一端进行删除的线性表。
队头(Front):允许删除的一端。
队尾(Rear):允许插入的一端。
基本操作:
InitQueue (&Q): 队列初始化
DestroyQueue (&Q): 撤消队列
ClearQueue (&Q): 置队列空
QueueEmpty(Q) : 判队列空
Queuelength(Q): 求队列元素个数
Gethead(Q,&e): 取队列头元素
EnQueue (&Q,e): 入队列
DeQueue (&Q,&e): 出队列
二、用栈实现中缀式求解
编程实现中缀式求解,设算符只有+,-,*,/,(,)。操作数0~9(可当字符用)。
2.1、输入输出设计
1.输入 从控制台读进一个字符串,由于设计时运用了string类中的成员函数,故此此处省略‘#’,只读进一个字符串。读入的时候可能有不同的情况,此处简单起见,设置默认情况下读入的操作数为0~9,另外还有+,-,*,/运算符,但是要按照中缀式的顺序从左到右输入。
2.输出 此程序的功能是求一个中缀式的值,返回值应该是算式的结果,根据一系列的运算最终输出结果。其结果应该为一个实数值,适宜选用double类型存放。
2.2、关键数据结构与算法描述
数据结构:
通过对问题的分析,此处选用栈类型的存储结构最为适宜,因为其先进后出的特性可以模拟中缀式的计算过程。具体构造如下,此处构造为类的形式:
1 class Stack{ 2 public: 3 Stack(){top=-1;}//构造函数,进行初始化操作 4 ~Stack(){}//析构函数 5 bool IsEmpty(){ 6 if(top==-1) 7 return true; 8 else 9 return false; 10 }//判断栈是否为空 11 //返回栈顶元素 12 T gettop() 13 { 14 return a[top]; 15 } 16 //将元素压栈 17 void push(T num) 18 { 19 if(top>=MAXSIZE) 20 return; 21 a[++top]=num; 22 } 23 //将元素弹出栈 24 T pop() 25 { 26 if(top==-1) 27 return OVER; 28 return a[top--]; 29 } 30 private: 31 T a[MAXSIZE]; 32 int top; 33 };
此处运用C++语言对模板栈进行了定义,定义了指针top和数组a[MAXSIZE];为以后的进栈和退栈操作提供了可能。
2.3、算法描述
要完成中缀式求值,最重要的是对读进的字符进行适当的处理,从而求出结果。
1.若读到字符就直接如数据栈;
2.若读进的是‘(’,直接入符号栈;
3.若读进的是‘)’,就要判断在‘(’前面有无符号,若有符号,就要进行循环每次将数据栈中的两个元素退栈,按照一定顺序和已经退栈的符号进行运算,运算结果再压入数据栈中,直至到‘(’或为空栈(正常情况下不可能),弹出‘(’;
4.若读进的是符号,就要判断优先级,若字符优先级高于符号栈的顶部优先级或者为空栈直接压入符号栈,否则进行循环,弹出顶部元素且再从数据栈弹出两个元素进行运算,结果压入数据栈。直至字符优先级大于栈顶元素或为空栈,此时再将字符压入符号栈中。一直循环上述步骤,直至字符串被读完,然后对符号栈进行查看,若不为空,则继续进行运算,直至最后为空,此时数据栈中必只有一个元素,此元素即为所求结果。输出即可。
具体算法如下:
1 double EvaluateExpression(string &s) 2 { 3 double answer; 4 for(int i=0;i<s.size();i++) 5 { 6 if(s[i]>=‘0‘&&s[i]<=‘9‘)//若为0~9字符,直接进数据栈 7 { 8 OPND.push(s[i]-‘0‘); 9 } 10 else 11 if(s[i]==‘(‘)//若为左括号直接进栈 12 { 13 OPTR.push(s[i]); 14 } 15 else if(s[i]==‘)‘)//若为右括号需判断 16 { 17 while((OPTR.gettop())!=‘(‘) 18 Count(); 19 OPTR.pop(); 20 } 21 else if(OPTR.IsEmpty()||Rank(s[i])>Rank(OPTR.gettop()))//优先级高直接入栈 22 { 23 OPTR.push(s[i]); 24 } 25 else 26 { 27 while(Rank(s[i])<=Rank(OPTR.gettop())&&!OPTR.IsEmpty())//否则,先计算已有元素 28 Count(); 29 OPTR.push(s[i]);//然后再进栈 30 } 31 } 32 while(!OPTR.IsEmpty())//若运算符栈为空,就要计算 33 { 34 Count(); 35 } 36 37 answer=OPND.pop();//得到最后结果,清空栈 38 return answer; 39 }
2.4、测试与理论
(注意,此处的除号是数学意义上的除法,不是c语言上的整除)
测试数据有: 理论结果为:
2+(4-1)/2*5; 9.5
(2+3-5/2)*5/1+8 20.5
((3+5)*6+1)/7 7
((2+2)/2+3*5-8/2)/2-1 5.5
运行程序得到的结果为:
2.5、源代码(附录)
中缀式求解的代码:
1 #include"iostream" 2 #include"string" 3 using namespace std; 4 #define MAXSIZE 200 5 #define OVER -2 6 //创建模板栈,因有两个栈 7 template <typename T> 8 class Stack{ 9 public: 10 Stack(){top=-1;}//构造函数,进行初始化操作 11 ~Stack(){}//析构函数 12 bool IsEmpty(){ 13 if(top==-1) 14 return true; 15 else 16 return false; 17 }//判断栈是否为空 18 19 //返回栈顶元素 20 T gettop() 21 { 22 return a[top]; 23 } 24 //将元素压栈 25 void push(T num) 26 { 27 if(top>=MAXSIZE) 28 return; 29 a[++top]=num; 30 } 31 //将元素弹出栈 32 T pop() 33 { 34 if(top==-1) 35 return OVER; 36 return a[top--]; 37 } 38 private: 39 T a[MAXSIZE]; 40 int top; 41 }; 42 //定义两个栈 43 Stack<char> OPTR; 44 Stack<double> OPND; 45 //比较优先级,优先级越高,就越先计算 46 int Rank(char c) 47 { 48 if((c==‘(‘)||(c==‘)‘)) 49 return 1; 50 else 51 if((c==‘+‘)||(c==‘-‘)) 52 return 2; 53 else if((c==‘*‘)||(c==‘/‘)) 54 return 3; 55 else 56 return -1; 57 } 58 //对式子中的加减乘除进行计算 59 void Count() 60 { 61 double temp; 62 switch(OPTR.pop()) 63 { 64 case‘+‘: 65 temp=OPND.pop(); 66 temp+=OPND.pop(); 67 OPND.push(temp); 68 break; 69 case‘-‘: 70 temp=OPND.pop(); 71 temp=OPND.pop()-temp; 72 OPND.push(temp); 73 break; 74 case‘*‘: 75 temp=OPND.pop(); 76 temp=OPND.pop()*temp; 77 OPND.push(temp); 78 break; 79 case‘/‘: 80 temp=OPND.pop(); 81 temp=OPND.pop()/temp; 82 OPND.push(temp); 83 break; 84 default: 85 cout<<"操作符有误"<<endl; 86 break; 87 } 88 } 89 /********************核心算法,计算表达式的算法*************/ 90 double EvaluateExpression(string &s) 91 { 92 double answer; 93 for(int i=0;i<s.size();i++) 94 { 95 if(s[i]>=‘0‘&&s[i]<=‘9‘)//若为0~9字符,直接进数据栈 96 { 97 OPND.push(s[i]-‘0‘); 98 } 99 else 100 if(s[i]==‘(‘)//若为左括号直接进栈 101 { 102 OPTR.push(s[i]); 103 } 104 else if(s[i]==‘)‘)//若为右括号需判断 105 { 106 while((OPTR.gettop())!=‘(‘) 107 Count(); 108 OPTR.pop(); 109 } 110 else if(OPTR.IsEmpty()||Rank(s[i])>Rank(OPTR.gettop()))//优先级高直接入栈 111 { 112 OPTR.push(s[i]); 113 } 114 else 115 { 116 while(Rank(s[i])<=Rank(OPTR.gettop())&&!OPTR.IsEmpty())//否则,先计算已有元素 117 Count(); 118 OPTR.push(s[i]);//然后再进栈 119 } 120 } 121 while(!OPTR.IsEmpty())//若运算符栈为空,就要计算 122 { 123 Count(); 124 } 125 126 answer=OPND.pop();//得到最后结果,清空栈 127 return answer; 128 } 129 /**********主函数用于测试************************/ 130 int main() 131 { 132 string s; 133 double answer1; 134 char c; 135 while(1) 136 { 137 138 cout<<"请输入一个中缀式:"<<endl; 139 cin>>s; 140 141 answer1=EvaluateExpression(s); 142 cout<<endl<<s<<"="<<answer1<<endl; 143 cout<<"是否继续?(Y||N)"<<endl; 144 cin>>c; 145 if(c==‘N‘||c==‘n‘) 146 break; 147 } 148 return 0; 149 }
将中缀式转换为后缀式
1 void ShowChange( string &s) 2 { 3 for(int i=0;i<s.size();i++) 4 { 5 if(s[i]>=‘0‘&&s[i]<=‘9‘) 6 { 7 cout<<s[i]; 8 } 9 else if(s[i]==‘(‘) 10 { 11 OPTR.push(s[i]); 12 } 13 else if(s[i]==‘)‘) 14 { 15 while(OPTR.gettop()!=‘(‘&&!OPTR.IsEmpty()) 16 { 17 cout<<OPTR.pop( ); 18 } 19 OPTR.pop( ); 20 } 21 else if(Rank(s[i])<=Rank(OPTR.gettop())) 22 { 23 while(OPTR.gettop()!=‘(‘&&!OPTR.IsEmpty()) 24 cout<<OPTR.pop(); 25 OPTR.push(s[i]); 26 } 27 else if(Rank(s[i])>Rank(OPTR.gettop())) 28 { 29 OPTR.push(s[i]); 30 } 31 } 32 while(!OPTR.IsEmpty()) 33 { 34 cout<<OPTR.pop(); 35 } 36 }
将中缀式转换为前缀式
1 void Z2QChange(string &s) 2 { 3 for(int i=s.size()-1;i>=0;i--) 4 { 5 if(s[i]>=‘0‘&&s[i]<=‘9‘) 6 { 7 OPND.push(s[i]); 8 } 9 else if(s[i]==‘)‘) 10 { 11 OPTR.push(s[i]); 12 } 13 else if(s[i]==‘(‘) 14 { 15 while(OPTR.gettop()!=‘)‘&&!OPTR.IsEmpty()) 16 { 17 OPND.push(OPTR.pop()); 18 } 19 OPTR.pop(); 20 } 21 else if(Rank(s[i])>=Rank(OPTR.gettop())||OPTR.IsEmpty()||OPTR.gettop()==‘)‘) 22 { 23 OPTR.push(s[i]); 24 } 25 else 26 { 27 while(Rank(s[i])<Rank(OPTR.gettop())&&!OPTR.IsEmpty()) 28 OPND.push(OPTR.pop()); 29 OPTR.push(s[i]); 30 31 } 32 } 33 while(!OPTR.IsEmpty()) 34 { 35 OPND.push(OPTR.pop()); 36 } 37 while(!OPND.IsEmpty()) 38 { 39 cout<<OPND.pop(); 40 } 41 }
三、入栈出栈问题
入栈次序1,2,3,4,则2在3前面出栈的情况有多少种?
解:根据树状图将所有弹出的次序找出来,从中找到符合要求的情况
易得结果共有7种,出栈情况分别为1234,1243,2134,2143,2314,2341,2431。
四、约瑟夫问题
链表实现:
1 #include<stdio.h> 2 #include<malloc.h> 3 typedef struct List 4 { 5 int data; 6 struct List *next; 7 }LinkList; 8 int main() 9 { 10 LinkList *L,*r,*s; 11 L = (LinkList *)malloc(sizeof(LinkList)); 12 r =L; 13 int n,i; 14 int k; 15 scanf("%d%d",&n,&k); 16 for(i = 1;i<=n;i++) 17 { 18 s = (LinkList *)malloc(sizeof(LinkList)); 19 s->data =http://www.mamicode.com/ i; 20 r->next = s; 21 r= s; 22 } 23 24 r->next =L->next; 25 LinkList *p; 26 p = L->next; 27 28 while(p->next != p) 29 { 30 for(i = 1;i<k-1;i++) 31 { 32 p = p->next; 33 } 34 p->next = p->next->next; 35 p = p->next; 36 } 37 printf("%d",p->data); 38 return 0; 39 }
数组队列实现:
#include<stdio.h> int main() { int n, k; scanf("%d%d", &n, &k); int i; int a[1001]; int dead = 0; int num = 0; for (i = 1; i<=n; i++) { a[i] = i; } for (i = 1;; i++) { if (i > n) { i = i%n; } if (a[i] > 0) num++; if (k == num && dead != n-1) { num = 0; a[i] = 0; dead++; } else if(k == num && dead == n-1) { printf("%d", a[i]); break; } } return 0; }
公式法实现:
1 #include <iostream> 2 using namespace std; 3 const int m = 3; 4 int main() 5 { 6 int n, f = 0; 7 cin >> n; 8 for (int i = 1; i <= n; i++) f = (f + m) % i; 9 cout << f + 1 << endl; 10 }
数据结构与算法系列研究二——栈和队列