注:不足及错误请指正
+qq 1366963396讨论
栈(stack)时限定仅在表位进行插入和删除操作的线性表。
栈又称为后进先出的线性表。
一.顺序栈
//建栈
void CreatStack(stack* s)
{
s->top = -1;
int i = 1;
int data;
do
{
printf("请输入第%d个元素:\n",i);
scanf("%d", &data);
if (data != -1)
{
s->top++;
s->data[s->top] = data;
}
i++;
} while (i<=MAXSIZE&&data!=-1);
}
//设置空栈
void SetEmpty(stack* s)
{
s->top = -1;
}
//判断是否空栈
bool IsEmpty(stack* s)
{
return ((s->top >= 0) ? false : true);
}
//进栈
stack* StackPush(stack* s, int x)
{
if (s->top == MAXSIZE - 1)//栈满
{
printf("OverFlow\n");
return NULL;
}
else
{
s->top++;//栈顶加一
s->data[s->top] = x;//新元素赋给栈顶空间
}
return s;
}
//出栈
int StackPop(stack* s)
{
if (IsEmpty(s) == true)//是否为空栈
{
printf("underflow\n");
return NULL;
}
else
{
return s->data[s->top--]; //栈顶出去,top--;
}
}
//读取栈顶元素
int ShowTop(stack* s)
{
if (IsEmpty(s))////是否为空栈
{
printf("underflow\n");
return NULL;
}
else
{
return s->data[s->top];
}
}
二.两栈共享空间
关键思路:
让一个栈的栈底为数组的始端,即下标为0处,另一个栈为数组的末端,即下标为数组长度n-1处。这样,如果两个栈增加元素,就是两端点向中间靠拢,只要top1和top2不见面,两个栈就可以一直使用。所以,当栈1为空时,栈1的top1为-1;而当栈2为空时,top2为n。反之,top1为n-1时,栈1满;top2为0时栈2满。更多的情况是两个栈顶之间相差1时,即top1+1==top2时为栈满。
//建栈
void CreatDoubleStack(doubleStack* s,int stackNumber)
{
int i = 1;//计数
int data;
if (stackNumber == 1)//栈1
{
s->top1 = -1;
do
{
printf("请输入栈1第%d个元素:", i);
scanf("%d", &data);
if (data!=-1)
s->data[++s->top1] = data;//top+1后,data传进去
i++;//计数
} while (data != -1 && s->top1 + 1 != s->top2);//判断是否栈满
}
if (stackNumber == 2)//栈2
{
s->top2 = MAXSIZE;//栈2为空
do
{
printf("请输入栈2第%d个元素:", i);
scanf("%d", &data);
if (data!=-1)
s->data[--s->top2] = data;//data传进去
i++;//计数
} while (data!=-1&&s->top1+1!=s->top2);//判断栈是否为空
}
}
//设置空栈
void SetEmpty(doubleStack* s,int stackNumber)
{
if (stackNumber==1)
s->top1 = -1;//栈1为空
else
s->top2 =MAXSIZE;//栈2为空
}
//判断是否为空栈
int IsEmpty(doubleStack* s,int stackNumber)
{
if (stackNumber == 1)
{
if (s->top1 == -1)
{
return 1;//1表示栈1为空
}
}
if (stackNumber == 2)
{
if (s->top2 == MAXSIZE)
return 2;//2表示栈2为空
}
return 0;//0表示都不为空
}
//入栈
doubleStack* StackPush(doubleStack* s, int data, int stackNumber)
{
if (s->top1+1==s->top2)//栈满
{
printf("overflow\n");
return NULL;
}
if (stackNumber == 1)//进栈1
{
s->top1++;
s->data[s->top1] = data;
}
else if (stackNumber == 2)//进栈2
{
s->data[--s->top2] = data;
}
return s;
}
//出栈
int StackPop(doubleStack* s, int stackNumber)
{
if (stackNumber == 1)//出栈1
{
if (s->top1 == -1)//空栈
{
printf("Empty\n");
return NULL;
}
return s->data[s->top1--];//不为空,出栈
}
if (stackNumber == 2)
{
if (s->top2 == MAXSIZE)
{
printf("underflow\n");
return NULL;
}
return s->data[s->top2++];
}
return NULL;
}
三.链栈
struct StackNode//栈的节点
{
int data;
StackNode* next;
};
struct LinkStack//链栈
{
StackNode* top;//栈顶
int count;//计数
};
//初始化栈
void InitStack(LinkStack* S)
{
S->top = NULL;
S->count = 0;
}
//元素进栈
void LinkStackPush(LinkStack* S, int data)
{
StackNode* newNode = (StackNode*)malloc(sizeof(StackNode));
newNode->data = data;
newNode->next = S->top;//新节点指向top
S->top = newNode;//top上移
S->count++;
}
//判空栈
bool IsEmpty(LinkStack* S)
{
if (S->top == NULL)
return true;
return false;
}
//元素出栈
int LinkStackPop(LinkStack* S, int data)
{
if (IsEmpty(S))
{
return NULL;
}
StackNode* p = S->top;//复制top
data = p->data;//保存top数据
S->top = S->top->next;//top下移
free(p);//释放原来的栈顶
S->count--;
return data;
}
//取栈顶元素
int ShowTop(LinkStack* S)
{
if (IsEmpty(S))
{
return NULL;
}
return S->top->data;
}
//建栈
void CreatLinkStack(LinkStack* S)
{
int data=0;
int i=0;
while (data != -1)//-1结束
{
printf("请输入第%d个元素:", ++i);
scanf("%d", &data);
if (data==-1)
break;
LinkStackPush(S, data);//入栈
}
}
例:斐波那契数列(递归)
0 1 2 3 4 5 6 7 8 9
0 1 1 2 3 5 8 13 21 34
fbi(i)=fbi(i-1)+fbi(i-2)
int Fbi(int i)
{
if (i < 2)
return i == 1 ? 1 : 0;
return Fbi(i - 1) + Fbi(i - 2);
}
递归函数定义:
把一个直接调用自己或通过一系列的调用语句间接的调用自己的函数,称为递归函数。
递归和栈的关系:
前面的例子我们可以看到递归是如何执行他的前行和退回阶段的。递归过程退回的顺序是它前行的逆序。在退回过程中,可能要执行某些动作,包括恢复在前行过程中存储起来的数据。
这种存储数据,并在后面又以存储的逆序恢复这些数据,显然很符合栈这样的数据结构。因此,编译器使用栈实现了递归。
栈应用:
四则运算表达式求值:
中缀表达式:标准的四则运算表达式。
后缀表达式(逆波兰表达式):
例如:9+(3-1)*3+10/2(中缀)
9 3 1 - 3 * 10 2 / +(后缀);
后缀表达式计算规则:
从左到右遍历表达式的每个数字和符号,遇到数字就进栈,遇到符号,就将处于栈顶的两个数字出栈,进行运算,运算结果进栈,直到获得最终结果。
中后缀转换规则:
从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或优先级不高于栈顶符号(乘除优先加减)则栈顶元素依次出栈并输出,并将当前符号进栈,直到最终输出后缀表达式为止。