栈 -- 学习笔记

1.栈的基本概念

栈是一种特殊的线性数据结构,它遵循特定的操作顺序,即后进先出(Last In First Out, LIFO)。这意味着最后添加到栈中的元素将是第一个被移除的元素。栈的这种特性使得它在处理具有递归性质的问题和算法中非常有用。

【特点】

    后进先出:栈的基本操作确保了最后被添加的元素总是第一个被移除。
    单端操作:所有的添加和移除操作仅在栈的同一端进行,这一端被称为“栈顶”,另一端称为“栈底”。

2.基本操作

栈的操作通常包括以下几种:

   1. Push(入栈):将一个元素添加到栈顶。这是一个添加操作,将元素放置在栈的最上方。
  2.  Pop(出栈):从栈顶移除一个元素,并返回被移除的元素。这是一个移除操作,它删除并返回栈顶元素。
   3. Peek 或 Top(查看栈顶元素):返回栈顶元素,但不从栈中移除它。这允许我们查看栈顶的元素而不改变栈的状态。
  4. IsEmpty(检查栈是否为空):确定栈是否为空。如果栈中没有元素,此操作返回 true;否则,返回 false。
    5.Size(获取栈的大小):返回栈中元素的数量。这允许我们了解栈中存储了多少元素。

3.栈的实现

3.1 数组OR链表?

实现栈的两种常见方法是使用数组和链表。每种方法都有其独特的优势和劣势,选择哪种方式取决于具体的应用场景和需求。下面我们将详细探讨使用数组和链表实现栈的优势和劣势,并解释为什么在某些情况下数组可能是更好的选择。

使用数组实现栈的优势:

    随机访问:数组提供了O(1)的时间复杂度来访问任何一个元素,这使得访问栈顶元素非常快速。
    内存连续性:数组在内存中是连续存储的,这有助于提高空间和缓存的效率。
    简单性:使用数组实现栈的代码通常更简单直观,特别是在栈操作不频繁或栈大小固定时。

使用数组实现栈的劣势:

    固定大小:数组的大小在初始化时就固定了,这限制了栈的最大容量,可能会导致栈溢出。
    扩容问题:如果栈满了需要扩容,就必须创建一个更大的数组并复制原数组的元素,这个过程的时间复杂度是O(n)。

使用链表实现栈的优势:

    动态大小:链表实现的栈可以动态地增长和缩小,不受固定大小的限制,从而避免了栈溢出的问题。
    内存利用率:链表是非连续存储的,它可以更好地利用分散的内存空间,理论上可以达到系统内存的限制。

使用链表实现栈的劣势:

    内存开销:链表的每个元素都需要额外的空间来存储指向下一个元素的指针。
    时间开销:相比数组实现,链表访问和操作元素(特别是非栈顶元素)的时间开销较大。
    缓存不友好:由于链表的元素在内存中是非连续存储的,它不如数组那样缓存友好。

3.2 为什么数组更好?

在特定的应用场景下,数组实现的栈可能更受青睐,主要是因为以下几点:

    性能:对于栈顶元素的操作,数组提供了更快的访问速度,尤其是在需要频繁访问或操作栈顶元素的情况下。
    简单性和高效性:数组实现的栈在代码上更直观简单,且由于内存连续性,它在空间和缓存效率上通常表现更好。
    可预测的性能:数组的固定大小虽然是一个限制,但也意味着栈的性能是可预测的,不会因为动态扩容而出现性能波动。

3.3 数组实现栈(静态)

#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 100  // 栈的最大容量

typedef struct stack_s 
{
    int data[MAX_SIZE];  // 用于存储栈中元素的数组
    int top;             // 指示栈顶元素的下标
} stack_t;

void stack_init(stack_t *stack);
int stack_full(stack_t *stack);
int stack_empty(stack_t *stack);
void stack_push(stack_t *stack, int value);
int stack_pop(stack_t *stack);
int stack_view_peek(stack_t *stack);

int main (int argc, char **argv)
{
        stack_t         stack;

        stack_init(&stack);
        /*  
        for(i=0; i<3; i++)
        {
                stack_push(&stack, value[i]);
        }
        */
        stack_push(&stack, 10);
        stack_push(&stack, 20);
        stack_push(&stack, 30);

        printf("栈顶元素:%d\n",stack_view_peek(&stack));

        printf("出栈元素:%d\n", stack_pop(&stack)); 
        printf("出栈元素:%d\n", stack_pop(&stack)); 

        printf("栈顶元素:%d\n",stack_view_peek(&stack));

        return 0;
} 

void stack_init(stack_t *stack)
{
        stack->top = -1;//栈为空
}

int stack_empty(stack_t *stack)
{
        return stack->top == -1;
}

int stack_full(stack_t *stack)
{
        return stack->top == MAX_SIZE - 1;
}

void stack_push(stack_t *stack, int value)
{
        if( stack_full(stack) ) 
        {
                printf("栈满,无法入栈\n");
                return;
        }
        stack->data[++(stack->top)] = value;
}

int stack_pop(stack_t *stack)
{
        if( stack_empty(stack) )
        {
                printf("栈空,无法出栈\n");
                return -1;
        }
        return stack->data[(stack->top)--];
}

int stack_view_peek(stack_t *stack)
{
        if( stack_empty(stack) )
        {
                printf("栈空,无法查看\n");
                return -2;
        }
        return stack->data[stack->top];
}

3.4 单链表实现(动态)

链表的结点相当于数组的一个元素,malloc动态分配一段存储空间。下面是入栈和出栈的草图

#include <stdio.h>
#include <stdlib.h>

typedef struct Node
{
    int data;
    struct Node* next;
} Node;

typedef struct Stack
{
    Node* top;
} Stack;

void initStack(Stack* stack);
void push(Stack* stack, int data);
int pop(Stack* stack);
int peek(Stack* stack);
void display(Stack* stack);

int main() 
{
    Stack stack;
    initStack(&stack);

    push(&stack, 10);
    push(&stack, 20);
    push(&stack, 30);
    display(&stack);

    printf("Top element is %d\n", peek(&stack));

    pop(&stack);
    display(&stack);

    pop(&stack);
    pop(&stack);

    display(&stack);

    return 0;
}

//初始化栈时,栈顶指针应该指向NULL
void initStack(Stack* stack) 
{
    stack->top = NULL;
}

//检查栈是否为空
int isEmpty(Stack* stack) 
{
    return stack->top == NULL;
}

//入栈操作
void push(Stack* stack, int data) 
{
    Node* newNode = (Node*)malloc(sizeof(Node));
    if (newNode == NULL) 
    {
        printf("Memory allocation failed\n");
        return;
    }
    newNode->data = data;
    newNode->next = stack->top;
    stack->top = newNode;
    printf("Pushed %d onto the stack\n", data);
}

//出栈操作
int pop(Stack* stack) 
{
    if (isEmpty(stack)) 
    {
        printf("Stack underflow\n");
        exit(EXIT_FAILURE);
    }
    Node* temp = stack->top;
    int poppedData = temp->data;
    stack->top = stack->top->next;
    free(temp);
    printf("Popped %d from the stack\n", poppedData);
    return poppedData;
}

//查看栈顶元素
int peek(Stack* stack) 
{
    if (isEmpty(stack)) 
    {
        printf("Stack is empty\n");
        exit(EXIT_FAILURE);
    }
    return stack->top->data;
}

//显示栈中的所有元素
void display(Stack* stack) 
{
    if (isEmpty(stack)) 
    {
        printf("Stack is empty\n");
        return;
    }
    Node* current = stack->top;
    printf("Stack elements are:\n");
    while (current != NULL) {
        printf("%d -> ", current->data);
        current = current->next;
    }
    printf("NULL\n");
}


参考原文链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值