1. Getting Started
1.1 Insertion-Sort
Insertion Sort
public class InsertSort {
private static ArrayList array = new ArrayList<>();
private static void insertSort(ArrayList array) {
System.out.println(array);
for(int i = 1; i < array.size(); i ++) {
int value = array.get(i);
int j = i - 1; // 对i左边的部分进行循环
while(j >= 0 && array.get(j) > value) {
array.set(j + 1, array.get(j)); //如果j位置上的数值大于value,则把j位置上的数向右移动一个
j = j - 1; // 继续向左
}
array.set(j + 1, value); // 跳出循环所得到的数j就是待插入位置左边的位置
}
System.out.println(array);
}
public static void main(String[] args) {
array.add(4);
array.add(5);
array.add(4);
array.add(3);
array.add(2);
insertSort(array);
}
}
1.2 Loop Invariants
如果一个算法要实现正确性,则其必须满足循环不变性
Initialization
: 第一次循环必须正确
Maintenance
: 如果第一次循环前是正确的,那么下一次循环也保持正确
Termination
: 当循环结束的时候,结果是正确的
1.3 Pseudocode Conventions
- 缩进就是块结构
- 循环结构
while
,for
, andrepeat-until
和条件结构if-else
- 符号
“//”
意味着注释 -
i = j = e
表示i
和j
同时赋值e
- 没有特殊声明的话,所有的变量都是局部变量
- 以
A[i]
这种形式来表示访问数组A的第i个元素 - 以
objects.attr
这种形式访问对象的属性 - 以传值的方式传参
-
return
语句立即返回,不带任何值 -
error
关键字代表因为条件错误导致的错误发生
1.4 Analyzing algorithms
- 输入大小:不同的问题有不同的描述方式,可以是一个输入数组中元素的个数,也可以是点和边共同组成
- 运行时间:基础运算符和步骤的数量
- Order of Growth:
Selection Sort
import java.util.ArrayList;
public class SelectionSort {
public static void selectionSort(ArrayList array) {
// 1. find the biggest element in the unsortedArray
// 2. exchange it with A[i]
for(int i = 0; i < array.size(); i ++) {
int max_v = array.get(i);
int max_v_index = i;
for(int j = i; j < array.size(); j ++) {
if(array.get(j) > max_v) {
max_v = array.get(j);
max_v_index = j;
}
}
int temp = array.get(i);
array.set(i, max_v);
array.set(max_v_index, temp);
}
System.out.println(array);
}
public static void main(String[] args) {
ArrayList array = new ArrayList<>();
array.add(4);
array.add(5);
array.add(4);
array.add(3);
array.add(2);
selectionSort(array);
}
}
2. Designing algorithms
- Incremental Approach
- Divide and Conquer: Divide the problem into subproblems and conquer them straightforward
2.1 Merge Sort
public class MergeSort {
private static List mergeSort(List arrayList) {
if(arrayList.size() == 1) {
return arrayList;
}
// 1. divide the problem into sub-problems
List leftArray = arrayList.subList(0, arrayList.size() / 2);
List rightArray = arrayList.subList(arrayList.size() / 2, arrayList.size());
// 2. conquer them respectively
leftArray = mergeSort(leftArray);
rightArray = mergeSort(rightArray);
// 3. merge the sub-array into a completed one
return merge(leftArray, rightArray);
}
private static List merge(List leftArray, List rightArray) {
ArrayList arrayList = new ArrayList<>();
int leftArrayIndex = 0;
int rightArrayIndex = 0;
while(leftArray.subList(leftArrayIndex, leftArray.size()).size() > 0 && rightArray.subList(rightArrayIndex, rightArray.size()).size() > 0) {
if(leftArray.get(leftArrayIndex) > rightArray.get(rightArrayIndex)) {
arrayList.add(leftArray.get(leftArrayIndex));
System.out.println("加入" + leftArray.get(leftArrayIndex) + "之后" + arrayList);
leftArrayIndex ++;
} else {
arrayList.add(rightArray.get(rightArrayIndex));
System.out.println("加入" + rightArray.get(rightArrayIndex) + "之后" + arrayList);
rightArrayIndex ++;
}
}
if(leftArray.size() > 0) {
arrayList.addAll(leftArray.subList(leftArrayIndex, leftArray.size()));
}
if(rightArray.size() > 0) {
arrayList.addAll(rightArray.subList(rightArrayIndex, rightArray.size()));
}
System.out.println(arrayList);
return arrayList;
}
public static void main(String[] args) {
ArrayList array = new ArrayList<>();
array.add(1);
array.add(5);
array.add(4);
array.add(3);
array.add(2);
mergeSort(array);
}
}
2.2 Analysis of Divide and Conquer Algorithm
- a:
sub-problem
的数量 - b: 原来问题与
sub-problem
大小的比值
课后练习2.3 -4
public class RecurrenceInsertionSort {
private static List array;
private static void recurrenceInsertionSort(List arrayList, int n) {
if(n == 0) {
return;
} // n指向当前处理的元素的索引,当为0的时候就退出,即不处理指向第一个数的那一轮
recurrenceInsertionSort(arrayList, n - 1); // 建立递归树
int i = n - 1; //
int key = (int)arrayList.get(n);//当前元素
// 当当前元素前的元素大于当前元素,那么把它们都移动位置,直到没有大于当前元素,那么也就保存了应该插入的位置i
while(i > 0 && (int)arrayList.get(i) > key) {
arrayList.set(i + 1, arrayList.get(i));
i --;
}
//把key(当前元素)插入合适的位置
arrayList.set(i + 1, key);
}
public static void main(String[] args) {
array = new ArrayList<>();
array.add(1);
array.add(5);
array.add(7);
array.add(3);
array.add(2);
recurrenceInsertionSort(array, 4);
for(Object o : array) {
System.out.println(o);
}
}
}
课后练习2.3 -5
public class BinarySearch {
private static boolean binarySearch(List arrayList, int value) {
int mid_index = arrayList.size() / 2;
int mid_value = arrayList.get(mid_index);
if(value == mid_value) {
System.out.println("找到了" + value + arrayList);
return true;
}
// 1. compare
if(value < mid_value) {
if(arrayList.size() == 1 && arrayList.get(0) != value) {
return false;
}
return binarySearch(arrayList.subList(0, mid_index), value);
} else {
if(arrayList.size() == 1 && arrayList.get(0) != value) {
return false;
}
return binarySearch(arrayList.subList(mid_index, arrayList.size()), value);
}
}
public static void main(String[] args) {
List array = new ArrayList();
array.add(1);
array.add(2);
array.add(4);
array.add(6);
array.add(7);
System.out.println(binarySearch(array, 3));
}
}
3. Growth Of Functions
3.1
- g(n) is an asymptotically tight bound for f(n)
- f(n) is asymptotically nonnegative
3.2
- asymptotic upper bound
- 表示存在一个的上界
3.3
- asymptotic lower bound
3.4 Theorem 1
For any two functions and , we have = only if and
3.5
3.6
3.7 Comparing Functions
3.7.1 Transitivity
3.7.2 Reflexivity
3.7.3 Symmertry
3.7.4 Transpose Symmetry
3.8 Standard Notations and Common Functions
- Monotonicity
- Floors and Ceilings
- Modular Arithmetic
- Polynomials
- Exponentials
- Logarithms
- Factorials
- Functional Iteration
- Fibonacci numbers
4. Divide-and-Conquer
recursive case
base case
有时会处理与原问题类型不同的问题,把这些问题放在conquer步骤中解决
4.1 Find Maximum Subarray
import java.util.ArrayList;
import java.util.List;
public class FindMaximumSubArray {
private static List findMaximumSubArray(List array, int low, int high) {
if(low == high) {
ArrayList arrayList = new ArrayList<>();
arrayList.add(low);
arrayList.add(high);
arrayList.add(array.get(low));
return arrayList;
} else {
int mid = (low + high) /2;
List leftArrayList = findMaximumSubArray(array, 0, mid);
int leftSum = leftArrayList.get(2);
List rightArrayList = findMaximumSubArray(array, mid + 1, high);
int rightSum = rightArrayList.get(2);
List crossArrayList = findMaximumCrossingArray(array, low, mid, high);
int crossSum = crossArrayList.get(2);
if(leftSum >= leftSum + rightSum && leftSum + rightSum >= crossSum) {
return leftArrayList;
} else if(rightSum >= rightSum + leftSum && rightSum + leftSum >= crossSum) {
return rightArrayList;
} else {
return crossArrayList;
}
}
}
private static List findMaximumCrossingArray(List array, int low, int mid, int high) {
int leftSum = 0;
int sum = 0;
int leftIndex = 0;
int rightIndex = 0;
List retList = new ArrayList<>();
for(int i = mid; i >= low; i --) {
sum += array.get(i);
if(sum > leftSum) {
leftSum = sum;
leftIndex = i;
}
}
int rightSum = 0;
sum = 0;
for(int j = mid + 1; j <= high; j ++) {
sum += array.get(j);
if(sum > rightSum) {
rightSum = sum;
rightIndex = j;
}
}
retList.add(leftIndex);
retList.add(rightIndex);
retList.add(leftSum + rightSum);
return retList;
}
public static void main(String[] args) {
List array = new ArrayList<>();
array.add(1);
array.add(5);
array.add(-7);
array.add(3);
array.add(5);
array.add(-2);
array.add(-9);
array.add(4);
System.out.println(findMaximumSubArray(array, 0, 7));
}
}
5. 求解递归式
5.1 Substitution Method
5.1.1 The Substitution Method for Solving Recurrences
证明的时候需要
- 假设一个可能的解
- 带入证明成立
- 如果在2中遇到了一些边界值,需要证明存在性就行
本质上数学归纳法,需要证明base case和Inductive case
康奈尔大学笔记:Using the substituion and master methods
5.1.2 Making a Good Guess
- If a recurrence is similar to one you have seen before, then guessing a similar
solution is reasonable - prove loose upper and lower bounds onthe recurrence and then reduce the range of uncertainty
5.2 The Recursion-tree method
- 用Recursion-tree method生成可能解,然后用代入法来验证
分治法 ( Divide And Conquer ) 详解
5.3 The Master Method
- 1和2,2和3之间都有gap
- Master Method不能涵盖所有的情况
6. Probabilistic Analysis and Randomized Algorithms
Probabilistic analysis
最好/最坏/平均分析:由于输入的分布不同,因此产生了这三种情况
randomized algorithm:算法表现不仅仅依赖于输入,还依赖于随机数生成
6.1 Indicator Random Variables
- 帮助概率与期望之间的转换
6.1 Randomized Algorithm
Lemma3
The expected hiring cost of the procedure RANDOMIZED-HIRE-ASSISTANT is
- 随机化算法将输入的序列重新排列,使得算法不依靠输入序列的分布,让每一种分布都有可能