算法基础
伪代码:利用简洁的语句,抛去繁杂的语言特征,忽略数据抽象、模块性、和错误处理的问题,表达出算
法的本质
循环不变式
作用:帮助我们理解算法的正确性
关于循环不变式需要证明三条性质:
1.初始化:循环的第一次迭代之前,它为真
2.保持:如果循环的某次迭代之前它为真,那么下次迭代之前它仍为真
3.在循环终止时,不变式为我们提供了一个有用的性质,该性质有助于证明算法是正确的。
其类似与数学归纳法,条件1为基本情况,条件2为归纳,
与数学归纳法不同的是,其有终止条件。
拿插入排序举例,
1.初始化:其刚开始只有一个数,定有序
2.保持:每次迭代都是把一个数插入到有序数列中,使该数类保存有序,所以其有序的性质不改变
3.当其到达其全部数字长度时,迭代终止。
分析算法:
算法的好坏通常是对其最坏情况进行衡量,
假设每条语句都有其对应的代价。对每条语句其在整个程序运行过程中,其用的时间为其代价乘上该条语
句执行的次数,
。把所有语句加起来可以得到一个函数,该函数以输入规模为参数。根据这个函数的形式,可以推断出其
最坏情况下的时间复杂度。例如插入
排序,其所用时间为一个二次的线性函数,在数据相当大的情况下,我们可以只分析其最高次幂。
这里引入增长量级的概念,其只关心函数中最影响时间变化的量,对于一个二次线性函数,其增长量级
为O(n^2),一次函数则为O(n)
分治法
思想:将原问题分解为几个规模较小但类似与原问题的子问题,递归地求解这些子问题,然后再合并这些
子问题的解来建立原问题的解。
分治模式在每层递归时都有三个步骤:
1.分解原问题为若干个子问题,这些子问题是原问题的规模较小的实例。
2.解决这些子问题,递归地求解各个子问题,若子问题的规模足够小,则直接求解
3.合并这些子问题的解成原问题的解
下面上代码(已加注释):
/*************************************************************************
> File Name: MERGE_SORT.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年06月23日 星期四 10时18分01秒
************************************************************************/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
int A[1000];
void MERGE_SORT(int A[],int p,int r);
void MERGE(int A[],int p,int q,int r);
int main(int argc,char *argv[])
{
int n;
printf("please input the number of array:");
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&A[i]);
MERGE_SORT(A,0,n-1);
for(int i=0;i<n;i++)
printf("%3d",A[i]);
return 0;
}
void MERGE(int A[],int p,int q,int r)
{
int L[1000];//临时数组
int R[1000];
int n1=q-p+1;
int n2=r-q;
for(int i=1;i<=n1;i++)//赋值
L[i]=A[p+i-1];
for(int j=1;j<=n2;j++)
R[j]=A[q+j];
L[n1+1]=INF;//最后一个是哨兵,放在数组最后,哨兵为无穷大,不可能被放入A
R[n2+1]=INF;//所以保证了每个数都可以按顺序放入数组A中。
int i=1,j=1;
for(int k=p;k<=r;k++)
{
if(L[i]<=R[j])//哪个小把哪个放入A中
{
A[k]=L[i];
i++;
}
else
{
A[k]=R[j];
j++;
}
}
}
void MERGE_SORT(int A[],int p,int r)
{
if(p<r)//剩下一个数字时不用排序
{
int q=(p+r)/2;//选择分割点
MERGE_SORT(A,p,q);//左边递归排序
MERGE_SORT(A,q+1,r);//有右边递归排序
MERGE(A,p,q,r);//合并
}
}
分治算法的分析:
当一个算法包含对其自身的递归调用时,我们往往可以用递归方程或递归式来描述其运行时间,该方程根
据在较小输入上的运行时间来描述在规模为n的问题上的总运行时间。
对于归并算法的分析:
设归并排序对n个数进行排序最坏情况下运行时间为T(n),当有以个元素时为O(1),下面分解运行时间:
分解:分解计算数组中的中间位置,因此为O(1)
解决:我们递归的求解两个规模为n/2的子问题,将贡献2T(n/2)的运行时间
合并:在n个元素的子数组上合并需要O(n)的时间
其实T(n)=O(n*lg(n))
下面对其进行分析:
事实上归并算法主要耗费的时间是在合并和分解上,我们对于每个问题都求出其分解和合并所耗费的时
间,然后再相加,就得到了其总的时间复杂度
令常量c代表求解规模为1的问题所需要的时间以及在分解步骤和合并步骤处理每个数组元素所需要的时间
则根据每个问题需要处理数的数量,可以的到如下的递归树
则由图可得,对于一个递归树,其每层的总代价为cn其一共有lg(n)+1层,则总代价T(n)为cn*lg(n)+cn
忽略常量c和低阶项,便给出了期望的结果:O(n*lg(n))