目录
一、认识复杂度和简单排序算法
1.1 时间复杂度(忽略了N前面的常数项,只保留幂项)
(1) 直接看N的幂,越小越快(当然)
但对于O(N^2)和O(N)来说,你也不能武断的就判断说后者比前者更好,因为你在表示时间复杂度的时候忽略了常数项,你只能说,当实验进程无限长的时候,后者比前者更好。
因此这个“好”的判断,是在数据样本足够大的时候给予的判断。
(2)对于相同时间复杂度O(N) 无法用理论预估的方式来判断谁好,给予超大的样本量让两个程序去跑,直接比较时间就可以了。
例:B的复杂度没有化简之前是1000N*(a操作)+500
而C是2000N*(b操作)+300
每种操作的固定时间差别导致了无法使用理论预估。
1.2 额外空间复杂度、选择排序、冒泡排序
选择排序:O(N^2) 额外空间复杂度O(1)
冒泡(Bubble 同样的)
**额外空间复杂度
要完成自己设计流程 需要多少额外的空间
如果程序运行过程中,不需要额外的数据结构,只是使用了额外的几个变量。那么额外空间复杂度为O(1);
如果要申请一个和原数组大小一样的数组,那额外空间复杂度为O(n);
如果申请一个是原数组大小一半的数组,那额外空间复杂度为O(n)(因为系数是可以忽略的)
随堂:BubbleSort Code(反正我是没搞明白为什么内嵌的循环只需要n-1,回头再看看)
#include <iostream>
using namespace std;
void Bubble(int n)
{
int temp;
int *arr=new int [n];
for(int i=0;i<n;i++)
{
cin>>arr[i];
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n-1;j++)
{
if(arr[j]>arr[j+1])
{
temp=arr[j+1];
arr[j+1]=arr[j];
arr[j]=temp;}
}
}
for(int i=0;i<n;i++)
{cout<<arr[i]<<" ";}
}
int main()
{
int n;
cin>>n;
Bubble(n);
return 0;
}
随堂:SelectSort Code
#include <iostream>
using namespace std;
void sort(int n)
{
int minIndex=0;
int min=0;
int *arr=new int[n];
for(int i=0;i<n;i++)
{
cin>>arr[i];
}
for(int i=0;i<n;i++)
{
minIndex=i;
min=arr[i];
for(int j=i+1;j<n;j++)
{
if(arr[j]<min)
{
minIndex=j;
min=arr[j];
}
}
int temp=arr[i];
arr[i]=min;
arr[minIndex]=temp;
}
for(int i=0;i<n;i++)
{
cout<<arr[i]<<" ";
}
}
int main()
{
int n;
cin>>n;
sort(n);
return 0;
}
1.3 插入排序及其时间复杂度
说起来比较简单的一类排序
比如 2 3 5 2 3 9 1 0,依照顺序给每个数字标号0—i
考虑顺序为:从0开始,先看0-0上是否排好了,再看0-1号是否排好,再看0-2号是否排好。
实际编写代码的时候,基于前面所有数都已经排列好的情况,只需要考虑最末位的i号数字是否比前面大(小),令j=i-1,则每次比较j和j+1即可
时间复杂度 O(N^2)
插入排序的进行弹性很大,他可能是最坏的情况,每次都要比较到底,这种情况下的计算量为等差数列,显然时间复杂度为O(N^2)。
同时他也可能是最好的情况,每次都只需要看一下就发现根本不需要交换位置,这种情况下的时间复杂度就是O(N)。
那么我们取最坏的情况所以他的时间复杂度为O(N^2),但插入排序显然仍然在常数级别的排序上,比冒泡排序和选择排序优秀的多。
随堂:InsertSort Code
两个注意的地方(我写错的地方)
1、i=1。 i=0的情况必然正确无需讨论
2、j>=0而不是j-1>=0.
#include <iostream>
using namespace std;
void InsertSort(int n)
{
int *arr=new int [n];
for(int i=0;i<n;i++)
{
cin>>arr[i];
}
for(int i=1;i<n;i++)
{
for(int j=i-1;j>=0&&arr[j]>arr[j+1];j--)
{
int temp=0;
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
for(int i=0;i<n;i++)
{
cout<<arr[i]<<" ";
}
}
int main()
{
int n;
cin>>n;
InsertSort(n);
return 0;
}
1.4 二分法的使用和复杂度分析
(1)原理
很简单,一串数字里找一个数字,砍一半,去剩下的一半里找,继续砍一半,直至找到。
那么最差的情况就是全砍完,砍到最后只剩下你要找的那个数,你砍的次数就是你的操作量。
(2)时间复杂度 O(logN)
所以二分法的时间复杂度是log2N(比如说,32个数,2^5你就要砍5次,所以是log2 32)
写作O(logN),默认以2为底,若不是以2为底,则写成O(log n N)(n是那个底数)
(3)适用问题
1)在一个有序数组中,找某个数是否存在
2)在一个有序数组中,找>=某个数最左的位置或找<=某个数最右的位置
3)局部最小值问题