【数据结构之旅】顺序栈的定义、初始化、空栈判断、入栈、出栈操作

简介:

说明:

    往前学习数据结构,想运行一个完整的顺序栈的程序都运行不了,因为书上给的都是一部分一部分的算法,并没有提供一个完整可运行的程序,听了实验课,自己折腾了一下,总算可以写一个比较完整的顺序栈操作的小程序,对于栈也慢慢开始有了感觉。下面我会把整个程序拆开来做说明,只要把这些代码放在一个文件中,用编译器就可以直接编译运行了。





一、实现


1.程序功能

  关于栈操作的经典程序,首当要提及进制数转换的问题,利用栈的操作,就可以十分快速地完成数的进制转换。




2.预定义、头文件导入和类型别名

    代码如下:

1
2
3
4
5
6
7
8
9
10
# include <stdio.h>
# include <stdlib.h>
#define OVERFLOW - 1
#define ERROR  0
#define FALSE  0
#define TRUE  1
#define OK  1
 
typedef  int  ElemType;
typedef  int  Status;

    除了两个头文件的导入是必须的之外,下面做两点说明:

(1)其余的常量定义都是可选的,为的就是在下面的代码书写过程中可以尽量使用英文来表达程序的意思,而不是在代码的实现过程中直接使用数字,依个人喜欢,也可以直接使用数字;

(2)使用typedef做类型的别名也仅仅是为了程序中代码的意思更加清晰明了而已,实际也可以不这样使用;




3.顺序栈的定义

    代码如下:

1
2
3
4
5
6
typedef struct{
     ElemType *elem;      //存储空间的基址
     int  top;             //栈顶元素的下一个元素,简称栈顶位标
     int  size;            //当前分配的存储容量,作用看入栈操作就可以知道
     int  increment;       //扩容时,增加的存储容量,作用看入栈操作就可以知道
} SqStack;                   //顺序栈名称




4.栈的初始化

    代码如下:

1
2
3
4
5
6
7
8
Status InitStack_Sq(SqStack &S,  int  size,  int  inc){      //接受3个参数,&S是对结构体的引用
     S.elem = (ElemType*)malloc(size*sizeof(ElemType));   //分配存储空间
     if (S.elem == NULL)  return  OVERFLOW;      //判断上一步分配存储空间是否成功
     S.top =  0 ;             //置S为空栈,S.top为0即表示栈为空栈
     S.size = size;         //栈的空间初始容量值
     S.increment = inc;     //栈的空间初始增量值(如果需要扩容)
     return  OK;        //上面的执行正常,返回OK
}




5.空栈的判断

    代码如下:

1
2
3
4
5
6
7
8
Status StackEmpty_Sq(SqStack S){
     if (S.top ==  0 )
         return  TRUE;
     else
         return  FALSE;
}
//空栈的决断是,如果栈为空就返回1,否则就返回0,当然可以不这样规定;
//至于为什么要做空栈的判断,自然是有原因的,下面再看程序的代码时就可以知道了。




6.入栈

    代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Status Push_Sq(SqStack &S, ElemType e){     //将元素e压入栈,这里e只是一个形参而已
     ElemType *newbase;         //定义中间变量
     if (S.top>= S.size){         //S.top如果指向最后一个不存储元素的地址时,即S.top大于
         newbase = (ElemType*)realloc(S.elem,  //等于S.size时,就表示栈满了
     (S.size + S.increment)*sizeof(ElemType));  //通过realloc动态扩容
     
     if (NULL == newbase)  return  OVERFLOW;  //判断扩容是否成功
     S.elem = newbase;       //扩容成功后才将中间变量的值指向S.elem,防止扩容失败时,
     S.size = S.size + S.increment;      //S.elem指向一个不是原来的位置
     }
     S.elem[S.top] = e;     //将e元素入栈
     S.top++;               //使S.top加1,表示指向的是栈顶位标
     return  OK;             //上面操作正常后返回1
}




7.出栈

    代码如下:

1
2
3
4
5
Status Pop_Sq(SqStack &S, ElemType &e){     //栈顶元素出栈,赋给元素e
     if ( 0  == S.top)  return  ERROR;    
     e = S.elem[--S.top];     //e出栈,并将S.top减1
     return  OK;
}




8.进制转换的函数

    其实上面的步骤操作都是为了创建一个顺序栈和定义顺序栈的操作而已,并对可能出现的各种情况做一些相应的举措,完毕后,下面就要使用上面创建的顺序栈以及栈的操作接口了,即在数制转换函数(这里是十进制转八进制)中使用上面的操作接口,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void  Converstion( int  N){
     SqStack S;
     ElemType e;
     InitStack_Sq(S,  10 5 );     //栈S的初始容量置为10,每次扩容容量为5
     
     while (N !=  0 ){
         Push_Sq(S, N% 8 );    //将N除以8的余数入栈
         N /=  8 ;             //N取值为其除以8的商
     }                           //理论基础为除8取余法
     
     while (StackEmpty_Sq(S) == FALSE){
         Pop_Sq(S, e);     //依次输出栈中的余数,并赋给元素e
         printf( "%d" , e);  //打印元素
     }




9.main函数

    进制转换函数调用栈操作的接口函数,以实现在数制转换过程中栈的操作;main函数调用数制转换函数,以实现数制的转换,代码如下:

1
2
3
4
5
int  main( void ){
     printf( "Enter a number:" );scanf( "%d" ,&num);
     Converstion(num);
     printf( "\n" );
}





二、执行

    有了上面的代码后,就可以在编译器中编译执行了,这里我是用c free 5来进行程序代码的编译:


(1)输入的数为1348时的结果:

wKiom1X-bHCSWO55AADAAgjs3ew463.jpg


(2)输入的数为2526时的结果:

wKioL1X-bsDTXhERAADDp63JNCE633.jpg





三、完整的代码

    下面把代码都放在一起:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# include <stdio.h>
# include <stdlib.h>
#define OVERFLOW - 1
#define ERROR  0
#define FALSE  0
#define TRUE  1
#define OK  1
 
typedef  int  ElemType;
typedef  int  Status;
 
typedef struct{
     ElemType *elem;
     int  top;
     int  size;
     int  increment;
} SqStack;
 
Status InitStack_Sq(SqStack &S,  int  size,  int  inc){
     S.elem = (ElemType*)malloc(size*sizeof(ElemType));
     if (S.elem == NULL)  return  OVERFLOW;
     S.top =  0 ;
     S.size = size;
     S.increment = inc;
     return  OK;
}
 
Status StackEmpty_Sq(SqStack S){
     if (S.top ==  0 )
         return  TRUE;
     else
         return  FALSE;
}
 
Status Push_Sq(SqStack &S, ElemType e){
     ElemType *newbase;
     if (S.top>= S.size){
         newbase = (ElemType*)realloc(S.elem,
     (S.size + S.increment)*sizeof(ElemType));
     
     if (NULL == newbase)  return  OVERFLOW;
     S.elem = newbase;
     S.size = S.size + S.increment;
     }
     S.elem[S.top] = e;
     S.top++;
     return  OK;
}
 
Status Pop_Sq(SqStack &S, ElemType &e){
     if ( 0  == S.top)  return  ERROR;
     e = S.elem[--S.top];
     return  OK;
}
 
void  Converstion( int  N){
     SqStack S;
     ElemType e;
     InitStack_Sq(S,  10 5 );
     
     while (N !=  0 ){
         Push_Sq(S, N% 8 );
         N /=  8 ;
     }
     
     while (StackEmpty_Sq(S) == FALSE){
         Pop_Sq(S, e);
         printf( "%d" , e);
     }
}
 
int  main( void ){
     int  num;
     printf( "Enter a number:" );scanf( "%d" ,&num);
     Converstion(num);
     printf( "\n" );
}





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

热门文章

最新文章