栈的定义:(特殊的线性表)
??仅在表的一端进行插入和删除的线性表。允许插入、删除的这一端称为栈顶,另一端称为栈底。表中没有元素时称为空栈。
??被称为后进先出的线性表(Last In First Out),简称 LIFO表,或被称为先进后出的线性表(First In Last Out),简称 FILO表。
??栈更具存储方式的不同分为两种:顺序栈和链栈。
顺序栈:
下面借图(来自http://www.nowamagic.net/librarys/veda/detail/2271)演示一下:
对于顺序栈,数据元素的进栈操作解释如下:
出栈的操作则相反,如下:
下面是我的Java代码实现:
package com.phn.stack;
/**
* @author 潘海南
* @Email 1016593477@qq.com
* @TODO 顺序栈
* @date 2015年7月20日
*/
public class FOArrayStack<E> {
//初始化默认栈的存储容量
private static final int DEFUALT_CAPACITY = 100;
//栈中存储数据元素的数组
private Object[] data = null;
//栈的实际大小
private int size;
//栈的栈顶指针
private int top;
//栈的实际容量
private int capacity;
/**
* @TODO 无参构造函数,初始化栈
*/
public FOArrayStack(){
this(DEFUALT_CAPACITY);
}
/**
* @TODO 带参构造函数,初始化栈
* @param initialCapacity 初始化栈的容量
*/
public FOArrayStack(int initialCapacity) {
this.capacity = initialCapacity;
this.data = new Object[initialCapacity];
this.size = 0;
this.top = this.size-1;
}
/**
* @TODO 压入数据元素到栈中
* @param e 数据元素
* @return true
*/
public boolean push(E e){
this.validateCapacity();
this.top++;
this.data[top]=e;
this.size++;
return true;
}
/**
* @TODO 验证栈的实际大小是否已经到达栈实际容量的极限
*/
private void validateCapacity() {
if(this.top==this.capacity-1){
throw new RuntimeException("此栈已满!最大容量="+this.capacity);
}
}
/**
* @TODO 获取栈顶元素,并没有将其弹出栈
* @return e 数据元素
*/
public E peek(){
if(this.isEmpty()){
throw new RuntimeException("此栈为空栈!");
}else{
Object e = new Object();
e = this.data[this.top];
return (E)e;
}
}
/**
* @TODO 获取栈顶元素并弹出栈
* @return e 数据元素
*/
public E pop(){
E e = this.peek();
this.top--;
this.size--;
return e;
}
/**
* @TODO 清空栈
* @return true
*/
public boolean clear(){
while(this.top>=0){
this.data[this.top]=null;
this.top--;
this.size--;
}
return true;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer("[");
if(this.top!=-1){
sb.append(this.data[this.top]);
int temp = this.top-1;
while(temp>=0){
sb.append(", "+this.data[temp]);
temp--;
}
}
sb.append("]");
return sb.toString();
}
/**
* @TODO 判断栈是否为空
* @return true空 or false不为空
*/
public boolean isEmpty(){
//或者用长度表示
if(this.top==-1){
return true;
}
return false;
}
/**
* @TODO 栈的实际大小
* @return
*/
public int size(){
return this.size;
}
}
我的测试代码:
package com.phn.stack;
/**
* @author 潘海南
* @Email 1016593477@qq.com
* @TODO 顺序栈测试
* @date 2015年7月20日
*/
public class FOArrayStackTest {
public static void main(String[] args) {
FOArrayStack<String> foas = new FOArrayStack<String>(6);
foas.push("元素1");
foas.push("元素2");
foas.push("元素3");
foas.push("元素4");
foas.push("元素5");
System.out.println(foas);
foas.pop();
System.out.println(foas);
String s = foas.peek();
System.out.println(s);
System.out.println(foas);
foas.clear();
System.out.println(foas);
System.out.println(foas.isEmpty());
}
}
测试结果截图:
下面扩展一下顺序栈,两栈共享空间:
??顺序栈具有单向延伸的特性,在一个程序中如果同时使用了具有相同数据类型的两个栈时可考虑使用一个数组来存储这两个栈,其中栈1的栈底设在该数组的始端,栈2的栈底设在该数组的尾端,两个栈都从各自的端点向数组中部延伸,只有在两个栈的栈顶在数组空间的某一位置相遇时才会产生“上溢”。栈1在入栈操作时栈顶指针top1++,出栈操作时top1–;栈2在入栈操作时栈顶指针top2–,出栈操作时top2++。
链栈:
对于链栈,插入(压栈)操作push解释如下:
删除(弹出)操作pop解释如下:
下面是我的Java实现代码:
package com.phn.stack;
/**
* @author 潘海南
* @Email 1016593477@qq.com
* @TODO 链栈
* @date 2015年7月20日
*/
public class FOLinkedStack<E> {
//栈顶指针
private FOLinkedNode<E> topNode = null;
//栈的长度
private int size;
/**
* @TODO 无参构造函数,初始化链栈
*/
public FOLinkedStack() {
this.size = 0;
}
/**
* @TODO 获取栈的长度
* @return
*/
public int size() {
return this.size;
}
/**
* @TODO 压入数据元素到栈中
* @param e 要压入的数据元素
* @return true
*/
public boolean push(E e) {
FOLinkedNode<E> temp = new FOLinkedNode<E>();
temp.setE(e);
temp.addNext(this.topNode);
this.topNode = temp;
this.size++;
return true;
}
/**
* @TODO 获取栈顶元素,并没有弹出,还存在栈中
* @return e 获取到的数据元素
*/
public E peek(){
if(this.isEmpty()){
throw new RuntimeException("链栈为空!");
}else{
E e = this.topNode.getE();
return e;
}
}
/**
* @TODO 弹出栈顶数据元素,不在栈中了
* @return e 弹出的数据元素
*/
public E pop(){
E e = this.peek();
this.topNode = this.topNode.next;
this.size--;
return e;
}
/**
* @TODO 栈是否为空
* @return true空 or false不为空
*/
public boolean isEmpty(){
if(this.topNode==null){
return true;
}
return false;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer("[");
if(this.topNode!=null){
sb.append(this.topNode.getE());
FOLinkedNode<E> temp =new FOLinkedNode<E>();
temp = this.topNode.next;
while(temp!=null){
sb.append(","+temp.getE());
temp = temp.next;
}
}
sb.append("]");
return sb.toString();
}
}
链栈节点类:
package com.phn.stack;
public class FOLinkedNode<E> {
private E e;// 结点中存放的数据
FOLinkedNode() {
}
FOLinkedNode(E e) {
this.e = e;
}
FOLinkedNode<E> next;// 用来指向该结点的下一个结点
// 设置下一节点的值
void addNext(FOLinkedNode<E> node) {
next = node;
}
public E getE() {
return e;
}
public void setE(E e) {
this.e = e;
}
@Override
public String toString() {
return e.toString();
}
}
下面是我的测试代码:
public static void main(String[] args) {
FOLinkedStack<String> fols = new FOLinkedStack<String>();
System.out.println(fols.isEmpty());
fols.push("元素1");
System.out.println(fols);
System.out.println(fols.size());
System.out.println(fols.peek());
System.out.println(fols);
System.out.println(fols.pop());
System.out.println(fols.isEmpty());
System.out.println(fols);
System.out.println(fols.size());
fols.push("元素4");
fols.push("元素2");
fols.push("元素5");
fols.push("元素3");
fols.push("元素6");
System.out.println(fols);
System.out.println(fols.size());
System.out.println(fols.isEmpty());
}
测试截图:
顺序栈和链栈的比较:
应用建议:
??如果栈的使用过程中元素变化不可预料,有时很小,有时非常大,那么最好是用链栈,反之,如果它的变化在可控范围内,建议使用顺序栈会更好一些。
使用栈的原因:栈的引入简化了程序设计的问题,划分了不同关注层次,使得思考范围缩小,更加聚焦于我们要解决的问题核心。
版权声明:本文为博主原创文章,如需转载请注明出处并附上链接,谢谢。
原文地址:http://blog.csdn.net/yannanying/article/details/46981109