本文深入讲解了栈这一数据结构的概念、特点及其实现方法。详细介绍了栈的基本操作,如初始化、遍历、判断空栈、求栈长度、压栈、出栈等,并探讨了栈的排序、特定节点的删除与插入等高级操作。

‎时间:2017‎年‎1‎月‎20‎日,‏‎15:19:06
自学于郝斌老师

栈的定义:

静态内存在栈里分配,动态内存在堆里分配。
在栈里面,系统帮你分配内存,在堆里,要自己手动分配
栈和堆表示的是分配数据的一种方式
静态的局部变量 是以压栈出栈的方式分配内存,称为  栈区
而动态的是以堆排序的方式分配内存,称为  堆区
eg:
#include <stdio.h>
#include <malloc.h>
void f(int k)
{
  int m;
  double *q=(double*)malloc(200);
}

int main(void)
{
  int i=10;
  int *p=(int *)malloc(100);
  return 0;
}
其中 局部变量 i,p,m,q,k  都在栈里面分配,而100200都在堆里分配的。

大纲:
     线性结构的两种常见应用之一 栈

     定义

         一种可以实现“先进后出”的存储结构
         栈类似于箱子

栈的分类:

 静态栈(以数组方式)
    动态栈(以链表方式)_重要

重点:

    先进后出 的特点,删除增加数据时,从后往前。

队列,先进先出;栈,先进后出。

栈的初始化

void Init_stack(PSTACK pS)//初始化栈的关键:让栈的尾指针与头指针共同指向一个无用的节点
{                         //并且还要把其节点的指针域清空
                         //而这个节点就是以后的尾节点,其含义就相当于单链表中的头节点
    pS->pTop = (PNODE)malloc(sizeof(NODE));
    pS->pBottom = pS->pTop;
    pS->pBottom->pNext = NULL;
    return;
}

栈的遍历

void Traverse_stack(PSTACK pS)//其关键:栈中凡是不等尾节点的节点,都是有效元素
{                            //注意:不同于链表的是:头节点的元素一定要输出,因为在栈中 头节点是有效元素
                            //而在链表中,头节点是无实际含义的节点,其作用就是便于对链表的操作
                           //在链表中 在头节点之后的节点才是有效节点
                          //总结:栈的尾节点含义相当于链表的头节点,操作其结构时,也要相应进行改变。
    PNODE p = pS->pTop;
    while (p != pS->pBottom)
    {
        printf("%d ", p->data);
        p = p->pNext;
    }
    printf("\n");
    return;
}

判断是否空栈

bool Empty_stack(PSTACK pS)//判断空栈的关键:(也就是根据初始化栈的原理而来)头节点是否等于尾节点
{
    if (pS->pTop == pS->pBottom)
        return true;
    else
        return false;
}

求栈长度

int Length_stack(PSTACK pS)//其关键:有多少个不等于尾节点的节点 就是其栈的长度
                           //注意:不能擅用头指针,应该新建个局部指针代替,不然头指针位置会改变,引起未来麻烦
{
    PNODE p = pS->pTop;
    int len = 0;
    while (p != pS->pBottom)
    {
        len++;
        p = p->pNext;
    }
    return len;
}

栈的压栈

void Push_stack(PSTACK pS)//进栈的位置:尾节点与头节点共同指向节点的 后一个节点,然后以此类推
{                         //元素进栈之后,栈的尾指针不变,头指针永远指向第一个元素 也就是最后一个进栈的元素
    int len;
    printf("输入生成节点的总个数:");
    scanf("%d", &len);
    for (int i = 1;i <= len;i++)
    {
        printf("输入第%d个节点的值:", i);
        PNODE pNew = (PNODE)malloc(sizeof(NODE));
        scanf("%d", &pNew->data);
        pNew->pNext = pS->pTop;
        pS->pTop = pNew;
    }
    return;
}

栈的出栈

bool Pop_stack(PSTACK pS,int *Val)//所谓出栈,就是把栈的最后一个节点移除  与删除节点方法类似
{
    if (pS->pTop == pS->pBottom)
        return false;
    PNODE q = pS->pTop;
    *Val = q->data;
    pS->pTop = q->pNext;
    free(q);
    q = NULL
    return true;
}

栈的清除

bool Clear_stack(PSTACK pS)//其原理很聪明,利用两个指针,一上接一下的清除掉各个节点。
{
    if (pS->pTop == pS->pBottom)
        return false;
    PNODE p = pS->pTop;//p 指针为主要清除指针
    PNODE q = NULL;   //q 指针为其辅助,给 q 指针 指出下一个清除目标
    while (p != pS->pBottom)
    {
        q = p->pNext;//q 指针永远指向 p指针的下一个节点!
        free(p);
        p = q;     //每次清除节点后,p 指针又指向 q 指针,继而清除下一个节点,以此类推。
    }
    pS->pTop = pS->pBottom;//注意,最后 尾指针一定要等于头指针!相当于把栈 初始化!
    return true;
}

栈不同于链表,不能对栈中元素进行操作,不然就不是栈了
但自己假期闲着无聊,搞了增删排序,但也是不符合栈的定义的。


栈的排序

void Sort_stack(PSTACK pS)//栈的排序与单链表的排序类似,同是利用冒泡框架,泛型算法
{
    PNODE p, q;
    int i, j, temp;
    int len = Length(pS);
    for (p=pS->pTop,i = 0;i < len - 1;i++,p=p->pNext)
    {
        for (q=p->pNext,j = i + 1;j < len;j++,q=q->pNext)
        {
            if (p->data > q->data)
            {
                temp = p->data;
                p->data = q->data;
                q->data = temp;
            }
        }
    }
    return;
}

栈的特定节点删除

bool Delete_stack(PSTACK pS, int pos, int *pVal)//这个函数需要考虑的地方不少
{
    if (pS->pTop == pS->pBottom)//先是判断空栈,不多说
        return false;
    int i = 1;//因为栈的第一个节点有实际含义,所以变量i 的初始值为:1
    PNODE p = pS->pTop;
    if (pos == 1)//一定要注意,栈的第一个元素是一个节点,不同于链表
                 //所以第一个节点需要单独拿出来操作,说白了,自己很弱,不知道怎么搞。。只能这样。
    {
        pS->pTop = p->pNext;
        *pVal = p->data;
        free(p);
        p = NULL;
        return true;
    }//要点:删除元素需要循环到的位置 是输入节点位置的前一个节点!p->pNext是要删除的节点!
     //      删除元素的位置必须是尾节点之前,并且不包括尾节点!因为尾节点以及之后不能删除!
                                                             //所以p->pNext!=pS->pBottom!
    while (i < pos - 1 && p->pNext != pS->pBottom)
    {
        i++;
        p = p->pNext;
    }
    if (i > pos - 1 || p->pNext == pS->pBottom)
        return false;
    PNODE q = p->pNext;//正常步骤
    p->pNext = q->pNext;
    *pVal = q->data;
    free(q);
    q = NULL;
    return true;
}

栈的特定节点增加

bool Insert_stack(PSTACK pS, int pos, int val)//其考虑点与删除函数差不多,但函数核心各不相同
{
    if (pS->pTop == pS->pBottom)//判断空栈,不多说
        return false;
    if (pos == 1)//如果增加第一个节点,需要单独拿出来进行操作
                 //与单链表不同的是:栈的首节点为有效节点,所以操作与链表大同小异
    {
        PNODE pNew = (PNODE)malloc(sizeof(NODE));
        pNew->data = val;
        pNew->pNext = pS->pTop;
        pS->pTop = pNew;
        return true;
    }
     //要点:增加元素需要循环到的位置 是输入节点位置的前一个节点!p->pNext是要增加的节点!
    //       增加元素的位置必须是尾节点之前,并且包括尾节点!所以是 p != pS->pBottom
   //        如果 p 是尾节点的话,p->pNext == NULL,pNew->pNext = p->pNext;p->pNext = pNew;
  //         这样就相等于在尾节点后面增加了一个节点,而这个节点是永远遍历不到的,是错误的;
 //          所以增加节点的位置必须在尾节点以及尾节点之前!

    int i = 1;//因为栈的第一个节点有实际含义,所以变量i 的初始值为:1
    PNODE p = pS->pTop;
    while (i < pos - 1 && p != pS->pBottom)
    {
        i++;
        p = p->pNext;
    }
    if (i > pos - 1 || p == pS->pBottom)
        return false;
    PNODE pNew = (PNODE)malloc(sizeof(NODE));//正常步骤
    pNew->data = val;
    pNew->pNext = p->pNext;
    p->pNext = pNew;
    return true;
}

代码:

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

typedef struct Node
{
    int data;
    struct Node * pNext;
}NODE, *PNODE;

typedef struct Stack
{
    PNODE pTop;
    PNODE pBottom;
}STACK, *PSTACK;

int Length_stack(PSTACK);
void Init_stack(PSTACK);
void Push_stack(PSTACK);
void Traverse_stack(PSTACK);
void Sort_stack(PSTACK);
void Pop_stack(PSTACK, int *);
bool Empty_stack(PSTACK);
bool Delete_stack(PSTACK, int, int *);
bool Insert_stack(PSTACK, int, int);
bool Clear_stack(PSTACK);

int main(void)
{
    STACK S;
    Init_stack(&S);
    Push_stack(&S);
    Empty_stack(&S);
    if (Empty_stack(&S))
        printf("栈为空\n");
    else
    {
        printf("栈不空\n");
        printf("遍历栈,得:");
        Traverse_stack(&S);
        int len = Length_stack(&S);
        printf("栈节点的个数为:%d\n", len);
        Sort_stack(&S);
        printf("经排序后,遍历得:");
        Traverse_stack(&S);
        int Val;
        Pop_stack(&S, &Val);
        printf("出栈一次,出栈元素为:%d\n", Val);
        printf("出栈后,遍历栈,得:");
        Traverse_stack(&S);
        int pos1;
        int val;
        printf("请输入您要删除节点的位置:");
        scanf("%d", &pos1);
        if (Delete_stack(&S, pos1, &val))
        {
            printf("出栈的元素是:%d\n", val);
            printf("遍历栈,得:");
            Traverse_stack(&S);
        }
        else
            printf("您输入的节点位置有误\n");
        int pos2;
        printf("请输入您要增加节点的位置:");
        scanf("%d", &pos2);
        int val2;
        printf("请输入您要增加节点的值:");
        scanf("%d", &val2);
        if (Insert_stack(&S, pos2, val2))
        {
            printf("遍历栈,得:");
            Traverse_stack(&S);
        }
        else
            printf("您输入的节点位置有误\n");
        if (Clear_stack(&S))
            printf("目前栈为空\n");
        else
            printf("清除失败\n");
    }
    return 0;
}

void Init_stack(PSTACK pS)
{
    pS->pTop = (PNODE)malloc(sizeof(NODE));
    pS->pBottom = pS->pTop;
    pS->pBottom->pNext = NULL;
    return;
}

void Push_stack(PSTACK pS)
{
    printf("请输入那您要生成节点的个数:");
    int len;
    scanf("%d", &len);
    for (int i = 1;i <= len;i++)
    {
        PNODE pNew = (PNODE)malloc(sizeof(NODE));
        printf("请输入第%d个节点的值:", i);
        scanf("%d", &pNew->data);
        pNew->pNext = pS->pTop;
        pS->pTop = pNew;
    }
    return;
}

void Traverse_stack(PSTACK pS)
{
    PNODE p = pS->pTop;
    while (p != pS->pBottom)
    {
        printf("%d ", p->data);
        p = p->pNext;
    }
    printf("\n");
    return;
}

bool Empty_stack(PSTACK pS)
{
    if (pS->pTop == pS->pBottom)
        return true;
    else
        return false;
}

void Pop_stack(PSTACK pS, int *Val)
{
    PNODE q = pS->pTop;
    pS->pTop = q->pNext;
    *Val = q->data;
    free(q);
    q = NULL;
    return;
}

bool Delete_stack(PSTACK pS, int pos, int *pVal)
{
    if (Empty_stack(pS))
        return false;
    int i = 1;
    PNODE p = pS->pTop;
    if (pos == 1)
    {
        pS->pTop = p->pNext;
        *pVal = p->data;
        free(p);
        p = NULL;
        return true;
    }
    while (p->pNext != pS->pBottom && i < pos - 1)
    {
        p = p->pNext;
        i++;
    }
    if (i > pos - 1 || p->pNext == pS->pBottom)
        return false;
    PNODE q = p->pNext;
    *pVal = q->data;
    p->pNext = q->pNext;
    free(q);
    q = NULL; 
    return true;
}

bool Insert_stack(PSTACK pS, int pos, int val)
{
    if (Empty_stack(pS))
        return false;
    if (pos == 1) 
    {
        PNODE pNew = (PNODE)malloc(sizeof(NODE));
        pNew->data = val;
        pNew->pNext = pS->pTop;
        pS->pTop = pNew;
        return true;
    }
    int i = 1;
    PNODE p = pS->pTop;
    while (p != pS->pBottom && i < pos - 1)
    {
        i++;
        p = p->pNext;
    }
    if (i > pos - 1 || p == pS->pBottom)
        return false;
    PNODE pNew = (PNODE)malloc(sizeof(NODE));
    pNew->data = val;
    pNew->pNext = p->pNext;
    p->pNext = pNew;
    return true;
}

bool Clear_stack(PSTACK pS)
{
    if (Empty_stack(pS))
        return false;
    PNODE p = pS->pTop;
    PNODE q = NULL;
    while (p != pS->pBottom)
    {
        q = p->pNext;
        free(p);
        p = q;
    }
    pS->pTop = pS->pBottom;
    return true;
}

int Length_stack(PSTACK pS)
{
    PNODE p = pS->pTop;
    int len = 0;
    while (p != pS->pBottom)
    {
        len++;
        p = p->pNext;
    }
    return len;
}

void Sort_stack(PSTACK pS)
{
    int i, j, temp;
    PNODE p, q;
    int len = Length_stack(pS);
    for (p = pS->pTop, i = 0;i<len - 1;i++, p = p->pNext)
    {
        for (q = p->pNext, j = i + 1;j<len;j++, q = q->pNext)
        {
            if (p->data<q->data)
            {
                temp = p->data;
                p->data = q->data;
                q->data = temp;
            }
        }
    }
    return;
}
【完美复现】面向配电网韧性提升的移动储能预布局与动态调度策略【IEEE33节点】(Matlab代码实现)内容概要:本文介绍了基于IEEE33节点的配电网韧性提升方法,重点研究了移动储能系统的预布局与动态调度策略。通过Matlab代码实现,提出了一种结合预配置和动态调度的两阶段优化模型,旨在应对电网故障或极端事件时快速恢复供电能力。文中采用了多种智能优化算法(如PSO、MPSO、TACPSO、SOA、GA等)进行对比分析,验证所提策略的有效性和优越性。研究不仅关注移动储能单元的初始部署位置,还深入探讨其在故障发生后的动态路径规划与电力支援过程,从而全面提升配电网的韧性水平。; 适合人群:具备电力系统基础知识和Matlab编程能力的研究生、科研人员及从事智能电网、能源系统优化等相关领域的工程技术人员。; 使用场景及目标:①用于科研复现,特别是IEEE顶刊或SCI一区论文中关于配电网韧性、应急电源调度的研究;②支撑电力系统在灾害或故障条件下的恢复力优化设计,提升实际电网应对突发事件的能力;③为移动储能系统在智能配电网中的应用提供理论依据和技术支持。; 阅读建议:建议读者结合提供的Matlab代码逐模块分析,重点关注目标函数建模、约束条件设置以及智能算法的实现细节。同时推荐参考文中提及的MPS预配置与动态调度上下两部分,系统掌握完整的技术路线,并可通过替换不同算法或测试系统进一步拓展研究。
<think>我们正在讨论数据中关于的操作,特别是清空的方法和实现。根据之前的引用内容,清空通常是将顶指针重置到初始状态(如-1),而不是释放的存储空间。销毁则是释放所占用的内存资源。 根据引用[1]中的清空操作: ```c /* 清空 */ bool ClearStack(SqStack &S) { if (S.top == -1) return false; S.top = -1; // 将顶指针指向-1,遍历的时候到top就结束 return true; } ``` 这里,清空的操作非常简单,直接将顶指针top置为-1(初始状态)即可。这样中原来的数据虽然没有被覆盖,但因为顶指针已经指向了初始位置,后续的操作会覆盖原来的数据,因此逻辑上已经被清空。 引用[3]中也提到了初始化时是将顶指针置为-1,并且将数组中元素置0(可选操作)。清空的操作与初始化类似,但通常不需要将数组中的每个元素都重置(除非有特殊的安全要求),因为只需要重置顶指针,在逻辑上就为空了。 另外,注意清空和销毁的区别: - 清空(ClearStack):重置的状态,使其成为空,但的存储空间仍然保留,可以继续使用。 - 销毁(DestroyStack):释放所占用的内存资源,不再存在。引用[1]中销毁的操作为: ```c /* 销毁 */ bool DestroyStack(SqStack &S) { if (S.top == -1) return false; free(S.data); // 释放的存储空间 return true; } ``` 注意:销毁的操作中释放了的存储数组(S.data),并将顶指针置为-1(在引用[1]的销毁操作中没有重置top,但通常销毁后本身可能也不再使用,所以重置top不是必须的,但释放内存是必须的)。另外,如果本身是动态分配的(如引用[1]中的是静态数组,但如果是动态分配的整个结体,则销毁时需要释放整个结体),则销毁方式会有所不同。 下面我将详细介绍清空的方法和实现,包括顺序(静态分配和动态分配)和链式。 ## 一、顺序的清空操作 ### 1. 静态分配的顺序 静态分配的顺序使用固定大小的数组存储元素,清空操作只需重置顶指针。 ```c // 定义 #define MAXSIZE 100 // 的最大容量 typedef struct { int data[MAXSIZE]; // 静态数组 int top; // 顶指针,初始为-1 } SqStack; // 清空 void ClearStack(SqStack *S) { S->top = -1; // 重置顶指针 } ``` ### 2. 动态分配的顺序 动态分配的顺序在初始化时动态分配了存储数组,清空操作同样只需重置顶指针,不需要释放数组内存(因为清空后还要继续使用)。 ```c // 动态分配的顺序 typedef struct { int *data; // 动态数组 int top; // 顶指针 int capacity; // 的容量 } DynStack; // 清空 void ClearStack(DynStack *S) { S->top = -1; // 重置顶指针 // 注意:动态数组的内存不释放,因为还要继续使用 } ``` ## 二、链式的清空操作 链式使用链表存储元素,清空操作需要释放所有节点的内存,并将顶指针置为NULL。 ```c // 链式节点 typedef struct StackNode { int data; struct StackNode *next; } StackNode; // 链式 typedef struct { StackNode *top; // 顶指针 } LinkStack; // 清空(需要释放所有节点) void ClearStack(LinkStack *S) { StackNode *p = S->top; while (p) { StackNode *temp = p; p = p->next; free(temp); // 释放节点 } S->top = NULL; // 顶指针置空 } ``` ## 三、注意事项 1. **清空与销毁的区别**: - 清空:重置为空状态,保留存储空间。 - 销毁:释放所有资源,不再存在。 2. **时间复杂度**: - 顺序清空:$O(1)$(只需重置指针) - 链式清空:$O(n)$(需要遍历释放所有节点) 3. **安全考虑**: - 在清空链式时,要防止内存泄漏,必须释放所有节点。 - 顺序清空后,原来的数据可能残留在数组中,如果涉及敏感数据,应该手动覆盖(但通常清空操作只重置指针)。 ## 四、清空操作示例 以下是一个完整的顺序(静态分配)清空操作的示例代码: ```c #include <stdio.h> #include <stdbool.h> #define MAXSIZE 10 typedef struct { int data[MAXSIZE]; int top; } SqStack; // 初始化 void InitStack(SqStack *S) { S->top = -1; } // 清空 void ClearStack(SqStack *S) { S->top = -1; } // 判断空 bool StackEmpty(SqStack S) { return S.top == -1; } int main() { SqStack S; InitStack(&S); // ... 入操作 ... // 清空 ClearStack(&S); if (StackEmpty(S)) { printf("已清空\n"); // 输出:已清空 } return 0; } ``` ## 五、引用说明 - 清空顺序只需重置顶指针(静态和动态分配)[^1][^3]。 - 链式的清空需要逐个释放节点并将顶指针置空。 §§相关问题§§ 1. 的清空操作和销毁操作在哪些场景下需要分别使用? 2. 链式的清空操作中如何避免内存泄漏? 3. 如何实现一个支持撤销操作(使用多个)的编辑器? 4. 在递归函数调用中如何保存现场? 5. 顺序和链式在清空操作上的性能差异对实际应用有什么影响? </think>## 数据中清空的方法与实现详解 ### 一、清空的核心概念 清空(Clear Stack)是指**重置的状态使其成为逻辑空**,但不释放的存储空间。这与销毁(Destroy Stack)有本质区别: - 清空:保留数据,仅移除所有元素(时间复杂度$O(1)$) - 销毁:释放占用的内存资源(时间复杂度$O(1)$或$O(n)$)[^1][^3] ### 二、顺序的清空实现 #### 1. 静态分配的顺序 ```c /* 清空静态顺序 */ bool ClearStack(SqStack *S) { if (S->top == -1) return false; // 已是空 S->top = -1; // 核心操作:重置顶指针 return true; } ``` - **操作原理**:顶指针`top`置为初始值-1,标记为空状态 - **时间复杂度**:$O(1)$ - **空间复杂度**:$O(1)$ - **特点**:原数据未被覆盖但逻辑失效[^1] #### 2. 动态分配的顺序 ```c /* 清空动态顺序 */ bool ClearStack_Dynamic(DynStack *S) { if (S->top == -1) return false; S->top = -1; // 重置顶指针 // S->data数组保留不释放 return true; } ``` - **关键区别**:保留动态分配的存储数组,仅重置指针[^3] ### 三、链式的清空实现 ```c /* 清空链式 */ void ClearStack_Linked(LinkStack *S) { while (S->top != NULL) { // 遍历释放所有节点 StackNode *temp = S->top; S->top = S->top->next; free(temp); // 释放节点内存 } S->count = 0; // 重置节点计数器 } ``` - **时间复杂度**:$O(n)$(需遍历所有节点) - **空间复杂度**:$O(1)$ - **特点**:必须显式释放每个节点内存[^3] ### 四、清空操作的技术要点 1. **状态重置**: - 顺序:`top = -1` - 链式:`top = NULL` + `count = 0` 2. **内存处理差异**: ```mermaid graph LR A[清空操作] --> B[顺序] A --> C[链式] B --> D[仅重置指针] C --> E[释放所有节点] ``` 3. **错误处理**: - 检查是否已空(避免冗余操作) - 检查指针有效性(防野指针) ### 五、清空 vs 销毁 vs 初始化 | 操作 | 目标 | 内存变化 | 状态 | |--------|-----------------------|--------------------|--------------| | 清空 | 移除所有元素 | 存储空间保留 | 逻辑空 | | 销毁 | 释放所有资源 | 内存完全释放 | 不存在 | | 初始化 | 创建空 | 分配/重置内存 | 初始空 | > **应用场景**:清空操作常用于需要复用的场景,如编译器语法分析中的符号重用[^1] ### 六、完整示例(顺序清空) ```c #include <stdio.h> #define MAXSIZE 100 typedef struct { int data[MAXSIZE]; int top; } SqStack; // 初始化 void InitStack(SqStack *S) { S->top = -1; // 初始空标记[^3] } // 清空 void ClearStack(SqStack *S) { S->top = -1; // 核心清空操作 } // 检测空 int IsEmpty(SqStack S) { return (S.top == -1); } int main() { SqStack S; InitStack(&S); // ... 入操作 ... ClearStack(&S); printf("是否空: %d\n", IsEmpty(S)); // 输出1(真) return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值