总结:
时间复杂度:衡量算法快慢的标准刻度
在CPU单位时间运行指令数恒定的情况下,求算法运行指令数和数据规模n的关系 F(n)
不求甚解
大O渐进法:
1)只保留最高次项
2)最高次项的系数化为1
时间复杂度不是绝对意义上的快慢,运行时间的变化趋势。
常见的时间复杂度:
O(1) O(log(n)) O(n) O(n*log(n)) O(n^2)
特殊的时间复杂度:
二分查找
递归函数--画框框、数框框 了解
空间复杂度
大O渐进表示法
数据规模是n的情况下,
1)额外使用的空间大小(不考虑输入/输出中用到的空间)
2)常见形式
i. int[] array=new int[和n的关系];
int[] array=new int[255]; S(n)=O(n)
ii.递归函数 调用栈
一、数据结构与算法
数据结构:存储、组织数据的形式。
算法:一系列步骤。
二、复杂度
1.含义
是衡量算法好坏的刻度尺(标杆)
2.衡量的角度:
快/慢--时间复杂度 ****
使用空间的角度(内存)--空间复杂度
如:快慢:冒泡排序 其它排序
3.拿运行时间衡量怎么样?
不好 原因:1)事后衡量 2)影响因素太多(如当时系统上打开程序的多少)
4.衡量算法快慢的标准
是算法运行的指令个数(基本指令)。这里有个前提,即:CPU每秒运行的指令数是恒定的。
5.大O渐进法
数据规模 n
for(int i=0;i<n;i++){}
运行指令个数F(n) 和数据规模n有关。n越大,F(n)越大。
衡量算法好坏,不需要精确衡量。-->大O渐进表示法
O(F(n))=
1)只保留最高次项
2)最高次项的系数变为1
//在array数组中查找value
int search(int[] array,int value){
for(int i=0;i<array.length;i++){
if(array[i]==value){
return i;
}
}
return -1;//没有找到
}
3 5 2 7 9 6 4
最好 平均 最坏
用平均/最坏来衡量
6.两个例子
例1:
void count(int m,int n){
int count=0;
for(int i=0;i<m;i++){
count++;
}
for(int i=0;i<n;i++){
count++;
}
System.out.println(count);
}
O(n)=O(m+n)
例2:
int String.indexOf(int ch);
数据规模:String的长度 O(n)
7.常见的时间复杂度
O(1)
O(log(n))
O(n)
O(n*log(n))
O(n^2)
8.递归函数的时间复杂度
做法:画框框,找框框的个数
例1:n的阶乘
long Factorial(int n){
return n>2?Factorial(n-1)*n:n;
}
这个递归函数有两个出口:
1)n=1或n=2-->返回1或2
2)n>2-->求(n-1)! 递归n次。
因此,T(n)=O(n)
例2 斐波那契数列
long Fabonacci(int N){
return N<2?N:Fabonacci(N-1)+Fabonacci(N-2);
}
两个出口:
1)N=1-->返回1
2)N>=2-->求Fabonacci(N-1)+Fabonacci(N-2)
举例说明:
三、冒泡排序和二分查找的时间复杂度
1.冒泡排序(升序)
冒泡过程:两个相邻的数依次比较,保证大的数在后面。
减治算法:每次处理都减少一个数,剩下的数用同样的方式处理。冒泡的过程就相当于减治算法。
外部循环表示比较的轮数,共比较array.length次。更优化的方式是array.length-1。
//冒泡排序
public static void bubbleSort(int[] array) {
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length - 1 - i; j++) {
//保证相邻的两个数,最大的在后面。
if (array[j] > array[j + 1]) {
int temp = array[j];
array[j + 1] = array[j];
array[j] = temp;
}
}
}
}
时间复杂度:F(n)=(n-1)+(n-2)+....+1=(n-1+1)(n-1)/2=(n^2-n)/2,因此冒泡排序的时间复杂度为O(n)=n^2。
冒泡排序的优化
每次冒泡之前,假设数组已经有序。如:2 3 4 5 6 7 9。可以在每轮排序前设置标志位假设已经是有序的,经过一轮排序后,如果标志位未发生变化,则说明整个数组是有序的,就不必进行后面的排序了。
for(int i=0;i<array.length;i++){
//每次冒泡之前,假设数组已经有序。
int sorted=true;
//一次冒泡的过程,保证最大的数被推到最后。
for(int j=0;j<array.length-1-i;j++){
//保证相邻的两个数,最大的在后面。
if(arr[j]>arr[j+1]){
int temp=arr[j];
arr[j+1]=arr[j];
arr[j]=temp;
sorted=false;
}
}
//如果过程一次交换都没有发生过,假设有序成立。
if(sorted==true){
break;
}
}
2、二分查找
前提:数组有序
查找方式:两种方式:[left,right) [left,right](其中, left=区间的左边界 right=区间的右边界)
中间位置的下标:int m=(left+right)/2
[left,right) 区间内只有一个数:right=left+1 没有数 left==right
//二分查找
[left,right]
public static int binarySearch1(int[] array, int value) {
int left = 0;
int right = array.length - 1;//5 []
while (left <= right) {
int mid = left + (right - left) / 2;
if (array[mid] == value) {
return mid;
}
//说明value在(mid,right],那么left=mid+1
if (array[mid] < value) {
left = mid + 1;
}
//说明value在[left,mid),那么right=mid-1
if (array[mid] > value) {
right = mid-1;
}
}
return -1;
}
//[left,right)
public static int binarySearch2(int[] array,int value){
int left=0;
int right=array.length;
//区间内还有一个数,就继续找,没有数就可以停下。
while(left<right){
int m=left+(right-left)/2;
if(value==array[m]){
return m;
}
if(value>array[m]){
left=m+1;
}
if(value<array[m]){
right=m;
}
}
return -1;
}
二分查找的时间复杂度:
长度变化:n n/2 n/4 ..... 1
1*2*2*2*...*2=n ->2^a=n-->a=log(n)
二分查找的时间复杂度T(n)=O(log(n))