每次敲都有新感受
设计模式
线性表:一对一
1 线性表的顺序存储
void *指针可以指向任意变量的内存空间:
普通的链表:
头文件:
_LinkNode节点结构体LinkNode
各种接口
接口全是void *=LinkList
相应.c文件:具体链表操作
LinkList链表结构体LList
接口的实现:都是对链表结构体的操作
初始化:目的是返回句柄
让C语言支持cpp中的 函数重载:
左孩子右兄弟可以把所有树 变成二叉树 从根节点开始
满二叉树:完美对称
完全二叉树:最后一层 左边... 表达不出来
优点在于:可以用数组表示出来 又可以还原成树 2i 2i+1
非递归遍历:栈 二次压栈 空的也要压进去
先把根压进去 接着循环
栈里 顶是true则输出 NULL则继续弹出 直到弹到一个false
再把false的 两个根节点压进去 此时的false变为了true 弹出
调用顺序栈API
//树专业定义:有且只有一个称为根的节点
//有若干个互不相交的子树 这些树本身也是一个树
//通俗点 树是由节点和边组成,每个节点只有一个父节点但可以有多个子节点
//但有一个节点例外 该节点没有父节点 称为根节点
//叶子节点 没有子节点的节点
//非终端节点 就是非叶子节点
//深度 有几层 从根1开始
//度 子节点的个数
//一般树 任意一个节点的子节点 个数不受限制
//二叉树 任意一个节点 的个数最多两个 且子节点位置不能更改
//一般二叉树
//满二叉树 都是挂两个 组成一个大三角形 高级对称的那个
// 完全二叉树 如果删除了满二叉树最低层 最右边的连续若干个节点 这样形成的二叉树就是完全二叉树
//森林 n个互不相交的树的集合
/*
一般树的存储
双亲表示法
孩子表示法
双亲孩子表示法
二叉树表示法
二叉树的遍历
先序遍历:先访问根节点 再先序访问左子树再先序访问柚子树
已知两种遍历序列求原始二叉树
*/
二叉树节点定义typedef只有3行话(重要的)而遍历也是3行话(不算外套那个if作为退出条件) 马格丹3行情书啊!
叶子个数:3行if 外套一个if作为退出条件.
一旦遇到空就全部回溯
深度:
拷贝二叉树:得释放TreeNode* CopyTree(TreeNode* root)
井号法创建二叉树:得释放TreeNode* CreateBinaryTree()
************************************************************
排序
那个排序的标志位 加的很吊
冒泡
int i, j, temp;
for (j = 0; j < n - 1; j++)
for (i = 0; i < n - 1 - j; i++)
{
if(a[i] > a[i + 1])//if(a[i] > =a[i + 1])这样就会变成不稳定的了
{
temp = a[i];
a[i] = a[i + 1];
a[i + 1] = temp;
}
}
冒泡稳定的根本原因是它是两个相邻元素的比较
直接排序:
序列5 8 5 2 9, 我们知道第一遍选择第1个元素5会和2交换,那么原序列中
2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法
插入排序:
插入排序也是稳定排序,联想冒泡
关键字比较次数永远是n(n-1)/2 记录移动次数最多为3(n-1),最少0次 前者起主导作用,因此实际上时间复杂度还是O(n^2)
void InsertSort(int arr[], int len){
for (int i = 1; i < len; i++){
if (arr[i] <arr[i - 1]){//a[i]
int temp = arr[i];// 缓存a[i] 把小的缓存下来
int j;
for (j = i - 1; j >= 0 && temp <arr[j]; j--){// 把a[i]插入到左边有序序列的合适位置
arr[j + 1] = arr[j];//右移
}
arr[j + 1] = temp;//一旦a[i]小于了某个数或者到头 插进去
}}}
缓存当前值;符合a[i]还是小的条件 则继续右移;一旦大于等于 则插入
希尔排序shell:在插入排序的基础之上 所以也是稳定的? 错是不稳定的
建议画图 12345678910 有三行那个图
跳着分 对每小组插入排序
减小间隔 同上
最后进行 插入排序
希尔排序的时间复杂度是:O(nlogn)~O(n2),平均时间复杂度大致是O(n√n)
算法的复杂度为n的1.2次幂:
希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长
最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,插入排序对于有序的序列效率很高。所以,
希尔排序的时间复杂度会比o(n^2)好一些。
int increasement = len;//10个元素
do{
//每次递减增量
increasement = increasement / 3 + 1; //4 2 1
for (int i = 0; i < increasement; i++)分increasement组
{
for (int j = i + increasement; j < len; j += increasement)//根据增量 分好一组 插入排序
{
if (arr[j] < arr[j - increasement])//a[j]小
{
int temp = arr[j];//缓存a[j]
int k = j - increasement;//与其 前面一个元素比较
for (; k >= i && temp < arr[k]; k -= increasement)//找位置。i也可以写成0
{
arr[k + increasement] = arr[k];//右移
}
arr[k + increasement] = temp;//插入
}
}
}
} while (increasement > 1);
快速排序:
建议画图,1-1024个数(第一个数是511 中间是1) 以511为基准 ......
第一次循环n次 第二次循环2*n/2次 ...... 一共有 log2n 行
所以 平均时间复杂度log2(n)*n
确定一个pivot 排序后 左边的值都小于pivot 右边都大于pivot
再分别对两边 取pivot排序
比如序列为 5 3 3 4 3 8 9 10 11, 现在中枢元素5和3(第5个元素,
下标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序是一个不稳定
的排序算法,不稳定发生在中枢元素和a[j]交换的时刻。
void QuickSort(int arr[], int start, int end){
int i = start;
int j = end;
int pivot = arr[start];
if (i < j)
{
while (i < j)
{
while (i < j&&arr[j] >= pivot){ j--; }
if (i < j){ arr[i] = arr[j]; i++; }
while (i < j&&arr[i] < pivot){ i++; }
if (i < j){ arr[j] = arr[i]; j--; }
}
arr[i] = pivot;
QuickSort(arr, start, i - 1);
QuickSort(arr, i + 1, end);
}
}
6作为基准值 被挖走
6 3 8 2 7 1 4 9 10 11
i <--j
4(a[j])填入6(a[i])的位置:
4 3 8 2 7 1 4 9 10 11
i--> j
i j
---------------------------
8(a[i])填入4(a[j])的位置:
4 3 8 2 7 1 8 9 10 11
i j
只要有替换就--或++
??????????????????????????????????????????、
http://blog.csdn.net/hr10707020217/article/details/8985592
归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2
个序列(1次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并直到
原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小
相等也没有人故意交换,这不会破坏稳定性。那么,在短的有序序列合并的过程中,稳
定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我
们把处在前面的序列的元素保存在结果序列的前面,这样就保证了稳定性。
所以,归并排序也是稳定的排序算法。
归并排序:
void Merge(int arr[], int start, int end, int mid, int tempSpace[]){
int iStart = start;int iEnd = mid;
int jStart = mid + 1;int jEnd = end;
int length = 0;
while (iStart <= iEnd && jStart <= jEnd){//一旦有一个写完到临时空间,就退出来
if (arr[iStart] < arr[jStart]){//排序插入
tempSpace[length] = arr[iStart];
iStart++;
}
else{
tempSpace[length] = arr[jStart];
jStart++;
}
length++;
}
while (iStart <= iEnd){//有一个已经结束了,把另外一个 还没有结束(<=)的直接插入
tempSpace[length] = arr[iStart];
length++;
iStart++;
}
while (jStart <= jEnd){
tempSpace[length] = arr[jStart];
length++;
jStart++;
}
//将辅助空间中有序序列覆盖原空间数据
for (int i = 0; i < length; i ++){
arr[start + i] = tempSpace[i];
}
}
void MergeSort(int arr[], int start, int end, int tempSpace[])
{
if (start == end){
return;
}
//计算中间下标
int mid = (start + end) / 2;
//拆分左半拉
MergeSort(arr, start, mid, tempSpace);
//拆分右半拉
MergeSort(arr, mid + 1, end, tempSpace);
//合并合并两个有序序列
Merge(arr,start,end,mid,tempSpace);
}
***********************************************
堆排序:不需要额外空间
完全二叉树的结构
我们知道堆的结构是节点i的孩子为2 * i和2 * i + 1节点,大顶堆要求父节点大于等于其
2个子节点,小顶堆要求父节点小于等于其2个子节点。在一个长为n 的序列,堆排序的过程
是从第n / 2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之
间的选择当然不会破坏稳定性。但当为n / 2 - 1, n / 2 - 2, ... 1这些个父节点选择元
素时,就会破坏稳定性。有可能第n / 2个父节点交换把后面一个元素交换过去了,而第n / 2 - 1个
父节点把后面一个相同的元素没 有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,
堆排序不是稳定的排序算法。
用428057139
//堆调整:顾名思义,调整爹家族,使得爹的位置是个最大值 内含递归
void HeapAdjust(int arr[],int len, int index){//数组 总长度 爹的位置
int lchild = index * 2 + 1;//都是位置 不要迷
int rchild = index * 2 + 2;
int max = index;//假设爹的位置为最大值的位置
if (index < len){//找出叶子最大值的位置 赋值给max
if (lchild < len && arr[lchild] > arr[max]){
max = lchild;
}
if (rchild < len && arr[rchild] > arr[max]){
max = rchild;
}
if (max != index){
//交换两个元素
MySwap(arr,max,index);//交换使得 爹最大
//递归调整堆
HeapAdjust(arr, len, max); //调整堆 叶子当爹
}
}
}
堆排序:
void HeapSort(int arr[], int len){
//初始化堆
for (int i = len / 2 - 1; i >= 0; i--){//找最后叶子的爹的位置i i从0开始
HeapAdjust(arr,len,i);
}
for (int i = len - 1; i > 0; i --){
//交换头尾两个元素
MySwap(arr, 0, i);//交换后 最后一个元素是最大值 然后递归
//从头部开始调整堆
HeapAdjust(arr,i,0);//使各个爹的位置是个最大值
}
}
初始胡堆的作用是 堆顶是最大值9
所有的递归都可以自行定义stack来解递归
二分查找法:时间复杂度是O(log n)
条件:存储在数组中 有序排列
数组是递增排列,数组中的元素互不相同
int bsearch(int array[], int low, int high, int target)
{
if (low > high) return -1;
int mid = (low + high)/2;
if (array[mid]> target)
return binarysearch(array, low, mid -1, target);
if (array[mid]< target)
return binarysearch(array, mid+1, high, target);
//if (midValue == target)
return mid;
} 此二分的递归是尾递归,它不关心递归前的所有信息。所以它的非递归实现可以不用栈 直接用while
int bsearchWithoutRecursion(int array[], int low, int high, int target)
{
while(low <= high)
{
int mid = (low + high)/2;
if (array[mid] > target)
high = mid - 1;
else if (array[mid] < target)
low = mid + 1;
else //find the target
return mid;
}
//the array does not contain the target
return -1;
}
一个算法中的语句执行 次数 称为语句频度或时间频度。记为T(n)。当n不断变化时,时间频度T(n)也会不断变化
一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),
使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作
T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
T(n)=n2+3n+4与T(n)=4n2+2n+1它们的频度不同,但时间复杂度相同,都为O(n2)。常数是4
若算法中语句执行次数为一个常数,则时间复杂度为O(1)
常见的时间复杂度有:常数阶O(1),对数阶O(log2n),线性阶O(n), 线性对数阶O(nlog2n),平方阶O(n2),
立方阶O(n3),..., k次方阶O(nk),指数阶O(2n)。随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。
复杂度与时间效率的关系:
c < log2n < n < n*log2n < n2 < n3 < 2n < 3n < n! (c是一个常量)
|--------------------------|--------------------------|-------------|
较好 一般 较差
其中c是一个常量,如果一个算法的复杂度为c 、 log2n 、n 、 n*log2n,那么这个算法
时间效率比较高 ,如果是 2n , 3n ,n!,那么稍微大一些的n就会令这个算法不能动了,居
于中间的几个则差强人意。
时间复杂度:O(log2n)
while (i<=n)
i=i*2;
设计模式
线性表:一对一
1 线性表的顺序存储
void *指针可以指向任意变量的内存空间:
普通的链表:
头文件:
_LinkNode节点结构体LinkNode
各种接口
接口全是void *=LinkList
相应.c文件:具体链表操作
LinkList链表结构体LList
接口的实现:都是对链表结构体的操作
初始化:目的是返回句柄
让C语言支持cpp中的 函数重载:
左孩子右兄弟可以把所有树 变成二叉树 从根节点开始
满二叉树:完美对称
完全二叉树:最后一层 左边... 表达不出来
优点在于:可以用数组表示出来 又可以还原成树 2i 2i+1
非递归遍历:栈 二次压栈 空的也要压进去
先把根压进去 接着循环
栈里 顶是true则输出 NULL则继续弹出 直到弹到一个false
再把false的 两个根节点压进去 此时的false变为了true 弹出
调用顺序栈API
//树专业定义:有且只有一个称为根的节点
//有若干个互不相交的子树 这些树本身也是一个树
//通俗点 树是由节点和边组成,每个节点只有一个父节点但可以有多个子节点
//但有一个节点例外 该节点没有父节点 称为根节点
//叶子节点 没有子节点的节点
//非终端节点 就是非叶子节点
//深度 有几层 从根1开始
//度 子节点的个数
//一般树 任意一个节点的子节点 个数不受限制
//二叉树 任意一个节点 的个数最多两个 且子节点位置不能更改
//一般二叉树
//满二叉树 都是挂两个 组成一个大三角形 高级对称的那个
// 完全二叉树 如果删除了满二叉树最低层 最右边的连续若干个节点 这样形成的二叉树就是完全二叉树
//森林 n个互不相交的树的集合
/*
一般树的存储
双亲表示法
孩子表示法
双亲孩子表示法
二叉树表示法
二叉树的遍历
先序遍历:先访问根节点 再先序访问左子树再先序访问柚子树
已知两种遍历序列求原始二叉树
*/
二叉树节点定义typedef只有3行话(重要的)而遍历也是3行话(不算外套那个if作为退出条件) 马格丹3行情书啊!
叶子个数:3行if 外套一个if作为退出条件.
一旦遇到空就全部回溯
深度:
拷贝二叉树:得释放TreeNode* CopyTree(TreeNode* root)
井号法创建二叉树:得释放TreeNode* CreateBinaryTree()
************************************************************
排序
那个排序的标志位 加的很吊
冒泡
int i, j, temp;
for (j = 0; j < n - 1; j++)
for (i = 0; i < n - 1 - j; i++)
{
if(a[i] > a[i + 1])//if(a[i] > =a[i + 1])这样就会变成不稳定的了
{
temp = a[i];
a[i] = a[i + 1];
a[i + 1] = temp;
}
}
冒泡稳定的根本原因是它是两个相邻元素的比较
直接排序:
序列5 8 5 2 9, 我们知道第一遍选择第1个元素5会和2交换,那么原序列中
2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法
插入排序:
插入排序也是稳定排序,联想冒泡
关键字比较次数永远是n(n-1)/2 记录移动次数最多为3(n-1),最少0次 前者起主导作用,因此实际上时间复杂度还是O(n^2)
void InsertSort(int arr[], int len){
for (int i = 1; i < len; i++){
if (arr[i] <arr[i - 1]){//a[i]
int temp = arr[i];// 缓存a[i] 把小的缓存下来
int j;
for (j = i - 1; j >= 0 && temp <arr[j]; j--){// 把a[i]插入到左边有序序列的合适位置
arr[j + 1] = arr[j];//右移
}
arr[j + 1] = temp;//一旦a[i]小于了某个数或者到头 插进去
}}}
缓存当前值;符合a[i]还是小的条件 则继续右移;一旦大于等于 则插入
希尔排序shell:在插入排序的基础之上 所以也是稳定的? 错是不稳定的
建议画图 12345678910 有三行那个图
跳着分 对每小组插入排序
减小间隔 同上
最后进行 插入排序
希尔排序的时间复杂度是:O(nlogn)~O(n2),平均时间复杂度大致是O(n√n)
算法的复杂度为n的1.2次幂:
希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长
最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,插入排序对于有序的序列效率很高。所以,
希尔排序的时间复杂度会比o(n^2)好一些。
int increasement = len;//10个元素
do{
//每次递减增量
increasement = increasement / 3 + 1; //4 2 1
for (int i = 0; i < increasement; i++)分increasement组
{
for (int j = i + increasement; j < len; j += increasement)//根据增量 分好一组 插入排序
{
if (arr[j] < arr[j - increasement])//a[j]小
{
int temp = arr[j];//缓存a[j]
int k = j - increasement;//与其 前面一个元素比较
for (; k >= i && temp < arr[k]; k -= increasement)//找位置。i也可以写成0
{
arr[k + increasement] = arr[k];//右移
}
arr[k + increasement] = temp;//插入
}
}
}
} while (increasement > 1);
快速排序:
建议画图,1-1024个数(第一个数是511 中间是1) 以511为基准 ......
第一次循环n次 第二次循环2*n/2次 ...... 一共有 log2n 行
所以 平均时间复杂度log2(n)*n
确定一个pivot 排序后 左边的值都小于pivot 右边都大于pivot
再分别对两边 取pivot排序
比如序列为 5 3 3 4 3 8 9 10 11, 现在中枢元素5和3(第5个元素,
下标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序是一个不稳定
的排序算法,不稳定发生在中枢元素和a[j]交换的时刻。
void QuickSort(int arr[], int start, int end){
int i = start;
int j = end;
int pivot = arr[start];
if (i < j)
{
while (i < j)
{
while (i < j&&arr[j] >= pivot){ j--; }
if (i < j){ arr[i] = arr[j]; i++; }
while (i < j&&arr[i] < pivot){ i++; }
if (i < j){ arr[j] = arr[i]; j--; }
}
arr[i] = pivot;
QuickSort(arr, start, i - 1);
QuickSort(arr, i + 1, end);
}
}
6作为基准值 被挖走
6 3 8 2 7 1 4 9 10 11
i <--j
4(a[j])填入6(a[i])的位置:
4 3 8 2 7 1 4 9 10 11
i--> j
i j
---------------------------
8(a[i])填入4(a[j])的位置:
4 3 8 2 7 1 8 9 10 11
i j
只要有替换就--或++
??????????????????????????????????????????、
http://blog.csdn.net/hr10707020217/article/details/8985592
归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2
个序列(1次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并直到
原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小
相等也没有人故意交换,这不会破坏稳定性。那么,在短的有序序列合并的过程中,稳
定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我
们把处在前面的序列的元素保存在结果序列的前面,这样就保证了稳定性。
所以,归并排序也是稳定的排序算法。
归并排序:
void Merge(int arr[], int start, int end, int mid, int tempSpace[]){
int iStart = start;int iEnd = mid;
int jStart = mid + 1;int jEnd = end;
int length = 0;
while (iStart <= iEnd && jStart <= jEnd){//一旦有一个写完到临时空间,就退出来
if (arr[iStart] < arr[jStart]){//排序插入
tempSpace[length] = arr[iStart];
iStart++;
}
else{
tempSpace[length] = arr[jStart];
jStart++;
}
length++;
}
while (iStart <= iEnd){//有一个已经结束了,把另外一个 还没有结束(<=)的直接插入
tempSpace[length] = arr[iStart];
length++;
iStart++;
}
while (jStart <= jEnd){
tempSpace[length] = arr[jStart];
length++;
jStart++;
}
//将辅助空间中有序序列覆盖原空间数据
for (int i = 0; i < length; i ++){
arr[start + i] = tempSpace[i];
}
}
void MergeSort(int arr[], int start, int end, int tempSpace[])
{
if (start == end){
return;
}
//计算中间下标
int mid = (start + end) / 2;
//拆分左半拉
MergeSort(arr, start, mid, tempSpace);
//拆分右半拉
MergeSort(arr, mid + 1, end, tempSpace);
//合并合并两个有序序列
Merge(arr,start,end,mid,tempSpace);
}
***********************************************
堆排序:不需要额外空间
完全二叉树的结构
我们知道堆的结构是节点i的孩子为2 * i和2 * i + 1节点,大顶堆要求父节点大于等于其
2个子节点,小顶堆要求父节点小于等于其2个子节点。在一个长为n 的序列,堆排序的过程
是从第n / 2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之
间的选择当然不会破坏稳定性。但当为n / 2 - 1, n / 2 - 2, ... 1这些个父节点选择元
素时,就会破坏稳定性。有可能第n / 2个父节点交换把后面一个元素交换过去了,而第n / 2 - 1个
父节点把后面一个相同的元素没 有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,
堆排序不是稳定的排序算法。
用428057139
//堆调整:顾名思义,调整爹家族,使得爹的位置是个最大值 内含递归
void HeapAdjust(int arr[],int len, int index){//数组 总长度 爹的位置
int lchild = index * 2 + 1;//都是位置 不要迷
int rchild = index * 2 + 2;
int max = index;//假设爹的位置为最大值的位置
if (index < len){//找出叶子最大值的位置 赋值给max
if (lchild < len && arr[lchild] > arr[max]){
max = lchild;
}
if (rchild < len && arr[rchild] > arr[max]){
max = rchild;
}
if (max != index){
//交换两个元素
MySwap(arr,max,index);//交换使得 爹最大
//递归调整堆
HeapAdjust(arr, len, max); //调整堆 叶子当爹
}
}
}
堆排序:
void HeapSort(int arr[], int len){
//初始化堆
for (int i = len / 2 - 1; i >= 0; i--){//找最后叶子的爹的位置i i从0开始
HeapAdjust(arr,len,i);
}
for (int i = len - 1; i > 0; i --){
//交换头尾两个元素
MySwap(arr, 0, i);//交换后 最后一个元素是最大值 然后递归
//从头部开始调整堆
HeapAdjust(arr,i,0);//使各个爹的位置是个最大值
}
}
初始胡堆的作用是 堆顶是最大值9
所有的递归都可以自行定义stack来解递归
二分查找法:时间复杂度是O(log n)
条件:存储在数组中 有序排列
数组是递增排列,数组中的元素互不相同
int bsearch(int array[], int low, int high, int target)
{
if (low > high) return -1;
int mid = (low + high)/2;
if (array[mid]> target)
return binarysearch(array, low, mid -1, target);
if (array[mid]< target)
return binarysearch(array, mid+1, high, target);
//if (midValue == target)
return mid;
} 此二分的递归是尾递归,它不关心递归前的所有信息。所以它的非递归实现可以不用栈 直接用while
int bsearchWithoutRecursion(int array[], int low, int high, int target)
{
while(low <= high)
{
int mid = (low + high)/2;
if (array[mid] > target)
high = mid - 1;
else if (array[mid] < target)
low = mid + 1;
else //find the target
return mid;
}
//the array does not contain the target
return -1;
}
一个算法中的语句执行 次数 称为语句频度或时间频度。记为T(n)。当n不断变化时,时间频度T(n)也会不断变化
一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),
使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作
T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
T(n)=n2+3n+4与T(n)=4n2+2n+1它们的频度不同,但时间复杂度相同,都为O(n2)。常数是4
若算法中语句执行次数为一个常数,则时间复杂度为O(1)
常见的时间复杂度有:常数阶O(1),对数阶O(log2n),线性阶O(n), 线性对数阶O(nlog2n),平方阶O(n2),
立方阶O(n3),..., k次方阶O(nk),指数阶O(2n)。随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。
复杂度与时间效率的关系:
c < log2n < n < n*log2n < n2 < n3 < 2n < 3n < n! (c是一个常量)
|--------------------------|--------------------------|-------------|
较好 一般 较差
其中c是一个常量,如果一个算法的复杂度为c 、 log2n 、n 、 n*log2n,那么这个算法
时间效率比较高 ,如果是 2n , 3n ,n!,那么稍微大一些的n就会令这个算法不能动了,居
于中间的几个则差强人意。
时间复杂度:O(log2n)
while (i<=n)
i=i*2;