`
LQJ2711
  • 浏览: 5426 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

如何理解栈?

 
阅读更多

我们在学习的阶段时,对一些数据结构的概念、用法,比如:栈。总是不那么熟悉,相信大部分初学者都感同身受,所以在此,我向大家分享一下自己如何将栈的概念、用法融汇贯通的。

对于数据结构中栈的学习,我认为可以分三个阶段:

1.字面理解阶段:

栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)。
  (1)通常称插入、删除的这一端为栈顶(Top),另一端[先放入的数据一端]称为栈底(Bottom)。
  (2)当表中没有元素时称为空栈
  (3)栈为后进先出(Last In First Out)的线性表,简称为LIFO表
     栈的修改是按后进先出的原则进行。每次删除(退栈)的总是当前栈中"最新"的元素,即最后插入(进栈)的元素,而最先插入的是被放在栈的底部,要到最后才能删除。
图例:


 

 

根据栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除。
也就是说,栈是一种后进先出(Last In First Out)的线性表,简称为LIFO表。由此可见,栈对于插入、修改等操作不擅长。所以在此我就不对该操作进行基础实现。

2、画图理解阶段与3、具体基础实现

栈的基础操作具体包括了增加、删除、插入、修改等。

在这里我只对增加、删除、判断是否为空、栈的长度进行基础实现[包括了数组与链表实现]。

注意:该实现过程使用了泛型[温馨提示:泛型的概念与使用,我已经在之前的可变数组的基础实现中说过]

           操作的步骤不是一成不变的,但又有一定的顺序,我只是写出了自己喜欢的顺序,但是你们可以自己拓展一下,看哪些步骤可交换,哪些不能交换。

1)实现栈的第一步是确定储存的数据的容器

     a、基于数组                

//定义一个储存数据的数组,初始化长度为0
Object[] src= new Object[0];

    b、基于链表

        不同于数组,栈的链表实现是基于链表的基础上的,所以必须定义一个结点类保存数据

/**
 * 链式结构内部结点类,单链表
 * 
 * @author Administrator
 *
 */
class Node<E> {
	// 内容,值
	E data;
	// 对下一个结点的引用
	Node<E> netx = null;
	//空的构造函数,用于构造空值的结点
	public Node() {
	}
	//构造有值的结点
	public Node(E e) {
		data = e;
	}
}


	//栈的属性:
	// 定义一个储存数据的链表
	private Node<E> head = null;
	//定义一个变量保存栈的长度
	private int num=0;

2)增加操作

     a、基于数组 

        图例: 

如上图解析:将src数组的数据复制到dest临时数组中相应位置,再将新的数据放到dest数组中去,最后将dest数组赋予src数组,就还原了数组。
 

         /**
	 * 将数据压入栈中
	 * @param e 压入的数据
	 */
	public void put(E e){
		//定义一个新的空数组dest,数组长度比原数组大1
		Object[] dest= new Object[src.length+1];
		//将src数组的数据复制到dest数组里,位置不变,长度不变
		System.arraycopy(src, 0, dest, 0, src.length);
		//将增加数据放入dest数组尾部
		dest[dest.length-1]=e;
		//将dest数组赋予src数组,数组还原
		src=dest;
	}

     b、基于链表
 图例:

如上图解析:先将新结点node的next指向head

                  再将head指向node结点,然后将栈的长度+1 

         /**
	 * 将数据压入栈中
	 * 
	 * @param e
	 *            压入的数据
	 */
	public void put(E e) {
		//建立一个新结点node,值为e,默认无指向
		Node<E> node = new Node<E>(e);
		//如果头结点为空,就将node结点置为头结点head
		if (head == null) {
			//将node结点置为头结点head
			head = node;
		} else {//否则头结点head就后移
			//将node结点与头结点连接
			node.netx = head;
			//头结点head后移
			head = node;
		}
		//栈长度+1
		num++;
	}

3)删除操作

     a、基于数组 

图例:

如上图解析:将src数组的数据复制到dest临时数组中相应位置【不包括src数组的最后一位】,再将新的数据放到变量e中去,再将dest数组赋予src数组,就还原了数组,最后返回输出e。
 

	/**
	 * 弹出栈顶数据
	 * @return 弹出的数据
	 */
	public E poll(){
		//定义一个新的空数组dest,数组长度比原数组小1
		Object[] dest= new Object[src.length-1];
		//将src数组的数据复制到dest数组里,位置不变,长度是dest的长度
		System.arraycopy(src, 0, dest, 0, dest.length);
		//将栈弹出的栈尾数据放入变量e中
		E e = (E)src[src.length-1];
		//将dest数组赋予src数组,数组还原
		src=dest;
		//返回弹出数据
		return e;
	}

     b、基于链表

图例:

如上图解析:先将新结点node的next指向head

                  再将head指向node结点的next指向的结点

                  将node结点的next指向null,然后将栈的长度-1 ,最后返回输出node.data
 

	/**
	 * 弹出栈顶数据
	 * 
	 * @return 弹出的数据
	 */
	public E poll() {
		//如果栈内有数据,就弹出,否则返回null
		if(head!=null){
			//建立一个新结点node,默认无指向
			Node<E> node = new Node<E>();
			//将node指向头结点
			node=head;
			//头结点head的指向移到下一个结点
			head=node.netx;
			//头结点的指向关系为null
			node.netx=null;
			//栈长度-2
			num--;
			//返回弹出的值
			return node.data;
		}	
		//返回null
		return null;
	}

 

4)判断栈是否为空操作

 

	/**
	 * 判断栈是否为空,基于链表实现[基于数组实现:将num改成src.length]
	 * 
	 * @return true/false
	 */
	public boolean isEmpty() {
		//如果栈长度是0就返回false
		if (num != 0) {
			return false;
		} else {
			//否则就返回true
			return true;
		}
	}

 

5)栈的长度

	/**
	 * 获得栈的长度,基于链表实现[基于数组实现:将num改成src.length]
	 * 
	 * @return 栈的长度
	 */
	public int size() {
		//返回栈长度
		return num;
	}

 
 

  • 大小: 11.8 KB
  • 大小: 8 KB
  • 大小: 7.8 KB
  • 大小: 13.8 KB
  • 大小: 16.6 KB
分享到:
评论

相关推荐

    Native下如何获取调用栈?.pdf

    总结来说,获取Native调用栈是通过理解函数调用过程、栈帧的建立与恢复,以及利用unwind库和相关工具来实现的。熟悉这一过程不仅有助于调试和修复崩溃,还能提升开发者对系统底层运作的理解,从而提高开发技能。

    Linux 中的各种栈:进程栈 线程栈 内核栈 中断栈

    Linux操作系统中的栈是一种重要的数据结构,尤其在内存管理、函数调用以及多任务处理中扮演着至关重要的角色。在Linux系统中,栈可以分为...正确理解栈的工作原理和使用方式对于软件开发人员和系统管理员都至关重要。

    08-调用栈:为什么JavaScript代码会出现栈溢出?_For_vip_user_0011

    "JavaScript调用栈:为什么JavaScript代码会出现栈溢出?" 在 JavaScript 代码中,调用栈是指 ...因此,了解调用栈的工作原理可以帮助我们更好地理解 JavaScript 代码的执行过程,避免栈溢出的错误和搞定面试。

    muluoleiguo#interview#理解有栈无栈协程实现1

    理解有栈无栈协程可能会有错误, 只是自己简单的理解之前一直没想明白了一个问题, 就是关于协程如何进行上下文切换.众所周知, 协程是分为有栈协程和无栈协程俩种.

    04深入理解栈:从CPU和函数的视角看栈的管理1

    栈是计算机内存管理中的一个重要概念,特别是...通过深入理解栈的工作原理,我们可以更好地诊断和修复与栈相关的错误,提高代码的稳定性和安全性。在编程实践中,时刻关注栈的使用和管理,是每个专业程序员必备的技能。

    Java虚拟机(JVM)面试题(总结最全面的面试题!!!)

    (重点理解)详细介绍下Java虚拟机栈?(重点理解)一个方法调用另一个方法,会创建很多栈帧吗?栈指向堆是什么意思?递归的调用自己会创建很多栈帧吗?你能给我详细的介绍Java堆吗?(重点理解)能不能解释一下本地...

    列车调度程序 利用栈

    1. **栈的定义与操作**:理解栈的基本性质,如入栈(push)、出栈(pop)、查看栈顶元素(peek)等操作。 2. **数据结构实现**:如何使用数组或链表等数据结构来实现栈。 3. **列车调度算法**:设计一种基于栈的...

    顺序栈,压栈、弹栈、获得栈顶元素、统计栈中元素个数、打印栈中元素

    在给定的压缩包文件“顺序栈利用连续存储单元实现栈结构”中,可能包含了实现上述功能的C语言源代码文件,读者可以通过阅读和运行这些代码来加深对顺序栈的理解和掌握。通过实际操作和调试,能够更好地领悟到顺序栈...

    两个栈空间共享,栈满打印

    描述中提到的实验目的是让学生掌握栈的两种实现方式,特别是栈空和栈满的判断条件,以及理解栈空间共享的概念及其实现方法。实验内容是设计一个程序,通过数组创建两个栈,输入一序列整数,将奇数存入第一个栈,偶数...

    建立栈和栈的逆置 栈操作

    首先,我们要理解如何建立一个栈。在C++编程语言中,可以使用STL(Standard Template Library)中的`&lt;stack&gt;`库来创建栈。以下是一个简单的示例: ```cpp #include #include int main() { std::stack&lt;int&gt; ...

    数据结构栈代码

    对于初学者来说,通过查看和理解这样的实现,可以深入理解栈的工作原理以及C++模板的运用。 在文件名"stack.cpp"中,".cpp"是C++源代码文件的扩展名,意味着这里包含了实现栈的源代码。通常,一个C++类定义会包含...

    C#中堆和栈的区别分析

    二、什么元素被分配到栈?什么被分配到堆? 在 C# 中,有四种主要类型被分配到栈和堆:值类型、引用类型、指针和指令。 * 值类型:继承自 System.ValueType 的类型,如 bool、byte、char、decimal 等。 * 引用类型...

    C++堆、栈及静态数据区详解

    本文将深入探讨C++中的堆、栈以及静态数据区,帮助理解这些内存区域的区别和使用场景。 1. 栈(Stack): 栈是编译器自动管理的内存区域,主要用来存放函数参数、局部变量等。每当进入一个函数调用,栈就会为函数的...

    内存中堆和栈的分配区别

    本文将深入探讨这两种内存区域的分配区别,以及它们在程序中的作用机制,帮助读者理解C/C++编程语言中内存管理的基本原理。 ### 堆和栈的定义 #### 栈(Stack) 栈是一种遵循先进后出(LIFO,Last In First Out)...

    栈:顺序栈和链表栈_C语言项目

    通过编写和测试这些功能,你可以更好地理解栈的工作原理及其在实际应用中的作用。 总结来说,顺序栈和链表栈是两种实现栈数据结构的方法,各有优缺点。顺序栈的优点在于访问速度快,但受限于预先分配的数组大小;...

    栈的基本操作与原理以及内容

    ### 栈的基本操作与原理详解 #### 栈的定义与特性 栈是一种特殊的线性数据结构,其特点是只允许在...通过实际操作,如实验报告中所描述的顺序表和链表操作,学习者可以更好地理解栈的内部机制及其在实际编程中的应用。

    栈和队列的基本操作

    这些问题可以帮助我们更好地理解栈和队列的基本操作。 在实验小结中,我们需要总结本次实验的重难点及心得、体会、收获。我们可以通过实验,掌握了栈和队列的知识,又学会了一些基本应用实例,例如括号匹配、回文...

    数据结构两栈共享空间C++顺序栈

    在计算机科学中,数据结构是组织和存储数据的方式,它直接影响到算法的效率。...通过这个实例,学习者不仅可以深入理解栈数据结构,还能进一步掌握C++的类设计、模板机制以及在内存管理上的优化策略。

    栈运算_栈运算_perhapswcl_

    栈是一种特殊的线性数据结构,它的特点是“后进先出”(Last ...学习这些内容,可以帮助你理解如何在实际编程中使用栈进行各种运算和数据处理。熟悉栈的原理和操作,将有助于解决复杂问题,提高程序设计的效率和质量。

    栈实现计算器(C语言实现)

    首先,我们需要理解栈的基本操作:入栈(push)、出栈(pop)、查看栈顶元素(peek)以及检查栈是否为空(isEmpty)。在我们的计算器中,主要会用到两个栈,一个是数字栈,用于存储待运算的数值;另一个是运算符栈,...

Global site tag (gtag.js) - Google Analytics