您的位置:首页 > 理论基础 > 数据结构算法

数据结构之栈

2016-04-14 00:16 501 查看

1、顺序存储结构与链式存储结构

顺序存储结构数据存在内存地址连续的一块区域,数组便是这种存储结构的典型表现。由于是在一块的内存区域,只要确定第一个元素的内存地址,后面的元素就可以在这快区域上随机存取。下面的示意图展示了顺序存储结构在内存中的存储形式:



链式存储结构

链式存储结构区别于顺序存储结构的一点就是数据是存放内存中任意一个地址中,每个数据除了有自己的数据属性还有下一个数据内存地址的引用,所以得到一个数据就可以知道下一个数据在哪个位置,显然这种存储方式的最有利的一点就是删除和插入比较有效率,这也是链式存储结构的应用的优势:

在表头插入结点:



在表头删除结点:



在表尾插入结点:



没有采取在表尾删除结点是因为,对于单链表,无法向前引用,若要将倒数第二结点变为last,则要从表头遍历一次,这样时间复杂度就为O(N)了,而上述操作时间复杂为为O(1),与链表长度无关。

2、使用数组和链表实现栈

栈,一种数据结构,满足“先进后出,后进先出”原则。

2.1数组实现

package dataStructure;

import java.util.Scanner;

/**
* 用数组实现的能够动态调整大小的下压栈
* @author 小锅巴
* @date 2016年4月13日下午3:54:19
* http://blog.csdn.net/xiaoguobaf */
public class ArrStack <T>{
private T[] a = (T[])new Object[1];//用于存储栈元素的泛型数组
private int N;//栈的大小,默认为0

public void push(T item){
if(N == a.length)
resize(2*a.length);
a[N++] = item;
}

public T pop() throws ArrayIndexOutOfBoundsException {
if(N == 0)
return null;
T item = a[--N];//将最后一个元素,即最后进栈的元素出栈,注意不是a
,它是null
a
= null;//避免对象游离
if(N > 0 && N == a.length/4)//调用resize
resize(a.length/2);
return item;
}

//用于调整数组大小
private void resize(int capacity){
T[] temp = (T[])new Object[capacity];
for(int i = 0 ; i < N ;i++)//将栈大小的数据复制到temp中,不能为a.length,因为没满的情况下有的是null
temp[i] = a[i];
a = temp;
}

private boolean isEmpty(){
return N == 0;
}

public static void main(String[] args) {
ArrStack<String> as = new ArrStack<>();
System.out.println("请输入待入栈的内容(q退出):");
Scanner s = new Scanner(System.in);
while(true){
String string = s.next();
if(string.equals("q"))
break;
as.push(string);
}

System.out.println("栈里面的内容是:");
while(!as.isEmpty())
System.out.print(as.pop()+",");
}
}
/**
* 输出:
请输入待入栈的内容(q退出):
a
sgfsdg
ghh
gh
qg
q
栈里面的内容是:
qg,gh,ghh,sgfsdg,a,
*/


eclipse中调试还是不是很好,还是cmd中调试方便,下图是我在Drjava中编译然后在cmd中调试:



2.2链表实现

package dataStructure;

import java.util.Scanner;

/**
* 链表实现的栈
* @author 小锅巴
* @date 2016年4月13日下午11:33:23
* http://blog.csdn.net/xiaoguobaf */
public class LinkedListStack <T>{
private Node first;//栈顶,指向链表头结点
private int N;//栈大小

private class Node{//私有内部类表示结点,隐藏对外实现,只能在其外围类LinkedListStack中使用
T item;
Node next;//由于java没有指针,使用引用
}

public void push(T item){//利用在表头插入结点操作
Node oldfirst = first;
first = new Node();
first.item = item;
first.next = oldfirst;
N++;
}

public T pop(){//利用在表头删除结点操作
T item = first.item;
if(N != 1)//栈中只有一个元素时,该结点的链接是null,不能访问
first = first.next;
N--;
return item;
}

public boolean isEmpty(){
return N == 0;
}

public static void main(String[] args) {
LinkedListStack<String> lls = new LinkedListStack<>();
System.out.println("请输入待入栈的内容(q结束):");
Scanner s = new Scanner(System.in);
while(true){
String string = s.next();
if(string.equals("q"))
break;
lls.push(string);
}

System.out.println("栈里面的内容是:");
while(!lls.isEmpty())
System.out.print(lls.pop()+",");
}
}
/**
* 输出;
请输入待入栈的内容(q结束):
a
bb
d
g
xiaoguo
q
栈里面的内容是:
xiaoguo,g,d,bb,a,
*/


对比两种实现,仅仅就push和pop来说,时间复杂度都是O(1),但是用数组实现,需要动态调整数组,其时间复杂度是O(N),故还是用链表实现好些。

参考资料:

1. 《Algorithms Fourth Edition》

2. http://blog.csdn.net/u011116672/article/details/51003189
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: