数据结构与算法(二)栈

1、栈

1.1、什么是栈

栈的结构类似于枪的弹夹,第一颗放入弹夹的子弹却是最后一颗被打出来,最后一颗放入弹夹的子弹却是第一颗被打出来,称为先进后出的数据结构。

现实中,栈的应用非常广泛和普遍,例如,使用浏览器上网时,不管什么浏览器都会有 个"后退"键,点击后可以按访问顺序的逆序,加载浏览过的网页;如office、photoshop等文档或图像的编辑软件中,都有撤销(undo)的操作,也是使用栈来实现的。

栈的定义:栈(stack)是限定仅能在表尾进行插入和删除的线性结构。

我们通常把可以进行插入和删除的一端称为栈顶,就像弹夹可以放入子弹的一端是位于弹夹顶部的,另一端则称为栈底,不含任何数据元素的栈称为空栈。

首先它是一个线性表 ,也就是说,栈元素具有线性关系,即前驱后继关系。只不过栈是一种特殊的线性表而已。栈的特殊之处就在于限制了这个线性表的插入和删除位置,它始终只在栈顶进行。这也就使得:栈底是固定的,最先进栈的只能在栈底。

栈的插入操作,叫作进栈,也叫压栈、入栈。
栈的删除操作,叫作出栈,也叫弹栈。

这个最先进栈的元素,是不是一定就只能最后出栈呢? 

答案是不一定,栈只是对线性表的插入和删除的位置进行了限制 ,并没有对元素进出的时间进行限制,也就是说,在所有元素没有完全进栈时,先进栈的元素也可以先出栈,只要保证是栈顶元素出栈即可。就像我们有五颗子弹来打枪,先放入三颗子弹,打了两枪,再放入一颗子弹,又打了两枪,再放入一颗子弹,最后把这课子弹打出去,此时最后入栈的子弹最后打出去。

1.2、栈的基本功能

InitStack:初始化操作,建立一个空栈。
DestroyStack:若栈存在,则销毁栈。
ClearStack:清空栈元素。
StackEmpty:判断栈是否为空栈。
GetTop:若栈存在且非空,返回栈顶元素。
Push:入栈,将新元素放置栈顶。
Pop :弹栈,返回栈顶元素并删除该栈顶元素。
StackLength:返回栈的元素个数。

2、栈的数组结构以及实现

栈的一切操作取决于栈顶指针,实现栈的功能更多的是对栈顶指针的操作。

2.1、栈的数组结构

const int MAXSIZE=100;

typedef int DataType;        //栈的数据元素类型 
typedef struct ArrayStack {
    DataType data[MAXSIZE]; //栈的数组 
    int top;                //栈顶指针 
}*Stack;                    //不需要加指针*的别名 

2.2、入栈操作

int Push(Stack s, DataType element)
{
    if (s->top >= MAXSIZE-1) return 0; //如果栈满则返回错误 
    s->top++;                          //栈顶指针向上移到空位 
    s->data[s->top] = element;         //将新元素赋给栈顶
    return 1;
}

2.3、出栈操作

DataType Pop(Stack s)
{
    if (s->top <= -1) return 0;  //如果栈空则返回错误      
    DataType element = 0;        //初始化一个返回值 
    element = s->data[s->top];   //将栈顶元素赋给返回值 
    s->top--;                    //栈顶指针向下移 
    return element;
}

两个最重要功能都没有涉及任何循环, 因此时间复杂度都为O(1)。

实现栈的数组结构还是非常简单的,因为它只准栈顶进出元素,所以不存在线性表插入和删除时需要移动大量元素的问题。不过栈的数组结构还是有个很大的缺陷,就是必须事先确定数组存储空间大小,万一不够用了,就需要编程手段来扩展数组的容量,非常麻烦。

3、栈的链表结构以及实现

栈的链表结构简称链栈,栈只是用栈顶来进行操作,单链表有头指针而栈顶指针也是必须,所以可以让他们合二为一:头结点的头指针为栈顶指针,头结点的数据用来记录栈元素个数。

对于链栈来说,基本不存在满栈的情况,所以无需考虑。

3.1、栈的链表结构

typedef int DataType;  //栈的数据元素类型
typedef struct StackNode {//链栈节点结构
    DataType data;        //栈的数据
    StackNode *next;      //后继指针 
}*LinkStackNode;//不需要加指针*的别名 

typedef struct StackTop {//链栈头节点结构
    int length;          //栈元素个数 
    LinkStackNode top;   //栈顶指针 
}*LinkStackTop;//不需要加指针*的别名 

3.2、入栈操作

int Push(LinkStackTop s, DataType element)
{
    LinkStackNode node = new StackNode;
    node->data = element;   //新元素赋值给新节点
    node->next = s->top;    //新节点的后继节点为当前栈顶节点
    s->top = node;          //新节点成为栈顶节点
    s->length++;            //栈元素个数加一 
    return 1;
} 

3.3、出栈操作

DataType Pop(LinkStackTop s)
{
    if (StackEmpty(s) == 0) return 0;//如果栈空则返回错误      
    DataType element = 0;            //初始化一个返回值 
    LinkStackNode node = s->top;    //得到栈顶节点 
    element = node->data;            //将栈顶元素赋给返回值 
    s->top = node->next;             //删除栈顶节点 
    s->length--;                     //栈元素个数减一 
    free(node);
    return element;
}

两个最重要功能在链表结构中的时间复杂度也都为O(1),只是清空栈操作稍微麻烦一下。 

4、对比两种结构的栈

对比数组栈和链栈,它们实现的出栈、入栈功能的时间复杂度是一样的,均为O(1)。时间性能一样,那么来比较空间性能,数组栈需要事先确定一个固定的长度,可能会存在内存空间浪费或不足的问题,优势是存取时定位方便,实现简单;而链栈因为每个元素都有指针域,增加了一定的空间开销,优势是栈的长度无限制,使用灵活。如果在使用栈的过程中,栈的元素个数变化在一定范围,或者一定要固定栈的长度,那么推荐使用数组栈,否则最好是链栈。

附录一: 栈的数组结构实现全部基本功能

const int MAXSIZE=100;

typedef int DataType;        //栈的数据元素类型 
typedef struct ArrayStack {
    DataType data[MAXSIZE]; //栈的数组 
    int top;                //栈顶指针 
}*Stack;                    //不需要加指针*的别名 

Stack InitStack()
{
    Stack s = new ArrayStack;
    s->top = -1;
    return s;
}

int StackEmpty(Stack s) 
{
    if (s->top <= -1) return 0;
    else return 1;
}

int Push(Stack s, DataType element)
{
    if (s->top >= MAXSIZE-1) return 0; //如果栈满则返回错误 
    s->top++;                          //栈顶指针向上移到空位 
    s->data[s->top] = element;         //将新元素赋给栈顶
    return 1;
}

DataType Pop(Stack s)
{
    if (StackEmpty(s) == 0) return 0;//如果栈空则返回错误      
    DataType element = 0;            //初始化一个返回值 
    element = s->data[s->top];       //将栈顶元素赋给返回值 
    s->top--;                        //栈顶指针向下移 
    return element;
}

DataType GetTop(Stack s) 
{
    if (StackEmpty(s) == 0) return 0;//如果栈空则返回错误      
    DataType element = 0;            //初始化一个返回值 
    element = s->data[s->top];       //将栈顶元素赋给返回值
                                     //不需要删除栈顶元素,栈顶指针不用下移 
    return element;
}

void ClearStack(Stack s) 
{
    s->top=-1;
}

int StackLength(Stack s) 
{
    return s->top+1;
}

void DestroyStack(Stack s) 
{
    free(s);
}

附录二: 栈的链表结构实现全部基本功能

typedef int DataType;  //栈的数据元素类型
typedef struct StackNode {
    DataType data;     //栈的数据
    StackNode *next;   //后继指针 
}*LinkStackNode;//不需要加指针*的别名 

typedef struct StackTop {
    int length;        //栈元素个数 
    LinkStackNode top; //栈顶指针 
}*LinkStackTop;//不需要加指针*的别名 

LinkStackTop InitStack()
{
    LinkStackTop s = new StackTop;
    s->length = 0;
    s->top = NULL;
    return s;
}

int StackEmpty(LinkStackTop s) 
{
    if (s->top == NULL) return 0;
    else return 1;
}

int Push(LinkStackTop s, DataType element)
{
    LinkStackNode node = new StackNode;
    node->data = element;   //新元素赋值给新节点
    node->next = s->top;    //新节点的后继节点为当前栈顶节点
    s->top = node;          //新节点成为栈顶节点
    s->length++;            //栈元素个数加一 
    return 1;
} 

DataType Pop(LinkStackTop s)
{
    if (StackEmpty(s) == 0) return 0;//如果栈空则返回错误      
    DataType element = 0;            //初始化一个返回值 
    LinkStackNode node = s->top;    //得到栈顶节点 
    element = node->data;            //将栈顶元素赋给返回值 
    s->top = node->next;             //删除栈顶节点 
    s->length--;                     //栈元素个数减一 
    free(node);
    return element;
}

DataType GetTop(LinkStackTop s) 
{
    if (StackEmpty(s) == 0) return 0;//如果栈空则返回错误      
    DataType element = 0;            //初始化一个返回值 
    LinkStackNode node = s->top;    //得到栈顶节点 
    element = node->data;            //将栈顶元素赋给返回值 
    return element;
}

void ClearStack(LinkStackTop s) 
{
    while (s->top != NULL) 
    {
        LinkStackNode node = new StackNode;
        node = s->top;
        s->top=node->next;
        free(node);
    }
    s->top = NULL;
    s->length = 0;
}

int StackLength(LinkStackTop s) 
{
    return s->length;
}

void DestroyStack(LinkStackTop s) 
{
    if (s->top != NULL) ClearStack(s);
    free(s);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值