数据结构 - 算法定义
算法就是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每个指令表示一个或多个操作
数据结构 - 算法特性
- 输入输出
- 有穷性
- 确定性
- 可行性
数据结构 - 算法设计要求
- 正确性
- 可读性
- 健壮性
- 时间效率高和储存量低
数据结构 - 算法比较
实现程序:求得1+2+3+......+100结果的程序?
第一种:
int sum,n;
n = 100;
sum = 0;
for (int i=0; i<=n; i++) {
sum += I;
}
printf("%d",sum)
第二种:
int sum = 0,n=100;
sum = (1+n)*n/2;
printf("%d",sum);
- 使用不同算法,解决同一个问题,效率可能相差非常大
- 比如:求第n个斐波那契数(fibonacci number)
0 1 1 2 3 5 8 13 ... 前两个数相加的和
/*
* 0 1 1 2 3 5 8 13 ...斐波那契数
* */
public static int fib1(int n) {
if (n<=1) return n;
return fib1(n-1) + fib1(n-2);
}
/*
* 0 1 2 3 4 5 ... n
* 0 1 1 2 3 5 8 13 ...斐波那契数 O(n)
* */
public static int fib2(int n) {
if (n<=1) return n;
int first = 0;
int second = 1;
for(int i=0;i
- 如何评判一个算法的好坏
//计算1+2+3+...+n的和
public static int sum1(int n) {
int result = 0;
for (int i = 1; i <= n; i++) {
result += I;
}
return result;
}
//计算1+2+3+...+n的和
public static int sum2(int n) {
return (1+n)*n/2;
}
1、如果单从执行效率上进行评估,可以比较不同算法对同一组输入的执行处理时间,这种方案叫做事后统计法,但是有明显的缺点:<1>执行时间严重依赖硬件以及运行时各种不确定的环境因素;<2>必须编写相应的测算代码;<3>测试数据的选择比较难保证公正性
2、一般从以下维度来评估算法的优劣
- 正确性、可读性、健壮性(对不合理输入的反应能力和处理能力)
- 时间复杂度
(time complexity):估算程序指令的执行次数(执行时间)
- 空间复杂度
(space complexity):估算所需占用的存储空间
更多关注的一般是时间复杂度
数据结构 - 常见的时间复杂度
时间复杂度:
- 算法输入时间
- 编译可执行代码
- 执行指令
- 执行重复的指令
时间复杂度术语:
- 常数阶
- 线性阶
- 平方阶
- 对数阶
- 立方阶
- nlog阶
- 指数阶(不考虑)O(2^n)或者O(n!)除非是非常小的n,否则会造成噩梦般的时间消耗。这是一种不切实际的算法时间复杂度,一般不考虑
- 大O表示法(Big O)
1、一般用大O表示法来描述复杂度,它表示的是数据规模n对应的复杂度
2、忽略常数、系数、低阶
3、对数阶的细节:对数阶一般省略底数,log2(n) = log2(9) * log9(n),所以log2(n)、log9(n)统称为logn
9 >> O(1)
2n+3 >> O(n)
n^2 + 2n + 6 >> O(n^2)
4n^3 + 3n^2 + 22n + 100 >> O(n^3)
写法上,n³ = n^3
4、注意:大O表示法仅仅是一种粗略的分析模型,是一种估算,能短时间内了解一个算法的执行效率
public static void test1(int n) {
//1
if (n>10) {
System.out.println("n>10");
}else if (n>5) {
System.out.println("n>5");
}else {
System.out.println("n<=5");
}
//1+4+4+4(i=0,1次;i++,4次;i<4,4次;打印4次)
for (int i = 0; i < 4; i++) {
System.out.println("test");
}
//14 时间复杂度 O(1)
//空间复杂度O(1)
}
public static void test2(int n) {
//1+3n 时间复杂度 O(n)
//空间复杂度O(1)
for (int i = 0; i < n; i++) {
System.out.println("test");
}
}
public static void test3(int n) {
//1+2n+n*(1+3n) = 3n^2+3n+1 O(n^2)
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
System.out.println("test");
}
}
}
public static void test4(int n) {
//1+2n+n*(1+15+15+15) = 48n+1 O(n)
for (int i = 0; i < n; i++) {
for (int j = 0; j < 15; j++) {
System.out.println("test");
}
}
}
public static void test5(int n) {
//执行次数 = log2(n) O(logn)
while ((n = n / 2) > 0) {
System.out.println("test");
}
}
public static void test6(int n) {
//执行次数 = log5(n) O(logn)
while ((n = n / 5) > 0) {
System.out.println("test");
}
}
public static void test7(int n) {
//n = 1*2*2*2...
//执行次数 = 1 + log2(n) + log2(n) + log2(n)*(1+3n) = 1+3*log2(n)+3*nlog2(n)
//O(nlogn)
for (int i = 1; i < n; i += i) {//i+= i等价于i = I*2,
//1 + 3n
for (int j = 0; j < n; j++) {
System.out.println("test");
}
}
}
- 可以借助函数生成工具对比复杂度的大小:函数图像绘制工具
数据结构 - 空间复杂度
算法的空间复杂度通过计算算法所需的存储空间实现,算法空间复杂度的计算公式记做:S(n) = n(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数
数据结构 - 程序空间计算因素:
1.寄存本身的指令
2.常数
3.变量
4.输入
5.对数据进行操作的辅助空间
在考量算法的空间复杂度,主要考虑算法执行时所需要的辅助空间。
问题:数组逆序,将一维数组a中的n个数逆序存放在原数组中。
void exangeArr(){
int n = 10;
int a[10] = {1,2,3,4,5,6,7,8,9,10};
//算法实现(1) O(1)
int temp;
for (int i=0; i
fib函数(斐波那契数列)的时间复杂度分析
第一种递归算法:
1 + 2 + 4 + 8 = 20+21+22+23 = 2^4-1 = 2^(n-1)-1 = 0.5*2^n。
所以复杂度是O(2^n).
第二种算法 : O(n)
算法的优化方向
- 用尽量少的存储空间
- 用尽量少的执行步骤(执行时间)
- 根据情况,可以空间换时间或者时间换空间
多个数据规模的情况
O(n+k)
public static void test(int n,int k) {
for (int i = 0; i < n; i ++) {
System.out.println("test");
}
for (int i = 0; i < k; i ++) {
System.out.println("test");
}
}
leetcode
- 一个用于练习算法的好网站
leetcode英文版
leetcode中文版