数据结构与算法基础(王卓)(11):栈的定义及其基础操作(顺序和链式的初始化、求长度,是否为空,清空和销毁、出栈、压栈),栈与递归

目录

 栈的定义:

顺序栈和链栈的设计参考:

顺序栈:

前置条件:

初始化:

简单操作:(求长度,是否为空,清空和销毁)

清空:

销毁:

压栈:

出栈:

链栈:

前置条件:

简单操作:(初始化、是否为空、取栈顶元素)

入栈:

出栈:

栈与递归

尾递归→循环结构

单向递归→循环结构


 栈的定义:

stack:一堆,一摞;堆;垛;


顺序栈和链栈的设计参考:

数据结构与算法基础(王卓)(7):小结:关于链表和线性表的定义及操作_宇 -Yu的博客-CSDN博客


顺序栈:


前置条件:

(这里写的是线性表的构造形式,也可以写成链表的构造形式)

//基于线性表的定义所做的更改
#include<iostream>
using namespace std;
#include<stdlib.h>//存放exit
#include<math.h>//OVERFLOW,exit

#define TRUE        1
#define FALSE       0
#define OK          1
#define ERROR       0
#define INFEASIBLE  -1
//#define OVERFLOW   -2   

#define MAXlength 100  
//可按需修改,PPT中写的是MAXlength

struct Poly
{
	float p;
	int e;

	bool operator==(Poly t)
	{
		return t.p == p && t.e == e;
	}
	bool operator!=(Poly t)
	{
		return t.p != p || t.e != e;
	}
};

struct Sqlist
{
	Poly* elem;
	int length;
};

typedef int Status; 
typedef Poly Elemtype;
typedef Elemtype SElemType;
//注意:这一段必须写在调用SElemType类型及其指针之前

struct SqStack
{
	SElemType* base; //栈底指针  
	SElemType* top;//栈顶指针
	int stacksize; //栈可用最大容量
};

初始化:

Status InitStack(SqStack& S)//构造一个空栈
{
	S.base = new SElemType[MAXlength];
	//或
	//S.base = (SElemType*)malloc(MAXlength * sizeof(SElemType));
	if (!S.base) exit(OVERFLOW);// 存储分配失败
	S.top = S.base;
	//栈顶指针等于栈底指针
	S.stacksize = MAXlength;
	return true;
}

简单操作:(求长度,是否为空,清空和销毁)

Status StackEmpty(SqStack S)
{
	// 若栈为空,返回TRUE;否则返回FALSE 
	if (S.top == S.base)
		return TRUE;
	else
		return FALSE;
}

int StackLength(SqStack S)
{
	return S.top - S.base;
}

Status ClearStack(SqStack S)//清空顺序栈
{
	if (S.base)
		S.top = S.base;
	return OK;
}


Status DestroyStack(SqStack& S)//销毁
{
	if (S.base)
	{
		delete S.base;
		S.stacksize = 0;
		S.base = S.top = NULL;
	}
	return OK;
}

在这里,我们很容易产生这样的疑问:

关于清空和销毁,我们不是应该把元素一个一个置为NULL吗???

这里销毁和清空改变的只是指向这个位置的指针的值,可是没有把位置的内容置空(null)啊??

实际原因解释如下:

清空:


这里我们其实只是让(将)栈回归到(置于)空栈的状态(两指针指向同一栈点)

至于栈的内容,清不清除其实都无所谓:

因为只要我们后面在写入元素,前面放在栈中没有被消除的元素自然都会被覆盖


销毁:


直接销毁base指针,这里我们或许会觉得:

你只不过是销毁base指针而已,你凭啥就说这样操作我们能实现销毁整个栈的内容和内存?

而事实上他确实办到了,至于原因,我们可以去看看该表的初始化操作:

在给该表初始化时,我们采用的操作是给base指针开辟内存空间:

    S.base = new SElemType[MAXlength];

所以只要我们把base指针销毁就可以实现销毁整个栈的内容和内存的操作:

delete操作销毁了base指针内部的内容同时也销毁了以base指针作为头指针的整个栈的内存空间


压栈:

程序设计流程:


程序:

Status Push(SqStack& S, SElemType e)
{
	if (S.top-S.base==S.stacksize)//不是MAXlength
		return OVERFLOW;
	*S.top = e;
	S.top++;
	//也可以写成:
	//*S.top++ = e;
	return true;
}

ISSUES:


(1):

注意,这里栈满的语句写的是:

    if (S.top-S.base==S.stacksize)

其中的 S.stacksize 不能写 MAXlength


(2):

关于 *S.top++ = e;    的优先级问题:

自增(算术运算符)的优先级大于赋值运算符

所以理论上说,这里应该是先自增,后运算

但是(然而),这里我们写的语句用的是:*S.top++

也就是说:在等到该语句中,所有其他操作都执行完成以后,再执行自增操作

所以才等价于先赋值,再自增


出栈:

程序设计流程:


程序:

Status Pop(SqStack& S, SElemType& e)
//若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;	否则返回ERROR
{
	if (S.top == S.base) // 等价于 if(StackEmpty(S))
		return UNDERFLOW;//ERROR;
	e = *S.top;
	S.top--;
	//e = *--S.top;
	return true;
}

同样的,这里先赋值,后自减;

这里不再赘述


链栈:

前置条件:

//基于链表的定义所做的更改
#include<iostream>
using namespace std;
#include<stdlib.h>//存放exit

#define TRUE        1
#define FALSE       0
#define OK          1
#define ERROR       0
#define INFEASIBLE  -1
//#define OVERFLOW   -2   

#define MAXlength 100  //初始大小为100,可按需修改

struct K//Poly
{
    float a;
    int b;
    string c;
    bool operator==(K& t)
    {
        return t.a == a && t.b == b;
        //&& t.c = c;
    }
    bool operator!=(K& t)
    {
        return t.a != a || t.b != b;
        //|| t.c = c;
    }
};
typedef K Elemtype;         //函数调用状态

struct Lnode
    //node:结; 结点;
{
    Elemtype data;
    Lnode* next;
};
typedef Lnode* LinkList;

typedef int Status; 
typedef K Elemtype;
typedef Elemtype SElemType;
//注意:这一段必须写在调用SElemType类型及其指针之前

struct StackNode
{
    SElemType data;
    StackNode* next;
};
typedef StackNode *LinkStack;
LinkStack S;

其中:(模块构造解析) 

SElemType:Poly(复合)型

top,base:SElemType型指针

 另外,在这里我们需要注意,定义结构体时可以实现嵌套自身(递归),本质上就像:

#include<iostream>
using namespace std;

struct S
{
    int data;
    S* next;
};

int main()
{

}

简单操作:(初始化、是否为空、取栈顶元素)

int InitStack(LinkStack& S) 
{
    //构造一个空栈,栈顶指针置为空
    S = NULL;
    return OK;
}

Status StackEmpty(LinkStack S)
{
    if (S == NULL)
        return TRUE;
    else return FALSE;
}

SElemType GetTop(LinkStack S)
{
    if (S != NULL)
        return S->data;
}


入栈:

Status Push(LinkStack& S, SElemType e)
{
    StackNode* p = new StackNode;
    p->data = e;
    p->next = S;

    S = p;

    return true;
}

出栈:


我写的:

Status Pop(LinkStack& S, SElemType e)
{
    LinkStack p = S;
    e = p->data;
    S = p->next;
    delete p;
    return true;
}

参考PPT,我们可以发现,做出如下改动会更好更严谨:

加上语句:
    if (S == NULL) return ERROR;

 

返回的e为引用类型

最终版:

Status Pop(LinkStack& S, SElemType &e)
{
    if (S == NULL) return ERROR;
    LinkStack p = S;
    e = p->data;
    S = p->next;
    delete p;
    return true;
}

栈与递归


尾递归→循环结构

long Fact(long n)
{
    if (n == 0) return 1;
    else 
        return n * Fact(n - 1);
}

long Fact(long n)
{
    auto t = 1;
    for (auto i = 1; i <= n; i++)
        t = t * i;

        return t;
}


单向递归→循环结构

long Fib(long n) // Fibonacci数列
{
    if (n == 1 || n == 2)
        return 1;
    else return
        Fib(n - 1) + Fib(n - 2);
}



long Fib(long n) 
{
    if (n == 1 || n == 2)
        return 1;
    else 
    {
        int t1 = 1,t2 = 1;
        for(int i = 3; i <= n; i++)
        {
            int t3 = t1 + t2;
            t1 = t2; t2 = t3;
                return t3;
        }

    }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值