Java数据结构和算法(一)--栈

栈:

  英文名stack,特点是只允许访问最后插入的那个元素,也就是LIFO(后进先出)

jdk中的stack源码:

public
class Stack<E> extends Vector<E> {  //继承Vector,Vector和ArrayList几乎相同,都是通过数组保存数据,只不过方法有Synchronized修饰 public Stack() {
} public E push(E item) {  //push,也就是add,把数据保存得add到数组的末尾
addElement(item);  
return item;
} public synchronized E pop() {  //pop,也就是removeLast(),并且返回末尾值
E obj;
int len = size();  //得到数组长度
obj = peek();  //得到数组末尾值
removeElementAt(len - 1);  //删除末尾元素 return obj;
} public synchronized E peek() {  //peek,返回末尾元素,只是查询,不会删除
int len = size(); if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);  
} public boolean empty() {  //empty,是否为空
return size() == 0;
} public synchronized int search(Object o) {
int i = lastIndexOf(o);
if (i >= 0) {
return size() - i;
}
return -1;
} }

从上面源码看到:

  JDK源码中的stack通过集成Vector实现,通过数组保存数据,一般使用push()、pop()、peek()来实现stack基本功能LIFO

  stack有栈顶、栈底的概念,栈底是封闭的,数据只能从栈顶进出

Java数据结构和算法(一)--栈

自定义实现stack:

public class MyStack<E> {  
private int maxDepth;  //栈深度
private Object[] elementData;  //保存数据的数组
private int top;  //栈顶的位置,空栈为-1 public MyStack() {
this.maxDepth = 10;
this.elementData = new Object[10];  
this.top = -1;
} public MyStack(int initialCapacity) {
this.maxDepth = initialCapacity;
this.elementData = new Object[initialCapacity];
this.top = -1;
} public E push(E e) {
ensureExplicitCapacity(top +1);
elementData[++top] = e;
return e;
} public E pop(){
E e = peek();
elementData[top] = null;
this.top--;
return e;
} public E peek() {
return (E)elementData[top];
} private void ensureExplicitCapacity(int minCapacity) {
if (minCapacity - elementData.length > 0)
grow(minCapacity);
} private void grow(int minCapacity) {  //扩容
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - Integer.MAX_VALUE > 0)
newCapacity = Integer.MAX_VALUE;
this.maxDepth = newCapacity;
elementData = Arrays.copyOf(elementData, maxDepth);
} public int size() {
return top + 1;
}
  public boolean isEmpty() {
    return top == -1;
  }
}
public static void main(String[] args) {
MyStack<Integer> stack = new MyStack<Integer>();
stack.push(1);
stack.push(2);
stack.push(3);
int size = stack.size();
for (int i = 0; i < size; i++) {
System.out.print(stack.peek() + " ");
}
System.out.println("");
for (int i = 0; i < size; i++) {
System.out.print(stack.pop() + " ");
}
}
输出结果:
3 3 3
3 2 1

这是使用ArrayList的思路实现Stack:

1、通过Object[]保存数据

2、扩展支持泛型

3、初始容量设置为10,扩容之后为原来容量的1.5倍

4、实现了push()、pop()、peek()和grow()

5、stack不适合使用for、foreach、Iterator,所以这里没实现Iterable接口,因为peek()永远只是栈顶的值,或者用pop()

PS:for循环的size,如果for循环使用pop(),不要写成:

for (int i = 0; i < stack.size(); i++) {
System.out.print(stack.pop() + " ");
}

因为弹出元素,size会变化,无法弹出所有数据,可以使用测试代码中那样,或者while循环:

while (!stack.isEmpty()) {
System.out.println(stack.pop());;
}

到此为止一个stack的功能已经实现,从输出结果看使用时没问题的

实践1:实现输入字符串,逆序输出

public static void main(String[] args) {
MyStack<Character> stack = new MyStack<Character>();
char[] chars = getInput().toCharArray();
for (char c : chars) {
stack.push(c);
}
while (!stack.isEmpty()) {
System.out.print(stack.pop() + " ");;
}
} public static String getInput() {
String s = null;
try {
InputStreamReader reader = new InputStreamReader(System.in);
BufferedReader bufferedReader = new BufferedReader(reader);
s = bufferedReader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return s;
}

输出结果:

Java数据结构和算法(一)--栈

实践2:分隔符匹配

  栈通常用于解析xml标签或者HTML标签,例如'{'要有个'}'进行匹配,通过把读取字符,发现左分隔符push,发现右分隔符pop,然后对比是否

匹配,不匹配就报错,如果栈中没有左分隔符和右分隔符匹配,或者有左分隔符一直没有被匹配,这些情况,也会报错。

例如:a{12(u<d>1)f3}

Java数据结构和算法(一)--栈

代码实现:

public static boolean isValid(String s) {
MyStack<Character> stack = new MyStack<>();
char[] chars = s.toCharArray();
for (char aChar : chars) {
switch (aChar) {
case '{':
stack.push('}');
continue;
case '<':
stack.push('>');
continue;
case '(':
stack.push(')');
continue;
default:
if (stack.isEmpty() || stack.pop() != aChar) {
return false;
} }
}
return stack.isEmpty();
}

总结:

stack是概念上的工具,我们已经实现了两个功能,能够实现什么功能看个人实现,甚至Ctrl+Z撤销的实现就是用stack实现的

stack入栈和出栈的时间复杂度都是O(1),栈的操作时间和栈中数据的个数没有关系,不需要移动和比较,只不过我们这里实现了扩容,如果

发生扩容,会稍微影响一点

内容参考:

<Java数据结构和算法>

Java数据结构和算法(四)——栈

上一篇:ASP.NET 截获服务器生成的将要发送到客户端的html的方法


下一篇:linux查看本机IP、gateway、DNS