0
点赞
收藏
分享

微信扫一扫

画解数据结构1-4栈

楚木巽 2022-04-27 阅读 80
c语言

一.概念

1、栈的定义

   是仅限在 表尾 进行 插入删除线性表

2、栈顶

   是一个线性表,我们把允许 插入删除 的一端称为 栈顶

二。栈的顺序表实现

1、数据结构定义

  • 我们可以定义一个  的 结构体,C语言实现如下所示:
#define DataType int        // (1)
#define maxn 100005         // (2)

struct Stack {              // (3)
    DataType data[maxn];    // (4)
    int top;                // (5)
};
  • (1)(1) 用DataType这个宏定义来统一代表栈中数据的类型,这里将它定义为整型,根据需要可以定义成其它类型,例如浮点型、字符型、结构体 等等;
  • (2)(2) maxn代表我们定义的栈的最大元素个数;
  • (3)(3) Stack就是我们接下来会用到的 栈结构体
  • (4)(4) DataType data[maxn]作为栈元素的存储方式,数据类型为DataType,可以自行定制;
  • (5)(5) top即栈顶指针,data[top-1]表示栈顶元素,top == 0代表空栈;

2.入栈

void stackpush(struct stack*stk, datatype dt)//因为会改变stk,所以传入指针
{
stk->data[stk->top]=dt;
stk-<top++;
}

3、出栈

1、动画演示

2、源码详解

  • 出栈 操作,只需要简单改变将 栈顶 减一 即可,代码实现如下:
void StackPopStack(struct Stack* stk) {
    --stk->top;
}

4、清空栈

1、动画演示

2、源码详解

  • 清空栈的操作只需要将 栈顶 指针直接指向 栈底 即可,对于顺序表,也就是 C语言 中的数组来说,栈底 就是下标 0 的位置了,代码实现如下:
void StackClear(struct Stack* stk) {
    stk->top = 0;
}

5、只读接口

  • 只读接口包含:获取栈顶元素、获取栈大小、栈的判空,实现如下:
DataType StackGetTop(struct Stack* stk) {
    return stk->data[ stk->top - 1 ];      // (1)
}
int StackGetSize(struct Stack* stk) {
    return stk->top;                       // (2)
}
bool StackIsEmpty(struct Stack* stk) {
    return !StackGetSize(stk);             // (3)
}
  • (1)(1) 数组中栈元素从 0 开始计数,所以实际获取元素时,下标为 栈顶元素下标 减一;
  • (2)(2) 因为只有在入栈的时候,栈顶指针才会加一,所以它 正好代表了 栈元素个数;
  • (3)(3) 当 栈元素 个数为 零 时,栈为空。

三。栈的链表实现//区别就是栈本身是数组还是链表

1.数据结构定义

struct stacknode{
Datatype data;
struct stacknode*next;
}
struct stack{
struct stacknode*top;
int size;
}
  • (1) 栈结点元素的 数据域,这里定义为整型;
  • (2)(2) struct StackNode;是对链表结点的声明;
  • (3)(3) 定义链表结点,其中DataType data代表 数据域struct StackNode *next代表 指针域
  • (4)(4) top作为 栈顶指针,当栈为空的时候,top == NULL;否则,永远指向栈顶;
  • (5)(5) 由于 求链表长度 的算法时间复杂度是 O(n)O(n) 的, 所以我们需要记录一个size来代表现在栈中有多少元素。每次 入栈size自增,出栈size自减。这样在询问栈的大小的时候,就可以通过 O(1)O(1) 的时间复杂度。

2.入栈

void push(struct stack*stk,Datatype dt)
{
struct stacknode*insertnode=(struct stacknode*)malloc(sizeof(struct stacknode) );
insertnode->next=stk->top;
stk->top=insertnode;
stk->size++;
}

3.出栈

void delete(struct stack*stk)
{
struct stacknode*cur=stk->top;//由于是已经存在的所以不用malloc
stk->top=cur->next;
free(cur);
stk->size--;
}

4.清空栈

清空栈 可以理解为,不断的出栈,直到栈元素个数为零。

void StackClear(struct Stack* stk) {
    while(!StackIsEmpty(stk)) {       // (1)
        StackPopStack(stk);           // (2)
    }
    stk->top = NULL;                  // (3)
}

用数组实现的话stk->top=0表示空栈

5、只读接口

  • 只读接口包含:获取栈顶元素、获取栈大小、栈的判空,实现如下:
DataType StackGetTop(struct Stack* stk) {
    return stk->top->data;                 // (1)
}
int StackGetSize(struct Stack* stk) {
    return stk->size;                      // (2)
}

int StackIsEmpty(struct Stack* stk) {
    return !StackGetSize(stk);
}

  • (1)(1) stk->top作为 栈顶指针,它的 数据域 data就是 栈顶元素的值,返回即可;
  • (2)(2) size记录的是 栈元素个数;
  • (3)(3) 当 栈元素 个数为 零 时,栈为空。

四、两种实现的优缺点

1、顺序表实现

  在利用顺序表实现栈时,入栈 和 出栈 的常数时间复杂度低,且 清空栈 操作相比 链表实现 能做到 O(1)O(1),唯一的不足之处是:需要预先申请好空间,而且当空间不够时,需要进行扩容,扩容方式本文未提及,可以参考以下文章:《C/C++ 面试 100 例》(四)vector 扩容策略。

2、链表实现

  在利用链表实现栈时,入栈 和 出栈 的常数时间复杂度略高,主要是每插入一个栈元素都需要申请空间,每删除一个栈元素都需要释放空间,且 清空栈 操作是 O(n)O(n) 的,直接将 栈顶指针 置空会导致内存泄漏。好处就是:不需要预先分配空间,且在内存允许范围内,可以一直 入栈,没有顺序表的限制。

举报

相关推荐

0 条评论