演算法時間複雜度的常用遞推關係
遞迴關係對於分析演算法時間複雜度非常有用,下表總結了常用的地推關係:
遞推關係 | 複雜度結果 | 示例 |
---|---|---|
T(n)=T(n/2) + O(1) | T(n) = O(logn) | 二分查詢 |
T(n)=T(n-1) + O(1) | T(n) = O(n) | 線性查詢 |
T(n)=2T(n/2) + O(1) | T(n) = O(n) | 尋最值的二分遞迴查詢 |
T(n)=2T(n/2) + O(n) | T(n) = O(nlogn) | 歸併排序 |
T(n)=T(n-1) + O(n) | T(n) = O(n^2) | 選擇排序 |
T(n)=2T(n-1) + O(1) | T(n) = O(2^n) | 漢諾塔 |
不同複雜度函式的比較關係如下:
O(1) < O(log n) < O(n) < O(nlogn) < O(n^2) < O(2^n)
演算法示例
字串的最長非遞減子串
public static String maxConsecutiveSortedSubstring(String s) {
int maxSize = 1;
int tmpbeg = 0;
int beg = 0;
int tmpMax = 1;
for (int i=1; i < s.length(); i++) {
if (s.charAt(i) >= s.charAt(i-1)) {
tmpMax++;
if (tmpMax > maxSize) {
maxSize = tmpMax;
beg = tmpbeg;
}
} else {
tmpMax = 1;
tmpbeg = i;
}
}
return s.substring(beg, beg + maxSize);
}
動態規劃求斐波那契序列
public class Fibnacci {
public static long fib(long n) {
long f0 = 0;
long f1 = 1;
long f2 = 1;
if (n==0)
return f0;
else if (n==1)
return f1;
else if (n==2)
return f2;
for (int i=3; i <=n; i++) {
f0 = f1;
f1 = f2;
f2 = f0 + f1;
}
return f2;
}
}
動態規劃通過解決子問題,然後將子問題的結果結合來獲得整個問題的解的過程。這自然地引向遞迴的解答。然而,使用遞迴效率不高,因為子問題相互重疊了。動態規劃的關鍵思想只解決子問題一次,並將子問題的結果以備後用,從而避免了重複的子問題求解。
求最大公約數的歐幾里得演算法
public class EuclidGCD {
public static int gcd(int m, int n) {
if (m % n == 0)
return n;
else
return gcd(n, m%n);
}
}
埃拉托色尼篩選法求素數
import java.util.Scanner;
public class SieveOfEratosthenes {
public static void main(String[] args) {
System.out.println("Find all prime numbers <= n.");
int n = input.nextInt();
boolean[] primes = new boolean[n+1];
for (int i=0; i < primes.length; i++) {
primes[i] = true;
}
for (int k = 2; k <= n/k; k++) {
if (primes[k]) {
for (int i = k; i<=n/k; i++) {
primes[k*i] = false; // k*i is not prime
}
}
}
int count = 0;
for (int i=2; i<primes.length; i++) {
if (primes[i])
count++;
}
System.out.println("\n" + count + " prime(s) less than or equal to " + n);
}
}
分治方法與最近點對問題
分而治之(divide-and-conquer),這個方法將問題分解為子問題,解決子問題,然後將子問題的解答合併從而獲得整個問題的解答。和動態規劃方法不同的是,分而治之方法中的子問題不會交叉。子問題類似初始問題,但具有更小的規模,因此可以應用遞迴來解決這樣的問題。事實上,所有的遞迴的解答都遵循分而治之方法。
步驟1: 以x座標的升序對點進行排序,對於x座標一樣的點,按它的y座標排序,這樣就得到一個排好序的點構成的線性表S;
步驟2: 使用排好序的線性表的中點將S劃分為兩個大小相等的子集S1和S2,讓中點位於S1。遞迴地找到S1和S2中的最近點對,設d1和d2分別表示兩個子集中最近點對點距離。
步驟3: 找出S1和S2中點點之間距離最近點點對,它們之間的距離用d3表示,則最近的點對距離為min(d1, d2, d3)的點對。
import java.util.Arrays;
import java.util.ArrayList;
public class ClosedPair {
public static Pair getClosestPair(double[][] points) {
Point[] pointsOrderedOnX = new Point[points.length];
for (int i = 0; i < pointsOrderedOnX.length; i++)
pointsOrderedOnX[i] = new Point(points[i][0], points[i][1]);
return getClosestPair(pointsOrderedOnX);
}
public static Pair getClosetPair(Point[] points) {
Arrays.sort(points);
// Locate the identical points if exists
Pair pair = checkIdentical(points);
if (pair != null)
return pair; // The distance between the identical points is 0
Point[] pointsOrderedOnY = points.clone();
Arrays.sort(pointsOrderedOnY, new CompareY());
return distance(points, 0, points.length - 1, pointsOrderedOnY);
}
/** Locate the identical points if exist */
public static Pair checkIdentical(Point[] pointsOrderedOnX) {
Pair pair = new Pair();
for (int i = 0; i < pointsOrderedOnX.length - 1; i++) {
if (pointsOrderedOnX[i].compareTo(pointsOrderedOnX[i + 1]) == 0) {
pair.p1 = pointsOrderedOnX[i];
pair.p2 = pointsOrderedOnX[i + 1];
return pair;
}
}
return null;
}
public static Pair distance(Point[] pointOrderedOnX, int low, int high, Point[] pointsOrderedOnY) {
if (low >= high) // Zero or one point in the set
return null;
else if (low + 1 == high) {
// Two points in the set
Pair pair = new Pair();
pair.p1 = pointsOrderedOnX[low];
pair.p2 = pointsOrderedOnX[high];
return pair;
}
// Step 2
int mid = (low + high) / 2;
Pair pair1 = distance(pointsOrderedOnX, low, mid, pointsOrderedOnY);
Pair pair2 = distance(pointsOrderedOnX, mid + 1, high, pointsOrderedOnY);
double d;
Pair pair = null;
if (pair1 == null && pair2 == null) {
d = Double.MAX_VALUE;
} else if (pair1 == null) {
d = pair2.getDistance();
pair = pair2;
} else if (pair2 == null) {
d = pair1.getDistance();
pair = pair1;
} else {
d = Math.min(pair1.getDistance(), pair2.getDistance());
if (pair1.getDistance() <= pair2.getDistance())
pair = pair1;
else
pair = pair2;
}
// Obtain stripL and stripR
ArrayList<Point> stripL = new ArrayList<Point>();
ArrayList<Point> stripR = new ArrayList<Point>();
for (int i = 0; i < pointsOrderedOnY.length; i++) {
if (pointsOrderedOnY[i].x <= pointsOrderedOnX[mid].x
&& pointsOrderedOnY[i].x >= pointsOrderedOnX[mid].x - d)
stripL.add(pointsOrderedOnY[i]);
else if (pointsOrderedOnY[i].x > pointsOrderedOnX[mid].x
&& pointsOrderedOnY[i].x <= pointsOrderedOnX[mid].x + d)
stripR.add(pointsOrderedOnY[i]);
}
// Step 3: Find the closest pair for a point in stripL and
// a point in stripR
double d3 = d;
int r = 0;
for (int i = 0; i < stripL.size(); i++) {
while (r < stripR.size() && stripL.get(i).y > stripR.get(r).y + d)
r++;
// Compare a point in stripL with at most six points in stripR
int r1 = r; // Start from r1 up in stripR
while (r1 < stripR.size() && stripR.get(r1).y <= stripL.get(i).y + d) {
if (d3 > distance(stripL.get(i), stripR.get(r1))) {
d3 = distance(stripL.get(i), stripR.get(r1));
pair.p1 = stripL.get(i);
pair.p2 = stripR.get(r1);
}
r1++;
}
}
return pair;
}
public static double distance(Point p1, Point p2) {
return distance(p1.x, p1.y, p2.x, p2.y);
}
public static double distance(double x1, double y1, double x2, double y2) {
return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}
/** Define a class for a point with x- and y- coordinates */
static class Point implements Comparable<Point> {
double x;
double y;
Point(double x, double y) {
this.x = x;
this.y = y;
}
@Override
public int compareTo(Point p2) {
if (this.x < p2.x)
return -1;
else if (this.x == p2.x) {
// Secondary order on y-coordinates
if (this.y < p2.y)
return -1;
else if (this.y == p2.y)
return 0;
else
return 1;
} else
return 1;
}
}
/**
* A comparator for comparing points on their y-coordinates. If y-coordinates
* are the same, compare their x-coordinator.
*/
static class CompareY implements java.util.Comparator<Point> {
public int compare(Point p1, Point p2) {
if (p1.y < p2.y)
return -1;
else if (p1.y == p2.y) {
// Secondary order on x-coordinates
if (p1.x < p2.x)
return -1;
else if (p1.x == p2.x)
return 0;
else
return 1;
} else
return 1;
}
}
/** A class to represent two points */
public static class Pair {
Point p1;
Point p2;
public double getDistance() {
/* if (p1 == null || p2 == null)
return Double.MAX_VALUE;
else */
return distance(p1, p2);
}
@Override
public String toString() {
return "(" + p1.x + ", " + p1.y + ") and (" + p2.x + ", " + p2.y + ")";
}
}
}
回溯法與八皇后問題
回溯法(backtyacking)漸進地尋找一個備選方案,一旦確定該被選方案不可能是一個有效方案的時候則放棄掉,繼而尋找一個新的備選方案。
public class EightQueens {
private static final int SIZE = 8;
private int[] queens = {-1, -1, -1, -1, -1, -1, -1, -1};
// check if a queen can be placed at row i and column j
private boolean isValid(int row, int column) {
if (queens[row-i] == column // check column
|| queens[row-i] == column - i // check upleft diagonal
|| queens[row-i] == column + i) //check upright diagonal
return false; // there is a conflict
return true;
}
private int findPosition(int k) {
int start = queens[k] + 1; // search for a new placement
for (int j = start; j < SIZE; j++) {
if (isValid(k, j))
return j; // (k, j) is the place to put the queen now.
}
return -1;
}
public boolean search() {
int k = 0;
while (k >= 0 && k < SIZE) {
int j = findPosition(k);
if (j < 0) {
queens[k] = -1;
k--;
} else {
queens[k] = j;
k++;
}
}
return (k==-1)? false:true;
}
}
附:遞迴解法
public class EightQueens {
private static final int SIZE = 8;
private int[] queens = new int[SIZE]; // queen positions.
// check if a queen can be placed at row i and column j
private boolean isValid(int row, int column) {
for (int i==1; i <= row; i++)
if (queens[row-i] == column // check column
|| queens[row-i] == column - i // check upleft diagonal
|| queens[row-i] == column + i) // check upright diagonal
return false; // there is a conflict
return true;
}
private boolean search(int row) {
if (row == SIZE) // stop condition
return true; // a solution found to place 8 queens in 8 rows.
for (int column=0; column<SIZE; column++) {
queens[row] = column; // place q queen at (row, column)
if (isValid(row, column) && search(row+1))
return true; // found, thus return to exit for loop
}
// no solution for a queen placed at any column of this row
return false;
}
public boolean search() {
return search(0);
}
}