其余原始类型:java
声明并初始化node
隐式赋值算法
单语句代码段编程
建立数组
起别名网络
方法的性质数据结构
递归模块化
编写递归代码时最重要的有如下三点:函数
递归调用的父问题和尝试解决的子问题之间不该该有交集。工具
二分查找的递归实现 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; }
基础编程模型
模块化编程
API
应用程序接口
重定向与管道
import java.util.Arrays; public class BinarySearch { public static int rank(int key, int[] a) { //数组必须是有序的 int lo = 0; int hi = a.length - 1; while(lo <= hi) { //被查找的键要么不存在,要么必然存在与a[lo..hi]之中 int mid = lo + (hi - lo) / 2; if (key < a[mid]) hi = mid -1; else if (key > a[mid]) lo = mid + 1; else return mid; } return -1; } public static void main(String[] args) { int[] whitelist = In.readInts(args[0]); Arrays.sort(whitelist); while(!StdIn.isEmpty()) { //读取键值,若是不存在于白名单中则将其打印 int key = StdIn.readInt(); if(rank(key.whitelist) < 0) StdOut.println(key); } } }
抽象数据类型(ADT)的定义和静态方法库共同之处:
不一样:
做用域:
封装
容许
隔离了数据类型的操做
接口继承:子类型,容许经过指定一个含有一组公共方法的接口为两个原本没有关系的类创建一种联系,这两个类都不准实现这些方法。
public interface Datable { int month(); int day(); int year(); } public class Date implements Datable { //实现代码 }
等价性:
java约定equals()必须是一种等价性关系。它必须具备:
另外,它必须接受一个Object为参数并知足如下性质:
不可变性:final只能用来保证原始数据类型的实例变量的不可变性,而没法用于引用类型的变量。 若是一个应用类型的实例变量含有修饰符final,该实例变量的值(某个对象的引用)永远没法改变——它将永远指向同一个对象,但对象的值自己仍然是可变的。
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
泛型
例如,编写用栈来处理String对象: java Stack<String> stack = new Stack<String>(); stack.push("Test"); ... String next = stack.pop(); 使用队列处理Date对象: java Queue<Date> queue = new Queue<Date>(); queue.enqueue(new Date(12, 31, 1999)); ... Date next = queue.dequeue();
自动装箱
java Stack<Integer> stack = new Stack<Integer>(); stack.push(17);//自动装箱(int -> Integer) int i = stack.pop();//自动拆箱(INteger -> int)
可迭代集合类型
例如,假设用例在Queue中维护一个交易集合 java Queue<Transaction> collection = new Queue<Transaction>(); //若是集合是可迭代的,用例用一行语句便可打印出交易的列表: for (Transaction t : collection){ StdOut.print(t);} 这种语法叫foreach语句
背包
简单的计算输入中全部double值的平均值和样本标准差。注意:不须要保存全部的数也能够计算标准差。
public ckass Stats { public static void main(String[] args) { Bag<Double> numbers = new Bag<Double>(); 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); } }
队列
In类的静态方法readInts()的一种实现,该方法解决的问题:用例无需预先知道文件的大小便可将文件中的全部整数读入一个数组中。 public static int[] readInts(String name) { In in = new In(name); Queue<Integer> q = new Queue<Integer>(); 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<Integer> stack; stack = new Stack<Integer>(); while(!StdIn.isEmpty()) stack.push(StdIn.readInt()); for (int i : stack) StdOut.println(i); } }
Dijikstra的双栈算术表达式求值算法
java public class Evaluate { public static void main(String[] args) Stack<String> ops = 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("*")) 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) } //若是字符既非运算符也不是括号,将它做为double之压入栈 else vals.push(Double.parseDouble(s));//字符是数字 } StdOut.println(vals.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<Item> 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() { } } }
在须要使用Node类的类中定义它并将它标记为private,由于它不是为用例准备的。 private class Node { Item item; Node next; }
经过new Node()触发(无参数的)构造函数来建立一个Node类型的对象。调用的结果是一个指向Node对象的引用,它的实例变量均被初始化为null。Item是一个占位符,表示咱们但愿用链表处理的任意数据类型。
构造链表
Node first = new Node(); Node second = new Node(); Node thrid = new Node();
first.item = "to"; second.item = "be"; thrid.item = "or";
first.next = second; second.next = third;
second也是一条链表(它是一个结点的引用,且该结点含有一个指向third的引用,而third是一条链表)
first也是一条链表(它是一个结点的引用,且该结点含有一个指向second的引用,而second是一条链表)
插入删除元素
栈的实现
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<String> s = new Stack<String>(); 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<String> s = new Queue<String>(); 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<Item> 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; } } } ```
对于大多数程序,获得其运行时间的数据模型所需的步骤:
2-sum NlogN解法(假设全部整数各不相同)
若是二分查找返回的j在0和i之间,不能增长计数器,避免重复计数。
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; } }
3-sum N2logN解法(假设全部整数各不相同)
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--; }
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--; } }