1、栈(Stack),是基本数据结构中比较重要的一种,其遵循的基本原则是:先进后出(First In Last Out,FILO);我们编程时操作系统为函数参数压栈,其系统内核栈的实现就是基于该数据结构。我们以顺序栈来介绍起基本结构。下图为一个基本的栈的结构(这是一个顺序栈):
我们所建的栈以及内核中实现,在结构上其实并非该图所示,因为内存地址从高到低分配,所以栈应该是“口朝下”的,但是为了方便理解(现实中我们认为“底”应该在下面)所以我们用这种图来表示。栈有两个指针来标识其“头”与“尾”,其中,栈的高地址指针叫栈底指针(pBottom),栈的低地址指针叫做栈顶指针(pTop)。数据存入栈中称为数据入栈(或者压栈),数据从栈中取出来称为出栈(或者推栈)。在该图中,入栈的顺序是num1->num2->num3->num4,而出栈顺序是num4->num3->num2->num1;最先入栈的数据最后才能被推栈出来,所以叫先入后出式存储结构。
2、关于栈的口朝下还是口朝上问题,我们用一个小例子来测试一下:
# include
int add(int x,int y)/*函数传参压栈顺序为从右向左*/
{
printf("address_x = %p\naddress_y = %p\n",&x,&y);
return (x+y);
}
int main(void)
{
int sum = add(3,5);
printf("sum = %d\n",sum);
}
我们将x和y的地址打印输出:
由于y先压栈,x后压栈,我们打印输出的结果显示,address_y为高地址,而address_x为低地址,所以由此我们可以得出,操作系统在栈的实现中是高地址为栈底,低地址为栈顶。因此我们还是将栈的结构图再翻过来吧:
3、满“栈”与空“栈”,听到这两个名字,也许你会认为,“满栈”就是栈已经存满了数据,而“空栈”则是栈中没有数据,那你就大错特错了。首先,我们画出两张栈的结构图:
在该图中,我们发现满栈与空栈中都没有存放数据,但是满栈pTop指针并没有指向第一块栈内存,而空栈的pTop指针指向了第一块栈内存。所以,这就是满栈与空栈的区别:
满栈:满栈的栈顶指针指向栈顶数据(没有数据时,和pBottom都指向NULL),对于满栈的操作则是,先移动栈顶指针,然后在栈顶指针所指向的内存中存入数据;
空栈:空栈的栈顶指针指向一块空内存(没有数据时,pBottom指向NULL,pTop不指向NULL),该内存的上一块内存才是最顶层的数据,对于空栈的操作则是,先在栈顶指针所指向的内存中存入数据,然后移动栈顶指针;
今天我们采用满栈来进行一系列操作。
# include
# include
# include
# define MAX_STACK_LEN 5 /*栈最大元素个数*/
/*顺序栈的结构体定义*/
typedef struct stack_arr
{
int data[MAX_STACK_LEN];
int top;//数组下标作为栈顶指针,而栈底指针不需要指定
}STACKARR;
void init_stack(STACKARR *);//初始化栈
int empty_stack(STACKARR * pS);//栈判空
int full_stack(STACKARR * pS);//栈判满
void push_stack(STACKARR *);//压栈
void traverse_stack(STACKARR *);//遍历
void pop_stack(STACKARR *);//出栈
void clear_stack(STACKARR * pS);//栈清空
int main(void)
{
STACKARR Stack;/*创建了一个含有MAX_STACK_LEN个数据的顺序栈*/
init_stack(&Stack);/*对该栈初始化*/
push_stack(&Stack);
printf("压栈完毕,栈中元素依次为(栈顶->栈底):\n");
traverse_stack(&Stack);
pop_stack(&Stack);
printf("出栈完毕,栈中元素依次为(栈顶->栈底):\n");
traverse_stack(&Stack);
clear_stack(&Stack);
printf("栈已清空");
traverse_stack(&Stack);
return 0;
}
void init_stack(STACKARR * pS)
{
memset(pS->data, 0, sizeof(pS->data));
pS->top = -1;/*满栈让栈定指针初始化在第一个地址的前一个地址*/
}
int empty_stack(STACKARR * pS)
{
return ( (pS->top == - 1) ? (-1) : (0) );
}
int full_stack(STACKARR * pS)
{
return ( (pS->top == MAX_STACK_LEN - 1) ? (-1) : (0) );
}
void push_stack(STACKARR * pS)
{
int num = 0;
int choose = 0;
while(1){
if(full_stack(pS) == -1){
printf("栈已满,不能压栈!\n");
exit(EXIT_FAILURE);
}
printf("请输入要压栈的数据:");
scanf("%d",&num);
pS->top++;/*满栈先移动指针,再存储*/
pS->data[pS->top] = num;
printf("入栈成功,继续入栈?(继续输入1,结束输入2):");
scanf("%d",&choose);
if(choose != 1){
break;
}
}
}
void traverse_stack(STACKARR * pS)
{
int i = 0;
if(empty_stack(pS) == -1){
printf("栈为空!\n");
exit(EXIT_FAILURE);
}
/*从栈顶部开始遍历*/
for(i = pS->top; i >= 0; i-- ){
printf("%d\t",pS->data[i]);
}
printf("\n");
}
void pop_stack(STACKARR * pS)
{
int choose = 0;
while(1){
if(empty_stack(pS) == -1){
printf("栈为空!\n");
exit(EXIT_FAILURE);
}
printf("出栈元素为:%d\n",pS->data[pS->top]);/*先出栈栈顶指针再自减1*/
pS->top--;
printf("出栈成功,继续出栈?(继续输入1,结束输入2):");
scanf("%d",&choose);
if(choose != 1){
break;
}
}
}
void clear_stack(STACKARR * pS)
{
memset(pS->data, 0, sizeof(pS->data));
pS->top = -1;
}
我们今天使用双向链表来实现(单向链表更简单,但是双向链表的复杂会使我们对栈这种结构认识更加清晰),基本结构如下:
测试代码:
/**
*2016年12月13日10:52:39
*Create Stack of Link
**/
#include <stdio.h>
#include <stdlib.h>
#ifndef bool
#define bool int
typedef struct stack_node{
int data;
struct stack_node * pDown;
struct stack_node * pUp;
}STACKNODE;/*栈的节点结构体*/
typedef struct stack_link{
STACKNODE * pTop;
STACKNODE * pBottom;
}STACKLINK; /*栈顶指针与栈底指针结构体*/
enum{false,true};
void init_stack(STACKLINK *);/*初始化*/
void push_stack(STACKLINK *);/*压栈*/
void travsal_stack(STACKLINK *);/*遍历*/
void pop_stack(STACKLINK *);/*出栈*/
bool empty_stack(STACKNODE * pS);/*栈判空,链式栈不存在满栈情况,无需判满*/
int main(void)
{
STACKLINK Stack;
init_stack(&Stack);/*初始化栈*/
push_stack(&Stack);/*压栈*/
travsal_stack(&Stack);/*从栈顶开始遍历*/
pop_stack(&Stack);/*出栈*/
travsal_stack(&Stack);
return 0;
}
void init_stack(STACKLINK * pS)
{
pS->pTop = NULL;
pS->pBottom = NULL;/*初始化将栈顶指针与栈底指针均赋空*/
printf("初始化成功!\n");
}
void push_stack(STACKLINK * pS)
{
int num, choose;
STACKNODE * pNew = NULL;
while(1){
printf("请输入压栈数据:");
scanf("%d",&num);
pNew = (STACKNODE *)malloc(sizeof(STACKNODE));
if(NULL == pNew){
printf("内存分配失败!\n");
exit(EXIT_FAILURE);
}
pNew->data = num;
pNew->pUp = NULL;
pNew->pDown = NULL;/*入栈元素初始化*/
if(empty_stack(pS->pTop) == -1){/*栈为空*/
pS->pBottom = pS->pTop = pNew;
}
else{/*栈不为空*/
pS->pTop->pUp = pNew;/*上一个为新元素*/
pNew->pDown = pS->pTop;/*新元素的下一个为原有栈顶元素*/
pS->pTop = pNew;/*栈顶指针向上移到*/
}
printf("继续压栈?(继续请选1,结束请选2):");
scanf("%d",&choose);
if(choose != 1){
break;
}
}
}
void travsal_stack(STACKLINK * pS)
{
STACKNODE * pMove;
pMove = pS->pTop;
while(1){
if(pMove == NULL){
break;
}
printf("%d\t",pMove->data);
pMove = pMove->pDown;
}
printf("\n");
}
void pop_stack(STACKLINK * pS)
{
STACKNODE * pDelete;
int choose = 0;
while(1){
pDelete = pS->pTop;
if(empty_stack(pDelete) == -1){
printf("栈为空,出栈失败!\n");
exit(EXIT_FAILURE);
}
printf("出栈元素为:%d\n",pDelete->data);
pS->pTop = pDelete->pDown;/*栈顶指针向下移*/
if(pS->pTop != NULL)/*注意当pS->pTop为空,即栈为空时,无pS->pTop->pUp,若不判断会出现段错误*/
pS->pTop->pUp = NULL;/*栈顶指针的上一个(前驱指针)为空*/
free(pDelete);
printf("继续出栈?(继续请选1,结束请选2):");
scanf("%d",&choose);
if(choose != 1){
break;
}
}
}
bool empty_stack(STACKNODE * pN)
{
return ((pN == NULL) ? -1 : 0);
}
#endif
/*用数组创建一个栈*/
# include
# include
# include
# define SIZE 32
typedef struct Stack{
int sta_arr[SIZE];
int top;
}STACK;
void init_stack(STACK *);
void init_stack(STACK * pS)
{
memset(pS->sta_arr, 0, sizeof(pS->sta_arr));
pS->top = -1;
}
int is_full(STACK * pS)
{
return ((pS->top == SIZE-1) ? 0 : 1);
}
int is_empty(STACK * pS)
{
return ((pS->top == -1) ? 0 : 1);
}
void input_stack(STACK * pS, int value)
{
if(is_full(pS)){
pS->top++;
pS->sta_arr[pS->top] = value;
}
}
void output_stack(STACK * pS)
{
if(is_empty(pS))
printf("%d",(pS->sta_arr)[pS->top]);
}
void invert(STACK * pS, int value, int type)
{
int tmp,num = value;
init_stack(pS);
while(num)
{
/*进制转化:取余之后压栈,再取整,低位就在栈底,自然最后输出*/
tmp = num % type;
input_stack(pS,tmp);
num = num / type;
}
/*出栈*/
while(pS->top != -1)
{
output_stack(pS);
pS->top--;
}
printf("\n");
}
int main (void)
{
STACK S;
int value, type;
init_stack(&S);
printf("请输入你要转换的十进制数字:\n");
scanf("%d",&value);
printf("请选择你要转换成的进制格式(2~9,如:2代表二进制):\n");
scanf("%d",&type);
if(type > 9 || type < 2){
printf("选择的进制格式暂时无法处理!\n");
exit(EXIT_FAILURE);
}
invert(&S,value,type);
return 0;
}
测试: