链栈的基本操作及应用

第1关:链栈的基本操作

任务描述

本关任务是实现链栈的基本操作函数,以实现判断栈是否为空、求栈的长度、进栈、出栈以及获取栈顶元素等功能。

相关知识

链式存储的栈

栈的链式存储结构是采用某种链表结构,栈的链式存储结构简称为链栈。 这里采用单链表作为链栈,该单链表是不带头结点的。

可以用不带头结点的单链表存储链栈,设计初始化栈、判断栈是否为空、进栈和出栈等相应算法。

不带头结点的单链表,在进行插入、删除、查值等操作运算时需要对表首进行特殊处理。

不带头结点的单链表基本操作

链栈就是不带头结点的单链表,不带头结点的单链表的基本操作如下:

 
  1. struct LNode
  2. {
  3. ElemType data;
  4. LNode *next;
  5. };
  6. typedef LNode * LinkList; // 另一种定义LinkList的方法
  7. // 不带头结点的单链表的部分基本操作(9个)
  8. void InitList(LinkList &L)
  9. { // 操作结果:构造一个空的线性表L
  10. L=NULL; // 指针为空
  11. }
  12. #define DestroyList ClearList // DestroyList()和ClearList()的操作是一样的
  13. void ClearList(LinkList &L)
  14. { // 初始条件:线性表L已存在。操作结果:将L重置为空表
  15. LinkList p;
  16. while(L) // L不空
  17. {
  18. p=L; // p指向首元结点
  19. L=L->next; // L指向第2个结点(新首元结点)
  20. free(p); // 释放首元结点
  21. }
  22. }
  23. int ListEmpty(LinkList L)
  24. { // 初始条件:单链表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE
  25. if(L)
  26. return FALSE;
  27. else
  28. return TRUE;
  29. }
  30. int ListLength(LinkList L)
  31. { // 初始条件:线性表L已存在。操作结果:返回L中数据元素个数
  32. int i=0;
  33. LinkList p=L;
  34. while(p) // p指向结点(没到表尾)
  35. {
  36. p=p->next; // p指向下一个结点
  37. i++;
  38. }
  39. return i;
  40. }
  41. int GetElem(LinkList L,int i,ElemType &e)
  42. { // L为不带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
  43. int j=1;
  44. LinkList p=L;
  45. if(i<1) // i值不合法
  46. return ERROR;
  47. while(j<i&&p) // 没到第i个元素,也没到表尾
  48. {
  49. j++;
  50. p=p->next;
  51. }
  52. if(j==i) // 存在第i个元素
  53. {
  54. e=p->data;
  55. return OK;
  56. }
  57. else
  58. return ERROR;
  59. }
  60. int LocateElem(LinkList L,ElemType e,int(*compare)(ElemType,ElemType))
  61. { // 初始条件:线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0)
  62. // 操作结果:返回L中第1个与e满足关系compare()的数据元素的位序。
  63. // 若这样的数据元素不存在,则返回值为0
  64. int i=0;
  65. LinkList p=L;
  66. while(p)
  67. {
  68. i++;
  69. if(compare(p->data,e)) // 找到这样的数据元素
  70. return i;
  71. p=p->next;
  72. }
  73. return 0;
  74. }
  75. int ListInsert(LinkList &L,int i,ElemType e)
  76. { // 在不带头结点的单链线性表L中第i个位置之前插入元素e
  77. int j=1;
  78. LinkList p=L,s;
  79. if(i<1) // i值不合法
  80. return ERROR;
  81. s=(LinkList)malloc(sizeof(LNode)); // 生成新结点
  82. s->data=e; // 给s的data域赋值
  83. if(i==1) // 插在表头
  84. {
  85. s->next=L;
  86. L=s; // 改变L
  87. }
  88. else
  89. { // 插在表的其余处
  90. while(p&&j<i-1) // 寻找第i-1个结点
  91. {
  92. p=p->next;
  93. j++;
  94. }
  95. if(!p) // i大于表长+1
  96. return ERROR;
  97. s->next=p->next;
  98. p->next=s;
  99. }
  100. return OK;
  101. }
  102. int ListDelete(LinkList &L,int i,ElemType &e)
  103. { // 在不带头结点的单链线性表L中,删除第i个元素,并由e返回其值
  104. int j=1;
  105. LinkList p=L,q;
  106. if(i==1) // 删除第1个结点
  107. {
  108. L=p->next; // L由第2个结点开始
  109. e=p->data;
  110. free(p); // 删除并释放第1个结点
  111. }
  112. else
  113. {
  114. while(p->next&&j<i-1) // 寻找第i个结点,并令p指向其前趋
  115. {
  116. p=p->next;
  117. j++;
  118. }
  119. if(!p->next||j>i-1) // 删除位置不合理
  120. return ERROR;
  121. q=p->next; // 删除并释放结点
  122. p->next=q->next;
  123. e=q->data;
  124. free(q);
  125. }
  126. return OK;
  127. }
  128. void ListTraverse(LinkList L,void(*vi)(ElemType))
  129. { // 初始条件:线性表L已存在。操作结果:依次对L的每个数据元素调用函数vi()
  130. LinkList p=L;
  131. while(p)
  132. {
  133. vi(p->data);
  134. p=p->next;
  135. }
  136. printf("\n");
  137. }

链栈的基本操作

针对链栈我们定义如下操作:

 
  1. typedef SElemType ElemType; // 栈结点类型和链表结点类型一致
  2. typedef LinkList LinkStack; // LinkStack是指向栈结点的指针类型
  3. #define InitStack InitList // InitStack()与InitList()作用相同,下同
  4. #define DestroyStack DestroyList
  5. #define ClearStack ClearList
  6. #define StackEmpty ListEmpty
  7. #define StackLength ListLength

编程要求

根据提示,在右侧编辑器 Begin-End 区间补充代码,实现4个链栈的基本操作函数,具体要求如下:

  • int GetTop(LinkStack S,SElemType &e); // 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR

  • int Push(LinkStack &S,SElemType e); // 插入元素e为新的栈顶元素

  • int Pop(LinkStack &S,SElemType &e)// 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR

  • void StackTraverse(LinkStack S,void(*visit)(SElemType)); // 从栈底到栈顶依次对栈中每个元素调用函数visit()

测试说明

平台会对你编写的代码进行测试:

测试输入: 12 47 5 8 6 92 45 63 75 38 4 29

预期输出: 栈中元素依次为:12 47 5 8 6 92 45 63 75 38 4 29 弹出的栈顶元素 e=29 栈空否:0(1:空 0:否) 栈顶元素 e=4 栈的长度为11 清空栈后,栈空否:1(1:空 0:否)

输入说明 第一行输入顺序栈的数据元素的个数n; 第二行输入顺序栈的n个整数。

输出说明 第一行输出顺序栈的所有数据元素; 第二行输出出栈的数据元素; 第三行判断栈是否为空; 第四行输出当前的栈顶元素及栈的长度; 第五行销毁栈后,判断栈是否为空;

 

#include <stdio.h>
#include<stdlib.h>
#include <iostream>
using namespace std;

 // 函数结果状态代码
 #define TRUE 1
 #define FALSE 0
 #define OK 1
 #define ERROR 0
typedef  int  SElemType;
void input(SElemType &s);
void output(SElemType s);

typedef  SElemType  ElemType;   // 栈结点类型和链表结点类型一致

struct  LNode
 {
   ElemType data;
   LNode *next;
 };
 typedef  LNode * LinkList; // 另一种定义LinkList的方法

// 不带头结点的单链表的部分基本操作(9个)
 #define DestroyList ClearList // DestroyList()和ClearList()的操作是一样的
 void InitList(LinkList &L); 
 void ClearList(LinkList &L);
 int ListEmpty(LinkList L);
 int ListLength(LinkList L);
 int GetElem(LinkList L,int i,ElemType &e);
 int LocateElem(LinkList L,ElemType e,int(*compare)(ElemType,ElemType));
 int ListInsert(LinkList &L,int i,ElemType e);
 int ListDelete(LinkList &L,int i,ElemType &e);
 void ListTraverse(LinkList L,void(*vi)(ElemType));

 /*****链栈的基本操作*****/
typedef  LinkList LinkStack;     // LinkStack是指向栈结点的指针类型
#define  InitStack  InitList     // InitStack()与InitList()作用相同,下同
#define DestroyStack  DestroyList
#define ClearStack  ClearList
#define StackEmpty  ListEmpty
#define StackLength ListLength

int GetTop(LinkStack S,SElemType &e); // 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
int Push(LinkStack &S,SElemType e);  // 插入元素e为新的栈顶元素
int Pop(LinkStack &S,SElemType &e); // 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
void StackTraverse(LinkStack S,void(*visit)(SElemType)); // 从栈底到栈顶依次对栈中每个元素调用函数visit()


int main()
 {
   int j,n;
   LinkStack s;
   SElemType e;
   InitStack(s);
   cin>>n;
   for(j=1;j<=n;j++)
   {
	   input(e);
	   Push(s,e);
   }
   printf("栈中元素依次为:");
   StackTraverse(s,output);
   Pop(s,e);
   printf("\n弹出的栈顶元素 e=%d\n",e);
   printf("栈空否:%d(1:空 0:否)\n",StackEmpty(s));
   GetTop(s,e);
   printf("栈顶元素 e=%d 栈的长度为%d\n",e,StackLength(s));
   ClearStack(s);
   printf("清空栈后,栈空否:%d(1:空 0:否)\n",StackEmpty(s));
   DestroyStack(s);  
 }
 
/*****SElemType类型元素的基本操作*****/
void input(SElemType &s)
{
	cin>>s;
}
void output(SElemType s)
{
	cout<<s<<" ";
}

// 不带头结点的单链表的部分基本操作(9个)
#define DestroyList ClearList    // DestroyList()和ClearList()的操作是一样的
void InitList(LinkList &L)
{ // 操作结果:构造一个空的线性表L
  //  L=NULL; // 指针为空
  L = (LinkStack)malloc(sizeof(LinkStack));
  L->next = NULL;
}

void ClearList(LinkList &L)
{ // 初始条件:线性表L已存在。操作结果:将L重置为空表
   LinkList p;
   while(L) // L不空
   {
     p=L; // p指向首元结点
     L=L->next; // L指向第2个结点(新首元结点)
     free(p); // 释放首元结点
   }
}

int ListEmpty(LinkList L)
{ // 初始条件:线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE
   if(L)
     return FALSE;
   else
     return TRUE;
}

int ListLength(LinkList L)
{ // 初始条件:线性表L已存在。操作结果:返回L中数据元素个数
   int i=0;
   LinkList p=L->next;
   while(p) // p指向结点(没到表尾)
   {
     p=p->next; // p指向下一个结点
     i++;
   }
   return i;
}

int GetElem(LinkList L,int i,ElemType &e)
{ // L为不带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
   int j=1;
   LinkList p=L;
   if(i<1) // i值不合法
     return ERROR;
   while(j<i&&p) // 没到第i个元素,也没到表尾
   {
     j++;
     p=p->next;
   }
   if(j==i) // 存在第i个元素
   {
     e=p->data;
     return OK;
   }
   else
     return ERROR;
}

int LocateElem(LinkList L,ElemType e,int(*compare)(ElemType,ElemType))
{ // 初始条件:线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0)
   // 操作结果:返回L中第1个与e满足关系compare()的数据元素的位序。
   //           若这样的数据元素不存在,则返回值为0
   int i=0;
   LinkList p=L;
   while(p)
   {
     i++;
     if(compare(p->data,e)) // 找到这样的数据元素
       return i;
     p=p->next;
   }
   return 0;
}

int ListInsert(LinkList &L,int i,ElemType e)
{ // 在不带头结点的单链线性表L中第i个位置之前插入元素e
   int j=1;
   LinkList p=L,s;
   if(i<1) // i值不合法
     return ERROR;
   s=(LinkList)malloc(sizeof(LNode)); // 生成新结点
   s->data=e; // 给s的data域赋值
   if(i==1) // 插在表头
   {
     s->next=L;
     L=s; // 改变L
   }
   else
   { // 插在表的其余处
     while(p&&j<i-1) // 寻找第i-1个结点
     {
       p=p->next;
       j++;
     }
     if(!p) // i大于表长+1
       return ERROR;
     s->next=p->next;
     p->next=s;
   }
   return OK;
}

int ListDelete(LinkList &L,int i,ElemType &e)
{ // 在不带头结点的单链线性表L中,删除第i个元素,并由e返回其值
   int j=1;
   LinkList p=L,q;
   if(i==1) // 删除第1个结点
   {
     L=p->next; // L由第2个结点开始
     e=p->data;
     free(p); // 删除并释放第1个结点
   }
   else
   {
     while(p->next&&j<i-1) // 寻找第i个结点,并令p指向其前趋
     {
       p=p->next;
       j++;
     }
     if(!p->next||j>i-1) // 删除位置不合理
       return ERROR;
     q=p->next; // 删除并释放结点
     p->next=q->next;
     e=q->data;
     free(q);
   }
   return OK;
}
 
void ListTraverse(LinkList L,void(*vi)(ElemType))
{ // 初始条件:线性表L已存在。操作结果:依次对L的每个数据元素调用函数vi()
   LinkList p=L;
   while(p)
   {
     vi(p->data);
     p=p->next;
   }
   printf("\n");
}


int GetTop(LinkStack S,SElemType &e)
 { // 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
    /********** Begin **********/ 
    while(S->next)
    {
      S=S->next;
    }
    e=S->data;
    return e;
    
    /********** End **********/	
}

int Push(LinkStack &S,SElemType e)
{ // 插入元素e为新的栈顶元素
    /********** Begin **********/ 
    LinkStack p=S;
    LinkStack q=(LinkStack)malloc(sizeof(LinkStack));
    while(p->next)
    {
      p=p->next;
    }
    q->data=e;
    q->next=p->next;
    p->next=q;
    return e;
    
    /********** End **********/	
 }

int Pop(LinkStack &S,SElemType &e)
{ // 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
    /********** Begin **********/ 
    LinkStack p=S,q;
    while(p->next->next)
    {
      p=p->next;
    }
    e=p->next->data;
    q=p->next;
    p->next=q->next;
    // free(q);
    return e;
    
    /********** End **********/	
 }

void StackTraverse(LinkStack S,void(*visit)(SElemType))
{ // 从栈底到栈顶依次对栈中每个元素调用函数visit()
    /********** Begin **********/ 
   LinkStack p=S->next;
   while(p)
   {
    visit(p->data);
    p=p->next;
   }
   
	/********** End **********/	
}

第2关:链栈的应用——括号匹配 

任务描述

本关任务:设计一个算法,判断一个可能含有花括号、中括号、和圆括号的表达式中各类括号是否匹配,若匹配,则返回1;否则返回0。其中表达式只包含三种括号,花括号{}、中括号[]、圆括号(),即它仅由 ()[]{}六个字符组成。

相关知识

从左至右扫描一个表达式(或字符串),则每个右括号将与最近遇到的那个左括号相匹配。

在从左至右扫描表达式过程中把所遇到的左括号存放到栈中。

每当遇到一个右括号时,就将它与栈顶的左括号(如果存在)相匹配,同时从栈顶删除该左括号。

算法思想:

设置一个栈,当读到左括号时,左括号进栈。当读到右括号时,则从栈中弹出一个元素,与读到的左括号进行匹配,若匹配成功,继续读入;否则匹配失败。另外,在算法的开始和结束时,栈都应该是空的,所以匹配到最后还要判断栈是否为空,若空,则说明匹配成功,返回1。若非空,则说明匹配失败,返回0。

步骤如下:

  • 设置一个链栈st,定义一个整型flag变量(初始为1)。

  • 用i扫描表达式exp,当i<n并且flag=1时循环:

  • 当遇到左括号“(”、“[”、“{”时,将其进栈;

  • 遇到“}”、“]”、“)”时,出栈字符ch,若出栈失败(下溢出)或者ch不匹配,则置flag=0退出循环;

  • 否则直到exp扫描完毕为止。

  • 若栈空并且flag为1则返回1,否则返回0。

编程要求

根据提示,在右侧编辑器 Begin - End 之间补充代码,判断表达式(或字符串)中括号是否成对出现。

测试说明

平台会对你编写的代码进行测试,只有所有数据全部计算正确才能通过测试:

测试输入:{ [ ] ( ) } 预期输出:{ [ ] ( ) }是匹配的表达式

测试输入:[ ( { } ] [ ) ] 预期输出:[ ( { } ] [ ) ]不是匹配的表达式

 
#include<stdio.h>
#include<string.h>
#include "slink.h"	//包含前面的链栈基本运算函数


int Match(char exp[],int n)	//exp存放表达式,n是表达式长度
{ 
	LinkStack st;			//定义一个链栈st
   InitStack(st);		//栈初始化
   int flag=1,i=0;
   char ch;
   /********** Begin **********/ 
   int m=0,p=0;
   for(i=0;i<n;i++)
   {
       if(exp[i]=='('||exp[i]=='['||exp[i]=='{')
       {
           Push(st,exp[i]);
           m++;
       }
       else
       {
           p++;
           GetTop(st,ch);
           if((ch=='('&&exp[i]==')')||(ch=='['&&exp[i]==']')||(ch=='{'&&exp[i]=='}'))
           {
               Pop(st,ch);
           }
       }
   }
       if(ListEmpty(st)&&(m==p))
       {
           return 1;
       }
       else
       {
            return 0;
       }
      

 
   /********** End **********/
}

void display(char exp[])
{
	int n=strlen(exp);
	if (Match(exp,n))
		printf("%s是匹配的表达式\n",exp);
	else
		printf("%s不是匹配的表达式\n",exp);
}

int main()
{
	char str[80];
	scanf("%s",str);
	display(str);
	return 0;
}

  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
链栈是一种使用链表实现的结构。它具有链表的灵活性和操作的特性。链栈基本操作包括:初始化链栈、入、出、获取顶元素和判断链栈是否为空。 要实现链栈,首先需要定义链表的结构,每个节点包含一个数据元素和指向下一个节点的指针。然后,初始化链栈时,创建一个空链表,并将顶指针指向链表头节点。此时,链栈为空。 入操作将一个新的元素插入到链栈顶位置。具体步骤如下:创建一个新的节点,将新节点的数据赋值为要入的元素值,将新节点的指针指向原来的顶节点,更新顶指针,使其指向新的节点。入操作完成后,链栈的长度加一。 出操作将链栈顶元素弹出。具体步骤如下:将顶指针指向的节点从链表中删除,释放其内存空间。然后,更新顶指针,使其指向新的顶节点。出操作完成后,链栈的长度减一。 获取顶元素操作返回链栈顶元素值,但不改变的结构。只需要将顶指针指向的节点的数据返回即可。 判断链栈是否为空操作通过判断顶指针是否为空来实现。如果顶指针为空,表示链栈为空,返回真;否则返回假。 综上所述,链栈基本操作包括初始化链栈、入、出、获取顶元素和判断链栈是否为空。通过这些操作,我们可以对链栈进行操作,实现对元素的添加、删除和查询。在实际应用中,链栈常用于需要动态改变大小的场景,因为其灵活的链表结构可以方便地插入和删除元素。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ଲଇଉକ ଲ ̊ଳ

多谢大哥赏赐

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值