马上要期末考试了,来写点博客吃吃吧,哦不学学吧
分治法的三点:
1.原问题能够分解成为若干个规模小,相互独立,与原问题类型相同的子问题。
2.子问题足够小可以解决
3.能够将子问题的解组合成原问题的解。
算法分析:时间复杂度的递推公式为
T(n)=aT(n/b)+cn^k;
T(1)=c;
有待补充!
分治法求最大元最小元问题
void findmaxmin(int i,int j,int max,int min)
{
if(i==j)
max=min=a[i];
if(i==j-1)
{
if(a[i]>a[j])
{
max=a[i];
min=a[j];
}
else
{
max=a[j];
min=a[i];
}
}
int m=(i+j)/2;
findmaxmin(i,m,max,min);
findmaxmin(m+1,j,max1,min1);
if(max<max1) max=max1;
if(min<min1) min=min1;
}
最好最坏都运行3n/2-2次(推倒过程有待考量)。但是在运行时,由于使用递归的办法,空间消耗(效率也比较低下)会比较大,未必是有利的,所以说在数目很小的情况下,不建议使用递归算法,但是比较希望得到相对应得迭代算法。
二分搜索
需要注意的是对半搜索要求随机存取位于划分点m处的元素,所以一般使用顺序表,而不是链接储存。
二叉搜索树
性质1.具有n个内节点的对半二叉搜索树的左孩子有(n-1)/2的向下取整节点,右孩子有n/2向下取整个
节点。
根据定义可以证明此点的正确性。
性质2:n个节点的二叉判定树的高度至少是n/2向下取整。
证明:因为高度为h的二叉树至少有2^h-1个节点(??)
性质3:若n=2^h-1 则对半搜索二叉树一定是满二叉树。
性质4:若搜索树是满二叉树,则外节点一定在h+1层,否则要么在h层,要么在h+1层。
性质5:对半搜索算法在成功搜索的情况下,关键字之间的比较次数不超过logn(向下取整)+1即不会超过判定树的高度,显而易见(最坏的情况)
平均时间复杂度应该是logn(??)
排序
两路合并排序,两个数组每次输出最小值,时间复杂度是o(n)
运用递推的合并排序算法,结果是o(nlogn)
快速排序,在之前一篇算法基础博客中已经提到了
所以就不再赘述原理了
下面讨论快速排序的时间复杂度,快速排序的时间复杂度最优便是 O(nlogn)即每一个序列恰好可以排成两份,最坏时间复杂度就是有一方是空集的情况哈,这时候的时间复杂度就是O(n)了,所以快速排序不太稳定。
快速排序的代码如下:
int parition(int left int right)
{
int i,j;
int i=left;
j=right;
do
{
do i++;
while(a[i]<a[left])
do j--;
while(a[j]>a[left])
if(i<j)
swap(i,j);
}while(i<j)
swap(left,i);
return j;
}
void quicksort(int left,int right)
{
if(left<right)
{
int t=parition(left,right);
quicksort(left,t-1);
quicksort(t+1,right);
}
}
改善快速排序性能的办法
1)改变主元法:三种选择主元的办法,选取中间值(left+right)/2作为主元。
2))选取left~right之间的随机数作为主元,这个会增加随机函数的时间复杂度
3))选取left right left+right的中间值的三者的平均值作为主元。
2)在数列划分足够小时,可以改变算法的办法,使用直接排序法可以进行直接的计算
3)递归算法的效率不是很高,所以我们可以一个堆栈对数组(这个具体实现过程,我会进一步搞定的)。
降低空间复杂度的办法,就是降低进栈的过程。
可以只进一部分栈,对另一部分进行操作,时间复杂度就是O(logn)
分治法求解第k小元素的问题:
步骤1:随机选择主元,若随机选的主元最终位置为j,j恰好等于k,那么我们就可以直接解决,如果不是就可以使用二分法继续往下搜索。
具体代码如下:
void sortlist(int left,int right)
{
do
{
//随机选择主元
int j=rand()%(right-left+1)+left;
swap(left,j);//交换主元的位置,将其置于0的位置;
int t=parition(left,rigth) ;//确定交换好的parition的位置,然后进行计算。
if(k==t+1)
flag=true;
if(k>t+1)
right=t;
if(k<t+1)
left=t+1;
}while(flag==true);
}
这就是寻找第n个元素的算法,那么这个算法的最坏时间复杂度是O(n^2).最好是O(n)所以
还需要改进,改成线性时间的选择算法,
采用二次取中法,对算法进行计算
二次取中法的实现原则如下:首先将n个数据平均分成n组,将每一组选区一个中间值,再从选取的中间值中再选取中间值,
举个例子
序列:41 76 55 19 59 63 12 47 67 45 26 74 33 18 65 86 49 77 35 80 53 19 97 22 52 62 39 60 59 29 72 31 56 91 76
首先将这些数组按每七个一组划分成五组
1:41 76 55 19 59 63 12
2:47 67 45 26 74 33 18
3:65 86 49 77 35 80 53
4:19 97 22 52 62 39 60
5:59 29 72 31 56 91 76
第一步调用直接插入排序法找到中间值
1:55
2:45
3:65
4:60
5:59
并且把他们交换到最前端,同时将数组进行合并,得到画线后的七个中间值。
后求出中间值以59为主元进行分划操作,发现59位于下标为7的地方所以说,需要在左边进行深度搜索,再调用一遍函数,在此区间内进行搜索一直到搜到为止。
时间分析就是O(n)的算法。
代码:
int Select(int left,int right,int k)
{
//首先如果数据很小就可以使用直接插入排序
if(n<=maxm)
{
Insertsort(left,right);
return left+k-1;//直接就可以求得结果了
}
for(int i=1;i<=n/r;i++)
{
Insertsort(left+(i-1)*r,left+i*r-1);
swap(left+(i-1)*r,left+(i-1)*r+Ceil(r,2)-1);
}
int j=Select(Ceil(n/r,2),left,left+(n+r)-1);//u1s1不太明白 大概就是因为你把所有的都放在了最前面,所以选取的时候,从左边往后选择
Swap(left,j);
j=parition(left,right);
if(k==j-left+1)
return j;
else if(k>j-left+1)
return Select(j+1,right,k);
else
return Select(left,j-1,k-(j-left+1));
}
斯特拉森矩阵乘法
普通的矩阵乘法他的时间复杂度是O(n^3),所以我们需要降低一个时间复杂度。
分治法在矩阵乘法中的应用大概就在于分块进行计算。
斯特拉森矩阵乘法将时间复杂度降低为了O(n^2.81)虽然说也不太快,但是至少提供了一个更加有用的数据计算方法。
课后习题与总结,将会在后面给出。
终于写完了啊啊啊啊啊啊啊啊啊
我好累啊!!