进栈和出栈的示意图
当元素进栈时,栈顶指针指向第一个为空的元素地址。
当元素出栈时,栈顶指针向下移动一个存储单元
//栈的类型定义
typedef int ElemType ;
typedef struct stack
{
ElemType *bottom; //栈底指针
ElemType *top; //栈顶指针
int stacksize ; //当前栈的容量
}Stack ;
/*栈的初始化
假设初始时给栈分配存储空间大小为STACK_SIZE,栈的容量也赋值为STACK_SIZE。
栈初始化算法的主要过程:
1)为栈分配存储空间(也就是给栈底指针分配空间,这是因为栈底是固定不动的);
2)让栈顶指针指向栈底(表示栈是空的)。*/
Status Init_Stack( Stack &S )
{
S.bottom=(ElemType *)malloc(STACK_SIZE *sizeof(ElemType));
if ( !S.bottom ) //判断分配空间是否成功,如果失败则返回错误
return ERROR;
S.top = S.bottom ; //初始化时让栈顶指针指向栈底
S. stacksize = STACK_SIZE; //初始化时
return OK ;
}//Init_Stack
/*进栈(也称压栈、入栈)
元素进栈算法的主要过程:
1)首先判断栈的容量是否已经达到最大,如果达到,则追加存储空间,并重新定位栈顶指针的位置(其实就是指向实际的栈顶),然后更新栈的容量;
2)把入栈元素存入栈顶;
3)栈顶指针指向新栈顶。*/
Status Push(Stack &S , ElemType e)
{
//如果栈满,则使用realloc追加存储空间
if( S.top-S.bottom >= S. stacksize-1 )
{
S.bottom=(ElemType *)realloc( S.bottom, (S.stacksize+STACK_SIZE) * sizeof(ElemType));
if( !S.bottom )//如果重新分配存储空间失败,则返回错误
return ERROR;
S.top = S.bottom + S.stacksize;//重新定位栈顶
S.stacksize += STACKINCREMENT;//更新栈的容量
}
*S.top = e;//把元素存入栈顶
S.top++; //栈顶指针加1(即向上移动一个存储单元),e成为新的栈顶
return OK;
}//Push
/*出栈(也称弹栈)
元素出栈算法的主要过程:
1)判断栈是否是控制,如果是则返回出栈失败;
2)栈顶指针下移一个存储单元;
3)把栈顶指针指向位置的元素存入到目标变量中*/
Status Pop( Stack &S, ElemType &e )
{
if( S.top == S.bottom ) //栈空,返回失败
return ERROR ;
S.top--;//栈顶下移
e = *S.top;//用e缓存栈顶元素
return OK;
}// Pop
/*取栈顶元素
取栈顶元素的主要步骤:
1)判断栈是否为空,为空则返回失败
2)取栈顶元素并存储到相应的变量中,需要注意的是栈顶元素实际存储在栈顶指针的下一个存储空间中。*/
Status GetTopElement( Stack S, ElemType &e )
{
if ( S.top == S.bottom ) //如果栈为空,则返回失败
return ERROR ;
e = *(S. top – 1) ; //用e缓存栈顶元素
return OK ;
}//GetTopElement
/*把栈置为空栈
把栈置为空栈的主要步骤:
1)把栈顶指针指向栈底即可。*/
Status ClearStack( Stack &S )
{
S.top = S.bottom;
return OK ;
}// ClearStack
/*销毁栈
销毁栈的主要步骤:
1)释放栈底指针(因为栈的存储空间是通过给栈底指针分配空间得到的)
2)把栈底指针和栈顶指针均赋值为NULL
3)把栈的容量赋值为0*/
Status DestroyStack( Stack &S )
{
free( S.bottom );
S.top = NULL;
S.bottom = NULL;
S. stacksize = 0;
return OK ;
}// DestroyStack
链栈与链栈的应用
链栈的基本操作
注:
1.链栈不需要设置头结点,因为链栈的插入和删除元素都在只能在栈顶操作。
2.栈分外部结构和内部结构,外部结构是指栈顶指针top;内部结构是指栈的结点环环相扣,链栈的内部结构类似于单链表,但next的指向方向不同,链栈是由栈顶指向栈底,即从后往前指,而单链表是从前往后指,注意区别!
3. 在链栈出栈操作中,需注意要存放出栈元素,须先在主函数定义一个dataType类型的变量elem(注意是“ 变量 ”而不是 “ 指针变量 ”),然后调用出栈函数时将elem的地址传给形参,即pop_out(&s,&elem),通过变量地址传入函数间接访问的方式,才能成功的用elem存取出栈元素的值。
//链栈的实现及功能
#include<stdio.h>
#include<stdlib.h>
typedef int datatype;
//栈的内部结构,即栈的结点
typedef struct node
{
datatype data;
struct node *next;
}stacknode;
//栈的外部结构
typedef struct stack
{
stacknode *top;//栈顶指针
int length;//栈的节点数(元素个数)
}linkstack;
//[1]栈的初始化,初始化外部结构即可
void initalize(linkstack **s)
{
(*s)->top=NULL;//栈顶指针置空,注意*s要加括号
(*s)->length=0;//栈顶元素初始值为0,节点数也为0
}
//[2]入栈,链栈与顺序栈不同,链栈入栈不需要判栈满
void push(linkstack **s,datatype value)
{
//对内部结构的操作(先)
stacknode *p;
p=(stacknode*)malloc(sizeof(stacknode));
p->data=value;
p->next=(*s)->top;//这里注意:链栈中next的指向与链表中next的指向不同,链栈由栈顶指向栈底,即由后往前指。而链表是由前往后指
//对外部结构的操作(后)
(*s)->top=p;
(*s)->length++;
}
//[3]出栈,需判断栈是否为空
void popout(linkstack **s,datatype *value)
{
stacknode *p;
if((*s)->length==0){
printf("此链栈为空");
return;
}
p=(*s)->top;
*value=(*s)->top->data;//此步骤是用于存放出栈的元素,注意事项见注3
(*s)->top=p->next;
(*s)->length--;
printf("pop element is %d\n",*value);
}
//[4]得到栈顶元素
datatype gettop(linkstack *s)
{
if(s->length==0)
printf("linkstack is empty\n");
else
return(s->top->data);
}
//[5]栈的遍历及输出
void traverse(linkstack *s)
{
stacknode *p;
int i;
if(s->length==0){
printf("linkstack is empty\n");
return;
}
p=s->top;
printf("the elements in the stack are:");
for(i=0;i<s->length;i++){
printf("%d->",p->data);//从栈顶指向栈底部
p=p->next;
}
printf("stack base\n");
}
int main()
{
linkstack *s;
datatype value;
initalize(&s);
push(&s,10);
push(&s,10);
push(&s,10);
push(&s,10);
popout(&s,&value);
traverse(s);
return 0;
}
栈的应用
1:进制转换
算法:假如N为输入的数,n为要转换为的进制,若要将十进制231转换为8进制数,过程如下;
N N/n N%n
231 28 7
28 3 4
3 0 3
则输出为347,可以看出,首先得到的应该是7,然后才是4,最后是3,但是要逆序显示,自然就类似压栈出栈的数据结构了(数组也可以实现,但是没有体现其本质).
所以,只需要初始化栈后,将N%n不断的压入栈底,需要注意的是如果要转换为16进制,则需要对大于9的数字作字符处理。
这里提一下realloc函数
1.realloc函数会将原有空间的数据拷贝到新的空间
2.realloc函数会释放原有空间的内存
3.底层使用malloc函数实现malloc函数
申请新空间,并且需要手动移动数据,释放原有空间
//利用栈实现进制转换
#include<stdio.h>
#include<malloc.h>
#include<String.h>
#define STACKSIZE 100
#define STACKINCREMENT 10
typedef int Status;
typedef int Etype;
typedef struct stack
{
Etype *bottom; //栈底指针
Etype *top; //栈顶指针
int stacksize; //当前栈的容量
}Stack;
Status InitStack( Stack &S );//初始化
Status Push( Stack &S , Etype e );//压栈
Status Pop( Stack &S, Etype &e );//弹栈
void BaseConversion( unsigned n, unsigned base );
int main()
{
unsigned n, base;
printf( "请输入一个十进制整数: " );
scanf( "%ud", &n );//无符号类型的整形,打印时用%ud
printf( "你想将他转为几进制: " );
scanf( "%ud", &base );
printf( "结果: " );
BaseConversion( n, base );
}
//栈的初始化
Status InitStack( Stack &S )
{
S.bottom=(Etype *)malloc(STACKSIZE *sizeof(Etype));
if ( !S.bottom ) //判断分配空间是否成功,如果失败则返回错误
{
return 0;
}
S.top = S.bottom ; //初始化时让栈顶指针指向栈底
S.stacksize = STACKSIZE; //初始化时
return 1 ;
}
//压栈(进栈、入栈)
Status Push( Stack &S , Etype e )
{
//如果栈满,则使用realloc追加存储空间
if( S.top - S.bottom >= S.stacksize-1 )
{
S.bottom=(Etype *)realloc( S.bottom, (S.stacksize+STACKSIZE) * sizeof(Etype) );
if( !S.bottom )//如果重新分配存储空间失败,则返回错误
{
return 0;
}
S.top = S.bottom + S.stacksize;//重新定位栈顶
S.stacksize += STACKINCREMENT;//更新栈的容量
}
*S.top = e;//把元素存入栈顶
S.top++; //栈顶指针加1(即向上移动一个存储单元),e成为新的栈顶
return 1;
}//Push
Status Pop( Stack &S, Etype &e )
{
if( S.top == S.bottom ) //栈空,返回失败
{
return 0;
}
S.top--; //栈顶下移
e = *S.top;//用e缓存栈顶元素
return 1;
}// Pop
void BaseConversion( unsigned n, unsigned base )
{
Stack S;
int k, e;
InitStack( S );
while( n > 0 )
{
k = n % base;
Push( S, k );
n = n / base;
}
while( S.top != S.bottom )
{
Pop( S, e );
char format[3]={ '%' };
if( base == 16 )
{
format[1] = 'x';
}
else
{
format[1] = 'u';
}
printf( format, e );
}
free( S.bottom );
}
2:判断括号是否匹配
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<string.h>
#define MaxSize 100
typedef struct {
char data[MaxSize];
int top;
}SqStack;
//初始化栈
void InitStack(SqStack *S) {
S->top = -1;
}
//判栈空
bool IsEmpty(SqStack S) {
if (S.top == -1) {
return true;
}
else {
return false;
}
}
//入栈
void Push(SqStack *S, char x) {
if (S->top == MaxSize-1) {
printf("栈已满");
return;
}
S->top += 1;
S->data[S->top] = x;
}
//栈顶元素出栈,用x返回
void Pop(SqStack *S, char *x) {
if (S->top == -1) {
printf("栈已满");
return;
}
*x = S->data[S->top];
S->top -= 1; //S->top=S->top+1;
}
//匹配算法
bool bracketCheck(char str[], int length) {
SqStack S;
InitStack(&S);//初始化
for (int i = 0; i < length; i++) {
if (str[i] == '(' || str[i] == '[' || str[i] == '{') {
Push(&S, str[i]);//扫描到左括号就进栈
}
else {
if (IsEmpty(S)) { //扫描到右括号,栈空
return false;//匹配失败
}
char topElem;//保存弹出的栈顶元素
Pop(&S, &topElem);//栈顶元素出栈
if (str[i] == ')' && topElem != '(') {
return false;
}
if (str[i] == '}' && topElem != '{') {
return false;
}
if (str[i] == ']' && topElem != '[') {
return false;
}
}
}
return IsEmpty(S);
}
int main() {
char c[MaxSize];
printf("请输入需要判断的括号:");
scanf("%s", c);
int len = strlen(c);
printf("当前输入括号的个数为:%d", len);
printf("现在开始判断...");
if (bracketCheck(c, len)) {
printf("匹配成功!");
}
else {
printf("匹配失败!");
}
return 0;
}
3:栈判断回文字符串