其餘原始類型: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--; } }