什么是算法
- 有限性 (某些局限条件)
- 确定性 (不会产生二义性)
- 可行性
- 输入
- 输出
线性查找法
public static <E> int search(E[] data, E target) {
for (int i = 0; i < data.length; i++) {
if (data[i].equals(target)) {
return i;
}
}
return -1;
}
循环不变量
在循环过程中保持不变的逻辑, 即 -> data[0...i-1]中没有找到目标
for (int i = 0; i < data.length; i++) {
/*
循环体: 维持循环不变量
*/
if (data[i].equals(target)) {
return i;
}
}
复杂度分析:表示算法的性能
public static <E> int search (E[] data, E target) {//通常看最差的情况来判断一个算法的复杂度
for (int i = 0; i < data.length; i++) { //算法运行的上界
if (data[i].equals(target)) { //n = data.length
retuern i;
}
}
return -1;
} //O(n)
假如算法需要运行 T = c1 * n + c2 行逻辑代码
则该算法的复杂度为 O(n) 常数不重要
复杂度描述的是随着数据规模n的增大,
算法性能的变化趋势
| T1 = 1000n | T2 = 2n^2 | ||
|---|---|---|---|
| 复杂度 | O(n) | O(n2) | |
| 对比 | O(n) < O(n2) | ||
| 一定存在某一临界点n0,当n >= n0, T1 < T2 |
常见算法复杂度
一个n*n的二维数组, 明确 n 所代表的含义
//这是一个复杂度为O(n^2)的算法, n 代表二维数组的某一个维度为n
for (int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
//循环到A[i][j]
}
}
//遍历一个a*a的二维数组, 复杂度为O(n)的算法, n代表二维数组中总元素数量
for(int x = 0; x < a; x++) {
for(int y = 0; y <a; y++) {
//循环到A[x][y]
}
}
数字n 的二进制位数
while (n) {
n %2; //n的二进制中的一位
n /= 2
}
时间复杂度为O(logn) (实际为以2为底,n为值的log函数, 但是在复杂度分析中常数不重要,所以认为复杂度为O(logn), 这类算法不关注对数函数中的 底 是多少,统称这类算法的复杂度为logn
被称为对数级别的复杂度)
看一个 算法的复杂度不能简单的去数循环的个数,而应该根据判断的逻辑来定义
数字n 的所有公约数
//该算法复杂度为O(n)
for (int i =0; i <= n; i++) {
if (n % i == 0) {
//i是n的一个约数
}
}
//另外一个思路是,实际上当 i * i <= n时,应该已经把 n 的所有公约数都得到了
该算法的时间复杂度为O(√n)
for () {
if(int i = 0; i * i <= n; i++) {
//i和n/i是n的两个公约数
}
}
长度为n的二进制数字 O(2^n)
长度为n 的数组的所有排序 O(n!)
复杂度 O(n!) > O(2^n),当n>2时, n! > 2^n
数字n 是否是偶数
return n % 2 ==0 //复杂度为O(1)
复杂度排序
O(1) < O(㏒n) < O(√n) < O(n) < O(n* ㏒n) < O(n²) < O(2^n) < O(n!)