最近刷数据结构的黑宝书,顺便将数据结构进行下总结,就先从最熟悉的栈开始吧。
栈(stack),数据结构中的一种,准确的来说,应该是数据逻辑结构。
栈的基本特点为: 后进先出,就像生活中我们取放盘子,当一摞盘子在桌子上时候,我们总是会取最上边的盘子,同时,我们在放盘子时,也会放在最上面。同样这种机制也在我们浏览网页时也有出现,在我们浏览网页时,左上角的转到上一页就是这种,最近浏览的网站在最上层,当返回时,先取到最上层的地址,这样即实现了逐层后退。
栈有几个基本的操作
- push 向栈底添加一个元素
- pop 从栈顶取出一个元素
- peek 复制栈顶元素,但不进行删除
- size 判定栈中所含元素的个数
- isEmpty 判断栈是否为空
因为对树结构还不够熟悉,我们就用简单的数组和链表来实现一个栈。
数组实现
利用数组实现栈,我们可以设置自定义容量和默认容量,也可以设置自动扩容机制。数组实现机制相对来说更加简单,考虑到栈的操作总是取最近添加的元素,所以,我们以头部作为栈底,尾部作为栈顶,这样向栈内添加元素时,时间复杂度为常数,对于经常对栈顶进行操作,并且栈内数据非常庞大的情况下,效率相对较高。
下面利用Java代码进行方法实现
package Stack;
public class ArrayStack {
/*
* 该类为使用数组实现的一个栈(Stack)
* 具有成员方法pop,push,peek,isEmpty,size,getMaxElementNumber
* 具有构造方法两种,默认容量和指定容量
* 数组应具有自动扩容能力(可选)
*/
//该值为栈索引,各项方法均对其进行操作(固定长度数组)
private int STACK_ARRAY_INDEX_OF_HANDMOVEMENT = 0;
//该值为栈最大容量数
public int TOP;
private Object[] StackArray;
public ArrayStack() {
//默认栈容量为15
StackArray = new Object[15];
}
public ArrayStack(int TOP) {
//指定栈容量
StackArray = new Object[TOP];
//返回TOP值,用于后续判断操作
this.TOP = TOP;
}
/*
* 栈推入操作 具有判满功能
*/
public boolean push(Object element) {
//判断栈顶是否满元素,若满,进行扩容,若空进行添加
if(StackArray[StackArray.length-1] != null) {
/*
* 准备新数组,将原数组数据全部复制进去
* 将原数组删除并扩容 扩容指数为2倍
* 将新数组数据复制进原数组
*/
Object[] StackArrayNew = new Object[StackArray.length];
for(int i = 0;i<=StackArrayNew.length;i++) {
StackArrayNew[i] = StackArray[i];
}
StackArray = new Object[StackArrayNew.length * 2];
for(int i = 0;i<=StackArrayNew.length;i++) {
StackArray[i] = StackArrayNew[i];
}
StackArray[STACK_ARRAY_INDEX_OF_HANDMOVEMENT] = element;
STACK_ARRAY_INDEX_OF_HANDMOVEMENT++;
return true;
}else {
StackArray[STACK_ARRAY_INDEX_OF_HANDMOVEMENT] = element;
STACK_ARRAY_INDEX_OF_HANDMOVEMENT++;
return true;
}
}
/*
* 栈取出操作,具有判空功能
*/
public Object pop() {
//判断栈中是否有元素,若有返回Object实例,若无返回提示
if(StackArray[0] == null) {
return null;
}else {
Object theReturnResult =
StackArray[STACK_ARRAY_INDEX_OF_HANDMOVEMENT];
StackArray[STACK_ARRAY_INDEX_OF_HANDMOVEMENT] = null;
return theReturnResult;
}
}
/*
* 判断栈中元素个数方法
*/
public int size() {
int index = 0;
while(StackArray[index] == null) {
index++;
}
return index;
}
/*
* 判断是否为空
*/
public boolean isEmpty() {
if(StackArray[0] == null)
return true;
return false;
}
/*
* peek复制栈顶元素
*/
public Object peek() {
//判断栈中是否有元素,若有返回Object实例,若无返回提示
if(StackArray[0] == null) {
System.out.println("该栈为空");
return null;
}else {
Object theReturnResult = StackArray[STACK_ARRAY_INDEX_OF_HANDMOVEMENT];
return theReturnResult;
}
}
}
Ps:该栈内实现了基本的五个操作,并且对基本的操作进行了判空功能。
链表实现
在这里就先不进行链表的介绍,等有时间再开一篇链表的总结。
为了实现起来更加方便,利用单链表即可满足栈的需求。对于链表来说,增删改的操作时间为常数,而因为其引用的特性,遍历的花费十分昂贵,所以我们为了优化栈的性能,尽可能减少遍历链表所浪费的时间,我们将链表表头作为栈顶,这样每次存取操作只需要用头引用取表头数据即可。
首先我们需要在链表内维护一个节点类。
package MyLinkedList;
/*
* 该类为单链表类的节点类,该类只服务于LinkedList类,并不进行对外暴露
*/
public class SingleLinkedListNode {
//该引用为链表中的数据部分,用于存放数据
public Object data;
//该引用为指向下一个链表,最后一个可循环或者置空
public SingleLinkedListNode Next;
public SingleLinkedListNode(Object data,SingleLinkedListNode Next) {
this.data = data;
this.Next = Next;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public SingleLinkedListNode getNext() {
return Next;
}
public void setNext(SingleLinkedListNode next) {
Next = next;
}
}
下面用链表实现
package MyLinkedList;
/*
* 该类为单链表类的节点类,该类只服务于LinkedList类,并不进行对外暴露
*/
public class SingleLinkedListNode {
//该引用为链表中的数据部分,用于存放数据
public Object data;
//该引用为指向下一个链表,最后一个可循环或者置空
public SingleLinkedListNode Next;
public SingleLinkedListNode(Object data,SingleLinkedListNode Next) {
this.data = data;
this.Next = Next;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public SingleLinkedListNode getNext() {
return Next;
}
public void setNext(SingleLinkedListNode next) {
Next = next;
}
}
只实现了几个基本操作,对于size,isEmpty等方法,和数组实现原理相同。
最后再简单介绍一种较为特殊的栈,该栈为栈中的一个特殊形式,主要用于取消操作,使用数组进行实现,该栈不能进行自动扩容,并且栈满后将从栈低进行元素删除。
其原理和以上两种栈无差别,只不过在栈满后,先将栈底元素删除,再将栈元素向前移动,最后在栈顶添加元素。
package Stack;
public class OverFlowStack {
/*
* 该栈为栈中的一个特殊形式,主要用于取消操作,使用数组进行实现
* 该栈不能进行自动扩容,并且栈满后将从栈低进行元素删除
* 该栈具有方法为pop,push,peek,size,isEmpty
*/
// 该值为栈索引,各项方法均对其进行操作(固定长度数组)
private int STACK_ARRAY_INDEX_OF_HANDMOVEMENT = 0;
public int TOP;
private Object[] OverFlowStack;
public OverFlowStack() {
// 默认栈容量为15
OverFlowStack = new Object[15];
}
public OverFlowStack(int TOP) {
// 指定栈容量
OverFlowStack = new Object[TOP];
// 返回TOP值,用于后续判断操作
this.TOP = TOP;
}
public boolean push(Object element) {
// 首先对栈进行数据判断,如果栈内还有空间,直接添加
// 如果栈内已经满数据,从栈底删除最后一个数据并进行数组索引的移动
if (OverFlowStack[OverFlowStack.length - 1] == null) {
OverFlowStack[STACK_ARRAY_INDEX_OF_HANDMOVEMENT] = element;
return true;
} else {
// 通过while进行对栈内数据移动的操作
// 移动次数为INDEX-1
int temp = 0;
while (temp <= OverFlowStack.length - 1) {
OverFlowStack[temp] = OverFlowStack[temp + 1];
temp++;
}
return true;
}
}
/*
* 栈取出操作,具有判空功能
*/
public Object pop() {
//判断栈中是否有元素,若有返回Object实例,若无返回提示
if(OverFlowStack[0] == null) {
return null;
}else {
Object theReturnResult = OverFlowStack[STACK_ARRAY_INDEX_OF_HANDMOVEMENT];
OverFlowStack[STACK_ARRAY_INDEX_OF_HANDMOVEMENT] = null;
return theReturnResult;
}
}
/*
* 判断栈中元素个数方法
*/
public int size() {
int index = 0;
while(OverFlowStack[index] == null) {
index++;
}
return index;
}
/*
* 判断是否为空
*/
public boolean isEmpty() {
if(OverFlowStack[0] == null)
return true;
return false;
}
/*
* peek复制栈顶元素
*/
public Object peek() {
//判断栈中是否有元素,若有返回Object实例,若无返回提示
if(OverFlowStack[0] == null) {
System.out.println("该栈为空");
return null;
}else {
Object theReturnResult = OverFlowStack[STACK_ARRAY_INDEX_OF_HANDMOVEMENT];
return theReturnResult;
}
}
}
基本内容就总结到这里,如果有错误的地方请留言指出,萌新发文,请大神轻喷。
本文原创,转载请注明来源。
QQ 489282384
JoryB