数组名表示的是整个数组——如果我们将一个数组变量赋予另一个变量,那么两个变量将会指向同个数组。
int[] a =new int[N];
...
a[i] = 1234;
...
int[] b = a;
...
b[i] = 5678//a[i]的值也会编程5678
Math.abs()
、Math.min()
、Math.max()
。另一种用法是为函数定义两个版本,一个需要一个参数另一个则为该参数提供一个默认值。return
的条件语句。public static int rank(int key, int[] a)
{ return rank(key, a, 0, a.length - 1); }
public static int rank(int key, int[] a, int lo, int hi)
{//如果key存在于a[]中,它的索引不会小于lo且不会大于hi
if(lo > hi) return -1;
int mid = lo + (hi - lo) / 2;
if(key < a[mid]) return rank(key, a, lo, mid -1 );
else if(key > a[mid]) return rank(key, a, mid + 1, hi);
else return mid;
}
自动转换:Java在连接字符串的时候会自动将任意数据类型的值转换为字符串:如果加号(+)的一个参数是字符串,那个Java会自动将其他参数都转换为字符串。这样,通过一个空字符串”“可将任意数据类型的值转换为字符串值。
public interface Datable
{
int month();
int day();
int year();
}
public class Date implements Datable
{
//实现代码
}
等价性:java约定equals()必须是一种等价性关系。它必须具有:
另外,它必须接受一个Object为参数并满足以下性质:
public class Vector
{
private final double[] coords;
public Vector(double[] a)
{
coords = a;
}
...
}
用例程序可以通过给定的数组创建一个Vector对象,并在构造对象执行之后改变Vector中的元素的值:
double[] a = {3.0, 4.0};
Vector vector = new Vector(a);
a[0] = 0.0;//绕过 了公有API
记号将Item
定义为一个类型参数。它是一个象征性的占位符,表示的是用例将会使用的某种具体数据类型。 String
对象: java
Stack stack = new Stack();
stack.push("Test");
...
String next = stack.pop();
java
Queue queue = new Queue();
queue.enqueue(new Date(12, 31, 1999));
...
Date next = queue.dequeue();
java
Stack stack = new Stack();
stack.push(17);//自动装箱(int -> Integer)
int i = stack.pop();//自动拆箱(INteger -> int)
java
Queue collection = new Queue();
如果集合是可迭代的,用例用一行语句即可打印出交易的列表:
for (Transaction t : collection){ StdOut.print(t);}
foreach
语句double
值的平均值和样本标准差。注意:不需要保存所有的数也可以计算标准差。public ckass Stats
{
public static void main(String[] args)
{
Bag numbers = new Bag();
while(!StdIn.isEmpty())
numbers.add(StdIn.readDouble());
int N = numbers.size();
double sum = 0.0;
for (double x : numbers)
sum += x;
double mean = sum/N;
sum = 0.0;
for(double x : numbers)
sum +=(x - mean)*(x - mean);
double std = Math.sqrt(sum/(N-1));
StdOut.printf("Mean: %.2f\n", mean);
StdOut.printf("Std dev: %.2f\n", std);
}
}
队列一种基于先进先出(FIFO)策略的集合类型。用集合保存元素的同时保存它们的相对顺序:是它们入列顺序和出列顺序相同。
In类的静态方法readInts()的一种实现,该方法解决的问题:用例无需预先知道文件的大小即可将文件中的所有整数读入一个数组中。
public static int[] readInts(String name)
{
In in = new In(name);
Queue q = new Queue();
while (!in.isEmpty())
q.enqueue(in.readInt());
int N = q.size();
int [] a = new int[N];
for (int i = 0; i < N; i++)
a[i] = q.dequeue();
return a;
}
把标准输入中的所有整数逆序排列,无需预先知道整数的多少。
public class Reverse
{
public static void main(String[] args)
{
Stack stack;
stack = new Stack();
while(!StdIn.isEmpty())
stack.push(StdIn.readInt());
for (int i : stack)
StdOut.println(i);
}
}
java
public class Evaluate
{
public static void main(String[] args)
Stack ops = new Stack();
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("*")) ops.push(s);
else if (s.equals("/")) ops.push(s);
else if (s.equals("sqrt")) ops.push(s);
else if (s.equals(")"))
{
String op = ops.pop();
double v = vals.pop();
if (op.equals("+")) v = vals.pop() + v;
else if (op.equals("+")) v = vals.pop() - v;
else if (op.equals("+")) v = vals.pop() * v;
else if (op.equals("+")) v = vals.pop() / v;
else if (op.equals("+")) v = Math.sqrt(v);
vals.push(v)
}
else vals.push(Double.parseDouble(s));//字符是数字
}
StdOut.println(vals.pop());
}
栈(能够动态调整数组大小的实现):
push()
、pop()
操作会调整数组的大小,这项操作的耗时跟栈大小成正比import java.util.Iterator;
public class ResizingArrayStack<Item> implements Iterable<Item>
{
private Item[] a = (Item[]) new Object[1];//栈元素。java不允许创建泛型数组,因此需要使用类型转换
private int N = 0;//元素数量
public boolean isEmpty() {return N == 0;}
public int size() {return N;}
private void resize(int max)
{//由于java数组创建后无法改变大小,采用创建大小为max的新数组来替代旧数组的方式动态改变数组实际大小
Item[] temp = (Item[]) new Object[max];
for (int i = 0;i < N; i++)
temp[i] = a[i];
a = temp;
}
public void push(Item item)
{//将元素添加到栈顶
if (N == a.length) resize(2*a.length);
a[N++] = item;
}
public Item pop()
{//从栈顶删除元素
Item item = a[--N];
a[N] = null;//避免对象游离
if (N > 0 && N == a.length/4) resize(a.length/2);
return item;
}
public Iterator- iterator()
{ return new ReverseArrayIterator(); }
private class ReverseArrayIterator implements Iterator<Item>
{//支持后进先出的迭代
private int i = N;
public boolean hasNext() { return i > 0;}
public Item next() { return a[--i];}
public void remove() { }
}
}
用一个嵌套类来定义节点的抽象数据类型
private class Node//在需要使用Node类的类中定义它并将它标记为private,因为它不是为用例准备的。
{
Item item;
Node next;
}
通过new Node()
触发(无参数的)构造函数来创建一个Node类型的对象。调用的结果是一个指向Node对象的引用,它的实例变量均被初始化为null。Item是一个占位符,表示我们希望用链表处理的任意数据类型。
java
Node first = new Node();
Node second = new Node();
Node thrid = new Node();
java
first.item = "to";
second.item = "be";
thrid.item = "or";
java
first.next = second;
second.next = third;
栈的实现(使用链表):
public class Stack<Item> implements Iterable<Item>
{
private Node first;//栈顶(最近添加的元素)
private int N;
private class Node
{//定义了结点的嵌套类
Item item;
Node next;
}
public boolean isEmpty() {return N == 0;}//或:return first == null;
public int size() {return N;}
public void push(Item item)
{//向栈顶添加元素
Node oldfirst = first;
first = new Node();
first.item = item;
first.next = oldfirst;
N++;
}
public Item pop()
{
Item item = first.item;
first = first.next;
N--;
return item;
}
//iterator()的实现见背包实现算法
public static void main(String[] args)
{//输入to be or not to - be - - that - - - is
Stack s = new Stack();
while(!StdIn.isEmpty())
{
String item = StdIn.readString();
if(!item.equals("-"))
s.push(item);
else if(!s.isEmpty()) StdOut.print(s.pop() + " ");
}
StdOut.println("(" + s.size() + " left on stack)");
}
}
public class Queue<Item> implements Iterable<Item>
{
private Node first;
private Node last;
private int N;
private class Node
{
Item item;
Node next;
}
public boolean isEmpty() {return N == 0;}//或:return first == null;
public int size() {return N;}
public void enqueue(Item item)
{//向表尾添加元素
Node oldfirst = last;
last = new Node();
last.item = item;
last.next = null;
if (isEmpty()) first = last;
else oldfirst.next = last;
N++;
}
public Item dequeue()
{//从表头删除元素
Item item = first.item;
first = first.next;
if (isEmpty()) last = null;
N--;
return item;
}
//
public static void main(String[] args)
{//输入to be or not to - be - - that - - - is
Queue s = new Queue();
while(!StdIn.isEmpty())
{
String item = StdIn.readString();
if(!item.equals("-"))
q.enqueue(item);
else if(!q.isEmpty()) StdOut.print(q.dequeue() + " ");
}
StdOut.println("(" + q.size() + " left on queue)");
}
}
背包的实现
import java.util.Iterator;
public class Bag<Item> implements Iterable<Item>
{
private Node first;
private class Node
{
Item item;
Node next;
}
public void add(Item item)
{
Node oldfirst = first;
first = new Node();
first.item = item;
first.next = oldfirst;
}
//通过遍历链表使Stack、Queue、Bag变为可迭代的。对于Stack,链表的访问顺序是后进先出;Queue,链表的访问顺序是先进先出;Bag,后进先出顺序,但顺序不重要。
public Iterator- iterator()
{ return new ListIterator();}
private class ListIterator implements Iterator<Item>
{
private Node current = first;
public boolean hasNext()
{ return current != null;}
public void remove() { }
public Item next()
{
Item item = current.item;
current = current.next;
return item;
}
}
}
java
import java.util.Arrays;
public class TwoSumFast
{
public static int cout(int[] a)
{
Arrays.sort(a);
int N = a.length;
int cnt = 0;
for (int i = 0; i< N; i++)
if (BinarySearch.rank(-a[i], a) > i)
cnt++;
return cnt;
}
}
import java.util.Arrays;
public class ThreeSumFast
{
public static int cout(int[] a)
{
Arrays.sort(a);
int N = a.length;
int cnt = 0;
for (int i = 0; i< N; i++)
for(int j = i + 1;j < N; j++)
if (BinarySearch.rank(-a[i]-a[j], a) > j)
cnt++;
return cnt;
}
}
java
public class UF
{
private int[] id;//分量id(以触点作为索引)
private int count; //分量数量
public UF(int N)
{//初始化分量id数组
count = N;
id = new int[N];
for(int i=0;i < N;i++)
id[i] = i;
}
public int count()
{ return count;}
public boolean connected(int p, int q)
{ renturn find(p) == find(q); }
public int find(int p)//见quick-find
public void union(int p, int q)//见quick-union,加权quick-union
public static void main(String[] args)
{//解决由StdIn得到的动态连通性问题
int N = StdIn.readInt() //读取触点数量
UF N = new UF(N); //初始化N个分量
while (!StdIn.isEmpty())
{
int p = StdIn.readInt();
int q = StdIn.readInt();//读取整数对
if (uf.connected(p, q)) continue;//如果已经连通则忽略
uf.union(p, q);//归并分量
StdOut.println(p + " " + q);//打印连接
}
StdOut.println(uf.count() + "components");
}
}
quick-find算法:保证当且仅当id[p]等于id[q]时p和q是连通的。换句话说,在同一个连通分量重的所有触点在id[]中的值必须全部相同。
public int find(int p)
{ return id[p]; }
public void union(int p, int q)
{//将p和q归并到相同的分量中
int pID = find(p);
int qID = find(q);
//如果p和q已经在相同的分量之中则不需要采取任何行动
if (pID == qID) return;
//将p的分量重命名为q的名称
for (int i = 0;i < id.length; i++)
if (id[i] == pID) id[i] = qID;
count--;
}
find()操作的速度显然是很快的,因为它只需要访问id[]数组一次。但quick-find算法一般无法处理大型问题,因为对于每一对输入union()都需要扫描整个id[]数组。
quick-union算法:
private int find(int p)
{//找出分量的名称
while(p != id[p]) p = id[p];
return p;
}
public void union(int p, int q)
{//将p和q的根节点统一
int pRoot = find(p);
int qRoot = find(q);
if (pRoot == qRoot) return;
id[pRoot] = qRoot;
count--;
}
加权 quick-union算法:记录每一棵树的大小并总是将较小的树连接到较大的树上。
public class UF
{
private int[] id;//父链接数组(由触点索引)
private int[] sz;//(有触点索引的)各个根节点所对应的分量的大小
private int count; //连通分量的数量
public WeightedQuickUnionUF(int N)
{
count = N;
id = new int[N];
for(int i=0;i < N;i++)
id[i] = i;
sz = new int[N];
for(int i = 0; i < N; i++) sz[i] = 1;
}
public int count()
{ return count;}
public boolean connected(int p, int q)
{ renturn find(p) == find(q); }
public int find(int p)
{//跟随链接找到根节点
while(p != id[p]) p = id[p];
return p;
}
public void union(int p, int q)
{
int i = find(p);
int j = find(q);
if(i == j) return;
//将小树的根节点连接到大树的根节点
if (sz[i] < sz[j]) { id[i] = j; sz[j] += sz[i];}
else{id[j] = i;sz[i] += sz[j];}
count--;
}
}