一、概念
  栈是限定仅在表尾进行插入或删除操作的线性表。表尾端称为栈顶,表头端成为栈底,栈的一大特点是先进后出
二、顺序栈的实现
  和线性表一样,栈也有两种存储方式,这里实现顺序栈。
1.栈结构

typedef struct{
	SElemType *base;
	SElemType *top;
	int        stacksize;
}

2.具体实现
(1)栈空:s.top == s.base;
(2)栈满:s.top - s.base = stacksize;
(3)入栈和出栈规则:入栈时,赋值后top指针才上移,栈顶指针始终在栈顶元素的下一个位置上;出栈时,指针下移后在取值。

//头文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#define STACK_INIT_SIZE 100
#define STACKINCREASEMENT 10
typedef int SElemType;
typedef struct{
	SElemType *base;
	SElemType *top;
	int       stackSize;
}SqStack;

bool initStack(SqStack &s);

bool destoryStack(SqStack &s);

bool clearStack(SqStack &s);

bool stackEmpty(SqStack s);

int stackLength(SqStack s);

bool getTop(SqStack s,SElemType &e);

bool push(SqStack &s,SElemType e);

bool pop(SqStack &s,SElemType &e);

bool stackTraverse(SqStack s);
//cpp文件
#include"stack.h"

bool initStack(SqStack &s){

	s.base = (SElemType*)malloc(STACK_INIT_SIZE * sizeof(SElemType));
	if(!s.base) return false;

	s.top = s.base;
	s.stackSize = STACK_INIT_SIZE;
	return true;
}

bool destoryStack(SqStack &s){

	if(!s.base) return false;
	s.stackSize = 0;
	free(s.base);
	s.top = s.base = NULL;
	return true;
}

bool clearStack(SqStack &s){
	if(s.base == s.top) return false;
	s.top = s.base;
	return true;
}

bool stackEmpty(SqStack s){
	if(s.base == s.top)
		return true;
	return false;
}

int stackLength(SqStack s){
	
	return s.top - s.base;
}

bool getTop(SqStack s,SElemType &e){

	if(s.base == s.top){
		e =  0 ;
		return false;
	}
	e = *(s.top - 1);
	return true;
}

bool push(SqStack &s,SElemType e){

	if((s.top - s.base) >= s.stackSize){
		s.base = (SElemType*)realloc(s.base,(s.stackSize + STACKINCREASEMENT) * sizeof(SElemType));
		if(!s.base) return false;
		s.top = s.base + s.stackSize; 
		s.stackSize = s.stackSize + STACKINCREASEMENT;
	}

	*s.top++ = e;//先入栈,再上移动,

	return true;
}

bool pop(SqStack &s,SElemType &e){

	if(s.base == s.top) return false;
	e = *(--s.top);//先向下移动,在出栈
	return true;
}

bool stackTraverse(SqStack s){
	if(s.top == s.base) return false;

	SElemType *p = s.top - 1;
	while((p - s.base) >= 0){
		printf("%d ",*p);
		p--;
	}
	return true;
}

int main(){
	SqStack s;
	initStack(s);
	push(s,1);
	push(s,2);
	push(s,3);

	stackTraverse(s);
	printf("\n");
	printf("length = %d\n",stackLength(s));

	SElemType e ;
	getTop(s,e);
	printf("top = %d\n",e);

	pop(s,e);
	stackTraverse(s);
	printf("\n");

	getchar();
	return 0;
}
<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; } ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值