栈,队列
本节包含了一些基础的数据结构以及他们的实现
栈(后进先出)
我们定义栈具有以下方法:
- push()
- pop()
- isEmpty()
- size()
栈的链表实现:
保留指向链表头部(初始位置)的引用,并在该位置增加或删除。
屏幕快照 2018-07-31 下午5.12.23.png
public class LinkedStackOfStrings
{
private Node first = null;
private class Node
{
String item;
Node next;
}
public boolean isEmpty()
{ return first == null; }
public void push(String item)
{
Node oldfirst = first;
first = new Node();
first.item = item;
first.next = oldfirst;
}
public String pop()
{
String item = first.item;
first = first.next;
return item;
}
}
对上面算法进行复杂度分析;
算法复杂度:每次增加或删除消耗是恒量
空间复杂度:这个类所占空间约40N,这个是根据java中每个变量或对象大小来计算的。
栈的数组实现
该算法可分为两部分:
- 元素数量未达到数组长度时的正常的删减
- 当数组容量不够时对数组的处理,我们重点关注这一种情况
若是当数组容量达到上限时加一,那么算法复杂度会较高,因为java中数组长度是在初始化时定死的,长度+1意味着对原来数组的全量copy到新数组中。这样的算法复杂度为N2/2。
下面我们尝试在数组长度的增长方式上做一些改变:
当数组中元素数量达到上限时,一次性初始化一个两倍长度的新数组,将老数组中数据copy到新数组中
算法复杂度分析:2+4+8+...+N ~ 3N
屏幕快照 2018-07-31 下午6.01.10.png
在达到数组上限时,消耗突然升高。
那么如果pop()时,要如何处理?当元素数量为长度数组长度一半时减半数组长度? NO,这里有个陷阱,若是恰好在数组填满时,持续push(),pop()那么每次操作都会有最差的算法复杂度N。所以我们在当数组被填充1/4时,将数组长度减半。
上面实现的空间复杂度:
8N: 数组满时
32N:数组被填充1/4时
栈的两种实现对比:
链表:每次pop push消耗稳定常量,但需要额外的空间去处理链表结构
数组:每次pop push消耗可能有很大差别,不过空间复杂度较小
队列
队列的链表结构
我们需要两个引用分别指向队列的头尾,在尾部入队列,头部出队列。
public class LinkedQueueOfStrings
{
private Node first, last;
private class Node
{ /* same as in StackOfStrings */ }
public boolean isEmpty()
{ return first == null; }
public void enqueue(String item)
{
Node oldlast = last;
last = new Node();
last.item = item;
last.next = null;
if (isEmpty()) first = last;
else oldlast.next = last;
}
public String dequeue()
{
String item = first.item;
first = first.next;
if (isEmpty()) last = null;
return item;
}
}
本节原文其余部分,讲述了java中的泛型与迭代器,这里不记录。
网友评论