普林斯顿算法课Part 1 Week 2 Stacks and Queues

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Cyril__Li/article/details/79760470

Stacks and queues:
用来保存collections of objects,支持插入,移除,遍历操作,区别是stack是last in first out,queue是last in last out。
这里写图片描述

1. Stacks

1.1 Stack的API

public class StackOfStrings
StackOfStrings() //create an empty stack
void push(String item) //insert a new string onto stack
String pop() //remove and return the string most recently added
boolean isEmpty() //is the stack empty?
int size() //number of strings on the stack

客户端:

public static void main(String[] args)
{
    StackOfStrings stack = new StackOfStrings();
    while (!StdIn.isEmpty())
    {
        String s = StdIn.readString();
        if (s.equals("-")) StdOut.print(stack.pop());
        else stack.push(s);
    }
}

1.2 Linked list实现Stack

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;
    }
}

1.3 Linked list实现Stack的复杂度

每个操作都是constant time
内存占用:object overhead,inner class overhead,string,node,一共16+8+8+8=40 bytes

1.4 Array实现Stack

public class FixedCapacityStackOfStrings
{
    private String[] s;
    private int N = 0;
    public FixedCapacityStackOfStrings(int capacity)
    { s = new String[capacity]; }
    public boolean isEmpty()
    { return N == 0; }
    public void push(String item)
    { s[N++] = item; }  //先将s[N]赋值为item,再将N增加1指向下一个entry
    public String pop()
    { return s[--N]; }  //先将N减一指向最近保存的entry
}

1.5 上面的实现存在的问题

Overflow and underflow.
・Underflow: throw exception if pop from an empty stack.
・Overflow: use resizing array for array implementation.
Null items.
We allow null items to be inserted.
Loitering.
Holding a reference to an object when it is no longer needed.

public String pop()
{ return s[--N]; }  //s[N]保存的object已经不需要了,但是这里还存在array里
public String pop()
{
    String item = s[--N];
    s[N] = null;  //只有把指向不需要的object的reference去掉,才能释放内存
    return item;
}

2. Resizing arrays

之前的实现需要用户在创建FixedCapacityStackOfStrings时提供capacity作为array的长度,但是大多数时候这是不现实的,所以需要实现能够自行变大和变小的array。

最直接简单的想法是:
・push(): increase size of array s[] by 1.
・pop(): decrease size of array s[] by 1.

但是这样每次push或pop操作就需要将array已有的内容复制到新array里。插入N个object,复杂度为:
1 + 2 + 3 + . . . + N = ( 1 + N ) N / 2 ~ N 2 2

2.1 Resizing array实现

另一种做法是,每次array满了之后,新建一个两倍于之前array大小的新array

public ResizingArrayStackOfStrings()
{ s = new String[1]; }
public void push(String item)
{
    if (N == s.length) resize(2 * s.length);
    s[N++] = item;
}
private void resize(int capacity)
{
    String[] copy = new String[capacity];
    for (int i = 0; i < N; i++)
    copy[i] = s[i];
    s = copy;
}

插入N个object,复杂度为:
N个object,需要新建array lg(N)次,并把旧array已有的内容复制到新array里,即

2 + 4 + 8 + . . . + N = 2 2 N 1 2 = 2 N 1

此外,当pop掉一些object之后,应该减小array大小,最简单的想法是,和增大array一倍一样,当array只有一半使用时就把只存减小到之前的一半:
・push(): double size of array s[] when array is full.
・pop(): halve size of array s[] when array is one-half full.
但是,考虑最坏情况下的复杂度:
当array满了之后,不断的push,pop,push,pop,此时每个操作都需要新建array,并将旧array的N个objects复制过来,每一次的array存取都是~N。

扫描二维码关注公众号,回复: 3770971 查看本文章

另一种高效率的方法是:
・push(): double size of array s[] when array is full.
・pop(): halve size of array s[] when array is one-quarter full.

public String pop()
{
    String item = s[--N];
    s[N] = null;
    if (N > 0 && N == s.length/4) resize(s.length/2);
    return item;
}

这种方法的复杂度分析:

/ best worst amortized
construct 1 1 1
push 1 N 1
pop 1 N 1
size 1 1 1

内存占用:
8 bytes (reference to array)
24 bytes (array overhead)
8 bytes × array size
4 bytes (int)
4 bytes (padding)
当array size = N (全满)的时候,~8N
当array size = 4N (只占四分之一)的时候,~32N

使用Linked list和Resizing array实现的Stack比较:
Linked-list implementation.
・Every operation takes constant time in the worst case.
・Uses extra time and space to deal with the links.
Resizing-array implementation.
・Every operation takes constant amortized time.
・Less wasted space.

3. Queues

3.1 Queues的API

public class QueueOfStrings
QueueOfStrings() create an empty queue
void enqueue(String item) insert a new string onto queue
String dequeue() remove and return the string
least recently added
boolean isEmpty() is the queue empty?
int size() number of strings on the queue

3.2 Linked list实现queue

queue是先入先出,因此enqueue时把刚存入的放在链表最后,dequeue时从链表前面开始取。

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;
    }
}

3.3 Resizing array实现queue

・Use array q[] to store items in queue.
・enqueue(): add new item at q[tail].
・dequeue(): remove item from q[head].
・Update head and tail modulo the capacity.
・Add resizing array.

4. Generics

4.1 Generic stack: linked-list implementation

public class Stack<Item>
{
    private Node first = null;
    private class Node
    {
        Item item;
        Node next;
    }
    public boolean isEmpty()
    { return first == null; }
    public void push(Item item)
    {
        Node oldfirst = first;
        first = new Node();
        first.item = item;
        first.next = oldfirst;
    }
    public Item pop()
    {
        Item item = first.item;
        first = first.next;
        return item;
    }
}

4.2 Generic stack: array implementation

Java不允许创建generic array,所以下面这种实现是不可以的。

public class FixedCapacityStack<Item>
{
    private Item[] s;
    private int N = 0;
    public FixedCapacityStack(int capacity)
    { s = new Item[capacity]; }
    public boolean isEmpty()
    { return N == 0; }
    public void push(Item item)
    { s[N++] = item; }
    public Item pop()
    { return s[--N]; }
}

在这一课里,采用了如下的使用强制类型转换的实现方法,后面应该会采用其他方法替代:

public class FixedCapacityStack<Item>
{
    private Item[] s;
    private int N = 0;
    public FixedCapacityStack(int capacity)
    { s = (Item[]) new Object[capacity]; }
    public boolean isEmpty()
    { return N == 0; }
    public void push(Item item)
    { s[N++] = item; }
    public Item pop()
    { return s[--N]; }
}

对于primitive types,每一种primitive type都有wrapper object type,例如Integer 是int的wrapper type。

//autoboxing: Automatic cast between a primitive type and its wrapper
Stack<Integer> s = new Stack<Integer>();
s.push(17); // s.push(Integer.valueOf(17));
int a = s.pop(); // int a = s.pop().intValue();

5. Iterators

Iterable:Has a method that returns an Iterator.
Iterator:Has methods hasNext() and next().

Iterable interface

public interface Iterable<Item>
{
Iterator<Item> iterator();
}

Iterator inferface

public interface Iterator<Item>
{
    boolean hasNext();
    Item next();
    void remove();
}

Client段可以这样遍历stack的内容:

for (String s : stack)
    StdOut.println(s);

或等价地:

Iterator<String> i = stack.iterator();
while (i.hasNext())
{
    String s = i.next();
    StdOut.println(s);
}

5.1 Stack iterator: linked list implementation

import java.util.Iterator;
public class Stack<Item> implements Iterable<Item>
{
    ...
    public Iterator<Item> iterator() { return new ListIterator(); }
    private class ListIterator implements Iterator<Item>
    {
        private Node current = first;
        public boolean hasNext() { return current != null; }
        public void remove() { /* not supported */ }
        public Item next()
        {
            Item item = current.item;
            current = current.next;
            return item;
        }
    }
}

5.2 Stack iterator: array implementation

import java.util.Iterator;
public class Stack<Item> implements Iterable<Item>
{public Iterator<Item> iterator()
    { return new ReverseArrayIterator(); }
    private class ReverseArrayIterator implements Iterator<Item>
    {
        private int i = N;
        public boolean hasNext() { return i > 0; }
        public void remove() { /* not supported */ }
        public Item next() { return s[--i]; }
    }
}

5.3 Bag

向一个collections里添加items,并能够进行遍历,顺序不重要,实现的时候相当于没有pop的stack或者没有dequeue的queue。

public class Bag<Item> implements Iterable<Item>
Bag() create an empty bag
void add(Item x) insert a new item onto bag
int size() number of items in bag
Iterable<Item> iterator() iterator for all items in bag

6. Applications

6.1 Two-stack algorithm. [E. W. Dijkstra]

・遇到Value: push onto the value stack.
・遇到Operator: push onto the operator stack.
・遇到Left parenthesis: ignore.
・遇到Right parenthesis: pop operator and two values; push the result of applying that operator to those values onto the value stack.

public class Evaluate
{
    public static void main(String[] args)
    {
        Stack<String> ops = new Stack<String>();
        Stack<Double> vals = new Stack<Double>();
        while (!StdIn.isEmpty()) {
            String s = StdIn.readString();
            if (s.equals("(")) ;
            else if (s.equals("+")) ops.push(s);
            else if (s.equals("*")) ops.push(s);
            else if (s.equals(")"))
            {
                String op = ops.pop();
                if (op.equals("+")) vals.push(vals.pop() + vals.pop());
                else if (op.equals("*")) vals.push(vals.pop() * vals.pop());
            }
            else vals.push(Double.parseDouble(s));
        }
        StdOut.println(vals.pop());
    }
}

猜你喜欢

转载自blog.csdn.net/Cyril__Li/article/details/79760470