栈的深入讲解与运用

简介: 栈的深入讲解与运用

一,栈的定义与操作基础

       栈是一种尽可以在一端进行插入和删除运算的限定线性表,栈中能被插入的一端为栈顶,最底部的一端为栈底,如图:

        由图可知,栈其实就像一个管道,只能从后往前依次插入或取出,不能从其它位置进行操作,是一种具有限制操作的线性表。

       栈既然属于线性表,那么它也有属于自已的基础操作,而栈的基础操作如下:

    (1)向栈顶插入数据

    (2)从栈顶取出数据

    (3)删除栈顶元素

    (4)判断栈是否为空

       只要是属于栈结构,无论是什么栈类型,基本都需要这几种操作,而复杂的问题基本是以这几种基础算法为操作进行的。下面我会详细的跟大家介绍。

二,栈的顺序栈

       顺序栈跟顺序表的结构差不多,是以数组为形式构成的一种结构,也是栈的基本结构之一,

顺序栈的基本形式如下:

typedef int Datatype;

struct stack

{

   Datatype a[100];

   int top;//top作为栈顶指针,用来指向数据的位置

};

       其中,用结构体中的a数组来存放数据,整个框架就是一个顺序栈,由此课看出,顺序栈与顺序表几乎一样,只是顺序栈被限制了数据元素的位置。

       下面是顺序栈的基本操作算法,在以后的运用栈中可根据以下作为基础。

#include<stdio.h>

#include<stdlib.h>

#define max 100

typedef int type;

typedef struct set//顺序栈的结构式

{

   type a[max];

   int top;//栈顶指针

}stack;

stack* add(stack* s, type n)//顺序栈的入栈算法

{

   if (s->top == max)

   {

       puts("栈已满,不能添加");

       return 0;

   }

   else

       s->a[s->top++] = n;

   return s;

}

stack* spill(stack* s, type m)//顺序栈的溢出算法

{

   if (s->top == 0)

   {

       puts("顺序栈内无任何数据元素");

       return 0;

   }

   else

       s->top--;

   return s;

}

type take(stack* s)//取栈顶元素算法

{

   type n;

   if (s->top == 0)

   {

       puts("顺序栈无任何元素");

       return 0;

   }

   else

       n = s->a[s->top - 1];

   return n;

}

void determine(stack* s)//判断算法

{

   if (s->top == 0)

       puts("顺序栈为空栈");

   else

       puts("顺序栈不为空栈");

}

int main()

{

   stack* s;

   s = (stack*)malloc(sizeof(stack));//建立顺序栈s

   if (!s)

       puts("空间不足,建立失败");

   else

       s->top = 0;

   //入栈操作

   type n;

   scanf("%d", &n);

   s = add(s, n);

   //出栈操作

   type m;

   scanf("%d", &m);

   s = spill(s, m);

   //取栈顶元素操作

   type x;

   x = take(s);

   //判别是否空栈

   determine(s);

   return 0;

}

三,栈的链式栈

       栈的链式栈与链式表的关系跟栈的顺序栈与顺序表的关系相同,除了在元素位置上被限制外没有其它的异处,在这里就不做过多说明,而链式栈的基本结构如下:

typedef int Datatype;

struct stack

{

   Datatype a;

   struct stack* next;

};

       而链式栈的基本操作流程如下:

stack* push(stack* s, type x)//入栈算法

{

   stack* p;

   p = (stack*)malloc(sizeof(stack));

   p->data = x;

   p->next = s;

   s = p;

   return s;

}

stack* deletdata(stack* s)//出栈算法

{

   stack* p;

   p = (stack*)malloc(sizeof(stack));

   p = s;

   s = s->next;

   free(p);

   return s;

}

Datatype Gettopdata(stack* pa)//得到栈顶元素算法

{

   Datatype a;

   a = pa->data;

  //不能pa = pa->next进行查找;因为只是在子程序内部进行,出了子程序就销毁

   return a;

}

       要说明的是,链式栈不必设置头结点,因为链式栈在算法中操作是用地址进行的,在链式栈中很容易造成程序上的链接错误,并且栈顶的操作还比较频繁。

       ·由以上可知,链式栈的操作虽然比顺序栈稍微复杂一点,但链式栈中的元素变化数量较大,并链式栈一般也不会出现栈满情况,因此,在栈的结构中,链式栈是首选的存储方式。

四,栈的综合运用

       讲解完栈的基础之后,接下来我们要考虑的这东西学完之后怎么用,在什么情况下运用。从以上对栈的认识上看,不难发现,凡问题求解具有后进先出的天然特性,其求解过程中必然需要利用栈。下面我来跟大家讲解几道例题,让大家学会如何更好的利用栈。

(1)链式栈的十进制的转换问题

#include<stdio.h>

#include<stdlib.h>

typedef struct set//建立链式栈

{

   int data;

   struct set* next;

}stack;

stack* reverse(stack* head)//逆序算法,将其全部逆序排列

{

   stack* p, * pa, * pp;

   pa = head;

   p = head->next;

   head->next = 0;

   while (p->next)

   {

       pp = p->next;

       p->next = pa;

       pa = p;

       p = pp;

   }

   return pa;

}

stack* change(int n, int m)

{

   int x = 0;

   stack* head = 0, * p = 0, * pa = 0;

   head = p = (stack*)malloc(sizeof(stack));

   do

   {

       x = n % m;

       p->data = x;

       pa = (stack*)malloc(sizeof(stack));

       p->next = pa;

       p = pa;

       n /= m;

   } while (n);

   p->next = 0;

   return reverse(head);

}

int main()

{

   int n, m;

   stack* p;

   scanf("%d%d", &n, &m);//n代表数,m代表转换的进制

   for (p = change(n, m);; p = p->next)

   {

       if (!p->next)

       {

           printf("%d", p->data);

           break;

       }

       else

           printf("%d->", p->data);

   }

   return 0;

}

(2)括号匹配问题

#include<stdio.h>

#include<stdlib.h>

typedef char type;

typedef struct note stack;

struct note

{

   type data;

   struct note* next;

};

stack* push(stack* s, type x)//入栈算法

{

   stack* p;

   p = (stack*)malloc(sizeof(stack));

   p->data = x;

   p->next = s;

   s = p;

   return s;

}

stack* deletdata(stack* s)//出栈算法

{

   stack* p;

   p = (stack*)malloc(sizeof(stack));

   p = s;

   s = s->next;

   free(p);

   return s;

}

int main()

{

   stack* s;

   type a[100];

   int i = 0;

   s = (stack*)malloc(sizeof(stack));//建立链式栈

   s->data = 0;

   fgets(a, 100, stdin);

   while (a[i])//下面程序反复调用算法

   {

       switch (a[i])

       {

       case '(':

       case '[':

       case '{':

           s = push(s, a[i]);

           break;

       case ')':

           if (s->data == '(')

               s = deletdata(s);

           else

           {

               puts("匹配失败");

               return 0;

           }

           break;

       case ']':

           if (s->data == '[')

               s = deletdata(s);

           else

           {

               puts("匹配失败");

               return 0;

           }

           break;

       case '}':

           if (s->data == '{')

               s = deletdata(s);

           else

           {

               puts("匹配失败");

               return 0;

           }

           break;

       }

       i++;

   }

   puts("匹配成功");

   return 0;

}

(3)用顺序栈来实现逆置单向链表

#include<stdio.h>

#include<stdlib.h>

#define max 100

typedef int Datatype;

typedef struct note//定义单向链式表

{

   Datatype a;

   struct note* next;

}link;

typedef struct set//定义顺序栈

{

   Datatype a[max];

   int Topdata;

}stack;

link* creat()//创建单链表

{

   link* p, * head, * pa;

   p = (link*)malloc(sizeof(link));

   head = pa = (link*)malloc(sizeof(link));

   pa->next = p;

   fscanf(stdin, "%d", &p->a);

   while (p->a)

   {

       pa = p;

       p = (link*)malloc(sizeof(link));

       fscanf(stdin, "%d", &p->a);

       pa->next = p;

   }

   pa->next = 0;

   return head;

}

void linkprint(link* head)//输出单向链表

{

   link* p;

   for (p = head->next; p; p = p->next)

   {

       if (!p->next)

           fprintf(stdout, "%d\n", p->a);

       else

           fprintf(stdout, "%d->", p->a);

   }

}

void push(stack* pa, Datatype a)

{

   pa->Topdata++;

   pa->a[pa->Topdata - 1] = a;

}

Datatype Gettopdata(stack* pa)

{

   return pa->a[--pa->Topdata];

}

void stackprint(stack* pa)

{

   int i;

   for (i = pa->Topdata; i; i--)

   {

       if (i == 1)

           fprintf(stdout, "%d\n", pa->a[i - 1]);

       else

           fprintf(stdout, "%d->", pa->a[i - 1]);

   }

}

int main()

{

   link* p, * s;

   s = creat();

   fprintf(stdout, "单向链表的数据域: ");

   linkprint(s);

   stack* pa;

   pa = (stack*)malloc(sizeof(stack));

   pa->Topdata = 0;

   //然后用栈实现逆置,首先将数据放入顺序栈中

   for (p = s->next; p; p = p->next)

       push(pa, p->a);

   fprintf(stdout, "此时栈中的元素: ");

   stackprint(pa);//输出栈中的元素,检查是否将单链表中的数据元素放到了栈中

  //然后出栈从前到后的顺序放入链表中,即实现逆置运算

   for (p = s->next; p; p = p->next)

       p->a = Gettopdata(pa);

   fprintf(stdout, "用栈逆置后的链表: ");

   linkprint(s);

   return 0;

}

(4)用链式栈来实现逆置单向链表

#include<stdio.h>
#include<stdlib.h>
#define max 100
typedef int Datatype;
typedef struct note
//定义单向链式表

{
   Datatype a;
   struct note* next;
}link;
typedef struct set
//定义顺序栈

{
   Datatype data;
   struct set* next;
}stack;
link* creat()
//创建单链表

{
   link* p, * head, * pa;
   p = (link*)malloc(sizeof(link));
   head = pa = (link*)malloc(sizeof(link));
   pa->next = p;
   fscanf(stdin, "%d", &p->a);
   while (p->a)
   {
       pa = p;
       p = (link*)malloc(sizeof(link));
       fscanf(stdin, "%d", &p->a);
       pa->next = p;
   }
   pa->next = 0;
   return head;
}
void linkprint(link* head)
//输出单向链表

{
   link* p;
   for (p = head->next; p; p = p->next)
   {
       if (!p->next)
           fprintf(stdout, "%d\n", p->a);
       else
           fprintf(stdout, "%d->", p->a);
   }
}
stack* push(stack* pa, Datatype a)
{
   stack* p;
   p = (stack*)malloc(sizeof(stack));
   p->data = a;
   p->next = pa;
   return p;
}
void stackprint(stack* pa)
{
   stack* p;
   for (p = pa; p->next; p = p->next)
   {
       if (!p->next->next)
           printf("%d\n", p->data);
       else
           printf("%d->", p->data);
   }
}
Datatype Gettopdata(stack* pa)
{
 
 //不能pa = pa->next进行查找;因为只是在子程序内部进行,出了子程序就销毁

   return pa->data;
}
int main()
{
   link* p, * s;
   s = creat();
   fprintf(stdout, "单向链表的数据域: ");
   linkprint(s);
   stack* pa;
   pa = (stack*)malloc(sizeof(stack));
//建立链式栈

   pa->next = 0;
   for (p = s->next; p; p = p->next)
//将数据放入链式栈中

       pa = push(pa, p->a);
   fprintf(stdout, "此时栈中的元素: ");
   stackprint(pa);

   for (p = s->next; p; p = p->next)
   {
       p->a = Gettopdata(pa);
       pa = pa->next;
   }
   fprintf(stdout, "用栈逆置后的链表: ");
   linkprint(s);
   return 0;
}

       由以上栈的综合运用我们不难发现,无论是多么复杂的栈问题,基本都需要反复运用栈的基本算法,因此,无论是顺序栈还是链式栈,这些基本的运算都必须完全掌握,笔者建议学者们在下多次练习这些基础操作。

相关文章
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
1486 10
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
558 59
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
1399 77
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
385 0
栈区的非法访问导致的死循环(x64)
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
693 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
算法 调度 C++
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
463 11
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
493 9
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
725 7

热门文章

最新文章