1.1.4 简便记法
程序有很多写法,我们追求清晰、优雅和高效的代码。这样的代码经常会使用以下这些广为流传的简便写法(不仅仅是Java,很多语言都支持它们)。
1.1.4.1 声明并初始化
最好在接近首次使用变量的地方声明它并将其初始化(为了限制它的作用域)。
int i = 1;//创建了名为i的变量并赋予其初始值1。
1.1.4.2 隐式赋值
- 递增/递减运算符 ++i;--i;
- 其它复合运算符 i/2; i += 1;
1.1.4.3 单语句代码段
如果条件或循环语句的代码段只有一条语句,代码段的花括号可以省略。
1.1.4.4 for语句
很多循环的模式都是这样的:初始化一个索引变量,然后使用while循环并将包含索引变量的表达式作为循环的条件,while循环的最后一条语句会将索引变量加1.使用Java的for语句可以更紧凑地表达这种循环。
1.3.3 链表
链表是一种递归的数据结构,它或者为空(null),或者是指向一个结点(node)的引用,该节点含有一个泛型的元素和一个指向另一条链表的引用。
1.3.3.1 结点记录
在面向对象编程中,实现链表并不困难。我们首先用一个嵌套类来定义结点的抽象数据类型。
class Node{
Item item;
Node next;
}
public class TestNode {
public static void main(String[] args) {
//新建结点
Node first = new Node();
Node second = new Node();
Node last = new Node();
//为结点赋值
first.item = "to";
second.item = " be";
third.item = " or";
//为结点的下一个引用赋值
first.next = second;
second.next = last;
third.next = null;
//在表头插入结点
Node oldfirst = first;
first = new Node();
first.item = "not";
first.next = oldfirst;
//从表头删除结点
first = first.next;
//在表尾插入结点
Node oldlast = last;
last = new Node();
last.item = "not";
oldlast.next = last;
//链表的遍历
for (Node x = first;x != null;x = x.next){
//处理x.item
}
}
}
1.3.3.8 栈的实现
由以上预备知识,给出Stack API的实现就很简单了。可将栈保存为一条链表,栈的顶部即为表头,实例变量first指向栈顶。这样,当使用push()压入一个元素时,我们会按照上述代码将该元素添加在表头;当使用pop()删除一个元素时,会按照上述讨论的代码将该元素从表头删除。要实现size()方法,我们用实例变量N保存元素的个数,在压入元素时将N加1,在弹出元素时将N减1.要实现isEmpty()方法,只需要检查first是否为null(或者检查N是否为0)。
下压堆栈(链表实现)
public class Stack<Item> implements Iterable<Item> {
private Node<Item> first; // top of stack
private int n; // size of the stack
// helper linked list class
private static class Node<Item> {
private Item item;
private Node<Item> next;
}
/**
* Initializes an empty stack.
*/
public Stack() {
first = null;
n = 0;
}
/**
* Returns true if this stack is empty.
*
* @return true if this stack is empty; false otherwise
*/
public boolean isEmpty() {
return first == null;
}
/**
* Returns the number of items in this stack.
*
* @return the number of items in this stack
*/
public int size() {
return n;
}
/**
* Adds the item to this stack.
*
* @param item the item to add
*/
public void push(Item item) {
Node<Item> oldfirst = first;
first = new Node<Item>();
first.item = item;
first.next = oldfirst;
n++;
}
/**
* Removes and returns the item most recently added to this stack.
*
* @return the item most recently added
* @throws NoSuchElementException if this stack is empty
*/
public Item pop() {
if (isEmpty()) throw new NoSuchElementException("Stack underflow");
Item item = first.item; // save item to return
first = first.next; // delete first node
n--;
return item; // return the saved item
}
/**
* Returns (but does not remove) the item most recently added to this stack.
*
* @return the item most recently added to this stack
* @throws NoSuchElementException if this stack is empty
*/
public Item peek() {
if (isEmpty()) throw new NoSuchElementException("Stack underflow");
return first.item;
}
/**
* Returns a string representation of this stack.
*
* @return the sequence of items in this stack in LIFO order, separated by spaces
*/
public String toString() {
StringBuilder s = new StringBuilder();
for (Item item : this) {
s.append(item);
s.append(' ');
}
return s.toString();
}
/**
* Returns an iterator to this stack that iterates through the items in LIFO order.
*
* @return an iterator to this stack that iterates through the items in LIFO order
*/
public Iterator<Item> iterator() {
return new ListIterator<Item>(first);
}
// an iterator, doesn't implement remove() since it's optional
private class ListIterator<Item> implements Iterator<Item> {
private Node<Item> current;
public ListIterator(Node<Item> first) {
current = first;
}
public boolean hasNext() {
return current != null;
}
public void remove() {
throw new UnsupportedOperationException();
}
public Item next() {
if (!hasNext()) throw new NoSuchElementException();
Item item = current.item;
current = current.next;
return item;
}
}
/**
* Unit tests the {@code Stack} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
Stack<String> stack = new Stack<String>();
while (!StdIn.isEmpty()) {
String item = StdIn.readString();
if (!item.equals("-"))
stack.push(item);
else if (!stack.isEmpty())
StdOut.print(stack.pop() + " ");
}
StdOut.println("(" + stack.size() + " left on stack)");
}
}
1.3.3.9 队列的实现
基于链表数据结构实现Queue API也很简单。它将队列表示为一条从最早插入的元素到最近插入的元素的链表,实例变量first指向队列的开头,实例变量last指向队列的结尾。这样,要将一个元素入列(enqueue()),我们就将它添加到表尾;要将一个元素出列(dequeue()),我们就删除表头的节点。
顺便纠错一波,《算法》(第4版)原文150页,关于Queue的表述:
This implementation uses the same data structure as does Stack—a linked list—but it implements different algorithms for adding and removing items, which make the difference between LIFO and FIFO for the client.
然而中文版95页翻译为:
Queue的实现使用的数据结构和Stack相同-链表,但它实现了不同的添加和删除元素的算法,这也是用例所看到的后进先出和先进后出的区别所在。
后进先出(LIFO)和先进后出(FILO) 是同样的,结合英文原版,这里应该是译者错误。
关于书中对log与lg的表述,有些疑问,经过英文版的对照和查询,得知logN与lgN两种表示均可。
在《算法导论》中有过约定,使用记号:lgn = log2(n)(以2为底的对数)。