栈的实现&用一个数组空间实现两个栈

本文深入探讨了栈的两种实现方式:共享存储单元的双栈和链栈。详细解析了如何在一个数组中同时实现两个栈,通过动态调整栈顶指针避免空间浪费,以及链栈的头部操作原理,提升数据结构理解和编程技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

栈是限制线性表中元素的插入和删除只能在线性表同一端进行操作的一种特殊的线性表,允许插入和删除的一端称为栈顶,另一端固定为栈底。
注意:操作只能在栈顶进行操作。
我个人觉得栈还是很好理解的,就不做过多介绍。

栈的共享存储单元:一个数组实现两个栈。
如下图所示,分配一个数组空间,分别在数组的两边实现一个栈,
在这里插入图片描述
(1)将所分配的数组空间的最小位置和最大位置分别设置为两个栈的栈顶top1和top2。
(2)对于第一个栈,若入栈,则top1++。对于第二个栈,若入栈,则top2–。分配的数组空间,使用时,由两边向中间。当top1+1==top2时,即表示数组满,即空间满,不可以在进行入栈操作。
(3)具体是对哪一个栈进行操作,我们设置一个标记tag来标记,若tag为0,对第一个栈进行操作。否则为第二个栈。

#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
#include<iostream>
typedef int tag;//用tag来标记是哪个栈 
using namespace std;

typedef struct stack
{
	int data[MAXSIZE];
	int top1;
	int top2;; 
}Mystack;


Mystack *Initial()
{
	Mystack *mystack=(Mystack*)malloc(sizeof(struct stack));
	mystack->top1=-1;//注意这里是-1,若是为0的话,其实第一个位置是给初始化浪费了的
	mystack->top2=MAXSIZE-1;
	return mystack;
}

int Push(Mystack *stack,int P,tag X)
{
	if(stack->top1!=stack->top2-1)//判断栈未满
	{ 
       if(X==0)
      {
    	stack->data[++stack->top1]=P;
	   }
	   else
	   {
	   	stack->data[--stack->top2]=P;
	   }	
	   return 0;
	} 
	cout<<"栈满,插入失败";
	return 0;  
}

int Pop(Mystack *stack,tag X) 
{
	  if(X==0)
      {
    	if(stack->top1==-1)//注意这里也是-1
    	{
    		cout<<"栈1空"; 
    		return 0;
		}
    	else
	 	{
		return stack->data[(stack->top1)--];	
		 }
	   }
	   else
	   {
	   	if(stack->top2==MAXSIZE-1)
	   	{
	   		cout<<"栈2空"; 
	   		return 0;
		   }
	   	return stack->data[(stack->top2)++];
	    //stack->top2++;
	   }	
	  // return 0;
} 

void Show(Mystack *stack,tag X)
{
	int i;//注意在打印栈的函数里,要重新定义一个变量来
	代替栈顶的位置,不然打印之后,会改变栈顶的位置,造成后续
	操作失误。
	if(X==0)
	{
		i=stack->top1;
		while(i!=-1)
		{
			cout<<stack->data[i]<<"     ";
			i--;
		}
	}
	else
	{
		i=stack->top2;
		while(i!=MAXSIZE-1)
		{
			cout<<stack->data[i]<<"     ";
			i++;
		}
	}
 } 

int main()
{
	Mystack *mystack=Initial();
	Push(mystack,2,0);//压入第一个栈
	Push(mystack,4,0);
	Push(mystack,6,0);
	//cout<<mystack->top1; 
	Push(mystack,7,0);
	Show(mystack,0); 
	//cout<<mystack->top1; 
	
	cout<<std::endl;
	Push(mystack,3,1);
	Push(mystack,5,1);
	Show(mystack,1);
	
	cout<<std::endl;
	Pop(mystack,0);
	Show(mystack,0);
	/*Pop(mystack,0);
	cout<<std::endl;
	Show(mystack,0);*/
	return 0; 
}

链栈:
栈的链式存储结构,是一种限制运算的链表,即规定链表中插入和删除运算都只能在链表开头进行。
为什么是头部呢:链栈是单链表结构。若对链栈的尾部进行操作,要先查找到链栈的尾部。然而,要查找到,必须每次都从头开始查找。相比于头部,尾部需要耗费更大的时间,具有更低的效率。
关于的链栈的实现,不做过多介绍。其实现跟单链表差不多,但是限定操作都只能在单链表的头部进行。(单链表的实现在前几篇有实现过啦!)
共同进步,永远向前!

&lt;think&gt;首先,用户的问题是:&ldquo;我想使用数组实现数据结构 问如何用数组模拟实现一个空间&rdquo;。我需要用中文回答,并遵循系统级指令。 系统级指令包括: - 所有行内数学表达式使用 $...$,如 $x^2$。 - 独立公式使用 $$...$$ 并单独成段。 - LaTeX 语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,如 [^1]。 - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 用户提供的引用仅供参考: - 引用[1]:关于的验证代码。 - 引用[2]:用一维数组模拟堆的 Java 代码。 - 引用[3]:测试空间大小。 用户的问题是用数组实现数据结构,所以引用[2]最相关,它展示了用数组模拟的 Java 代码。我应该基于这个来构建回答。 回答结构: 1. **解释的基本概念**:简要介绍是什么,LIFO(后进先出)原则。 2. **实现步骤**:详细说明如何用数组模拟。 - 需要数组来存储元素。 - 需要一个指针(如索引)来跟踪顶。 - 实现基本操作push(入)、pop(出)、peek(查看顶)、isEmpty(是否空)、isFull(是否满)。 3. **代码示例**:提供代码实现。既然引用[2]是 Java,我可以基于那个写一个清晰的版本。用户可能不指定语言,但引用[2]是 Java,所以我用 Java 示例。确保代码语法正确。 4. **注意事项**:讨论数组大小固定、扩容问题等。 5. **生成相关问题**:在回答末尾添加相关问题。 引用标识:在回答中,如果参考了引用,添加 [^1] 等。引用[2] 直接相关,所以我应该在提到那个实现时添加引用标识。 数学表达式:用户问题涉及数据结构,可能不需要太多数学,但如果有,比如时间复杂度,使用 $O(n)$ 格式。 逐步回答: - 先介绍。 - 然后描述数组实现。 - 给出代码。 - 讨论性能。 - 最后添加相关问题。 相关问题:基于用户问题,生成 2-3 个相关问题,如优化、应用等。 最终回答结构: 1. 的简介。 2. 数组实现的步骤。 3. 代码实现(用数组模拟)。 4. 时间复杂度分析。 5. 注意事项。 6. 相关问题。&lt;/think&gt;### 使用数组实现数据结构 是一种后进先出(LIFO)的数据结构,支持入push)、出(pop)、查看顶(peek)等基本操作。使用数组模拟是一种常见实现方式,核心思想是通过一个数组存储元素,并用一个索引(顶指针)跟踪顶位置。下面我将逐步解释实现步骤,并提供代码示例。实现基于固定大小的数组,时间复杂度为 $O(1)$ 对于基本操作(在数组未满时),空间复杂度为 $O(n)$($n$ 为数组容量)[^2]。 #### 1. **的基本概念操作** - **LIFO 原则**:最后入的元素最先出。 - **核心操作**: - `push(element)`:将元素添加到顶。 - `pop()`:移除并返回顶元素(如果非空)。 - `peek()`:返回顶元素但不移除。 - `isEmpty()`:检查是否为空。 - `isFull()`:检查是否已满(针对固定大小数组)。 - 数组实现的关键:使用一个整数索引 `top` 表示顶位置。初始时,`top = -1`(表示空)。 #### 2. **实现步骤** 以下是使用数组模拟的详细步骤: - **步骤 1: 定义数组顶指针** - 创建一个固定大小的数组 `stackArray` 存储元素。 - 初始化 `top = -1`(空状态)。 - **步骤 2: 实现操作 (push)** - 检查是否已满(`top == array.length - 1`)。如果满,报错或处理溢出。 - 否则,`top` 加 1,并将元素存入 `stackArray[top]`。 - 时间复杂度:$O(1)$。 - **步骤 3: 实现操作 (pop)** - 检查是否为空(`top == -1`)。如果空,报错或返回特定值(如 `null`)。 - 否则,获取 `stackArray[top]` 的值,然后 `top` 减 1。 - 时间复杂度:$O(1)$。 - **步骤 4: 实现其他辅助操作** - `peek()`:返回 `stackArray[top]`(不修改 `top`)。 - `isEmpty()`:检查 `top == -1`。 - `isFull()`:检查 `top == array.length - 1`。 - **注意事项**: - 数组大小固定:容量有限,需提前定义。如果需动态扩容,可添加逻辑(如复制到新数组),但会增加时间复杂度。 - 边界处理:始终检查空或满,避免数组越界错误。 - 性能:在理想情况下,所有操作均为 $O(1)$,但数组满时需处理溢出。 #### 3. **代码实现(Java 示例)** 以下是一个完整的 Java 实现,基于数组模拟。代码参考了经典实现[^2],并优化了可读性。 ```java public class ArrayStack { private int[] stackArray; // 存储元素的数组 private int top; // 顶指针 private int capacity; // 的最大容量 // 构造函数,初始化 public ArrayStack(int size) { capacity = size; stackArray = new int[capacity]; top = -1; // 初始时空 } // 入操作 public void push(int element) { if (isFull()) { System.out.println(&quot;已满,无法入&quot;); return; } stackArray[++top] = element; // top 先加1,再存入元素 } // 出操作 public int pop() { if (isEmpty()) { System.out.println(&quot;已空,无法出&quot;); return -1; // 返回错误码,实际中可抛异常 } return stackArray[top--]; // 返回元素,top 减1 } // 查看顶元素 public int peek() { if (isEmpty()) { System.out.println(&quot;为空!&quot;); return -1; } return stackArray[top]; } // 检查是否为空 public boolean isEmpty() { return top == -1; } // 检查是否已满 public boolean isFull() { return top == capacity - 1; } // 测试代码 public static void main(String[] args) { ArrayStack stack = new ArrayStack(3); // 创建容量为3的 stack.push(10); stack.push(20); stack.push(30); System.out.println(&quot;顶元素: &quot; + stack.peek()); // 输出 30 stack.pop(); // 移除30 System.out.println(&quot;出顶元素: &quot; + stack.peek()); // 输出 20 } } ``` **代码说明**: - **初始化**:`ArrayStack` 构造函数指定数组大小,`top` 初始为 -1。 - **push**:如果不满,`top` 递增后存储元素。 - **pop**:如果不空,返回 `stackArray[top]` 并递减 `top`。 - **测试**:在 `main` 方法中演示了入、出查看操作。 - **输出示例**: ``` 顶元素: 30 出顶元素: 20 ``` #### 4. **时间复杂度空间复杂度** - **时间复杂度**: - 入push(pop)在数组未满/非空时为 $O(1)$,因为只涉及索引操作。 - 最坏情况(如动态扩容)可能为 $O(n)$,但本实现固定大小。 - **空间复杂度**:$O(n)$,其中 $n$ 是数组容量,用于存储元素[^2]。 - **优化考虑**:如果需支持动态大小,可在 `push` 中检测满时创建新数组(例如,扩容为原大小两倍),但这会增加均摊时间复杂度。 #### 5. **注意事项** - **数组大小限制**:容量固定,需预估最大需求。实际应用中,可结合链表或动态数组解决。 - **错误处理**:代码中使用了简单输出,生产环境建议使用异常(如 `IllegalStateException`)。 - **通用性**:本示例使用 `int` 类型,可通过泛型(如 Java 的 `ArrayStack&lt;T&gt;`)支持任意数据类型。 - **溢出风险**:递归或深度操作时,空间可能耗尽,需监控 `top` 位置[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值