3. 数据结构程序(略过)
4. 编写正确的程序
1.在返回值p中返回元素t在数组x中第一次出现时的位置。(如果t在数组中多次出现的话,原先的算法所返回的是众多位置中的任意一个)。代码应该对数组元素进行对数次比较,可能要进行logn次这样的比较才能完成二分查找
补充:(临时想到的一个题目)
查找一个数组中出现次数超过数组元素总数一半的数字
1. 是否排序?
如果是排序的序列,那么n/2位置的元素就是要找的元素。
没有排序,则较复杂。
2.使用先排序,后查找的方法。
时间复杂度 O(nlogn): 快速排序之后,直接取数组的中间元素即可。
3.统计元素的方法
将数组中的所有元素统计一遍,那么需要额外的内存,最多时需要O(n/2)
4.采用计数法:
设置一个目标元素target,与目标元素的计数counter:碰到与target相同的元素则将计数器counter加1,遇到与target不同的元素,counter减1。如果counter小于0,则将下一个元素置为目标元素。
因为有一个元素占数组一半,因此总会找到target,其counter大于0。
2.二分查找基本上比顺序查找更快一些:如果要在一个具有n个元素的表中进行查找的话,二分查找粗略说来需要进行log n次比较,而顺序查找粗略说要进行n/2次比较,虽然二分查找通常已经够快了,在少数情况下,查找还必须更快一些,尽管在算法上你不能减少比较的逻辑次数,但是你能否重写二分查找代码,使它更快些吗?为了明确起见,假设你需要在一个具有1000个整数的排序表中进行查找。
3.使用C/C++语言编写下面的递归二分查找,并证明其正确性。注:单独使用这个函数,不要调用任何其他的递归函数。
intbinarysearch(DataType x[], int n, DataType t)
{
if(x== NULL)
{
return - 1;
}
intmid = n / 2;
if(x[mid] == t)
{
return mid;
}
else if(x[mid] < t)
{
binarysearch(x, mid, t);
}
else if(x[mid] > t)
{
binarysearch( x+mid+1, n– mid - 1, t);
}
}
5. 编程中的次要问题(略)
6. 性能透视
性能优化主要从如下的几个方面进行:
1.算法和数据结构
2.代码优化
3.系统软件:包括更新数据库,不同操作系统,以及开启编译器优化等
4.硬件:运行速度更快的硬件可以提高性能
7. 性能估计(略)
8. 算法设计
问题:输入时一个具有n个浮点数的向量x;输出时在输入的任何相邻子向量中找出的最大和。
解法1:完成该任务时迭代所有满足0 <= i <= j < N 的i 和j整数对,对每一个i和j整数对都计算x[i…j]的总和,并检查总和是否大于迄今为止的最大总和。
#include <iostream>
#define N 10
using namespace std;
int main()
{
int array[10] = {31, -41,59, 26, -53, 58, 97, -93, -23, 84};
int maxsofar = 0;
for( int i = 0; i < N;i++)
{
for(int j = 0; j <N; j++)
{
int sum = 0;
for(int k = i; k<= j; ++k)
{
sum +=array[k];
}
maxsofar =max(maxsofar, sum);
}
}
cout << "thesum of subarray sum: " << maxsofar << endl;
return 0;
}
解法2:在两次循环时,将所有的可能的情况都遍历完,求出其中最大的一项。
#include <iostream>
#define N 10
using namespace std;
int main()
{
int array[10] = {31, -41,59, 26, -53, 58, 97, -93, -23, 84};
int maxsofar = 0;
for( int i = 0; i < N;i++)
{
int sum = 0;
for(int j = i; j <N; j++)
{
sum += array[j];
maxsofar = max(maxsofar, sum);
}
}
cout << "thesum of subarray sum: " << maxsofar << endl;
return 0;
}
解法3:仍然是二次算法,通过访问在外部循环执行之前就构建的数据结构的方式进行。
在计算之前,首先计算一个累加和序列,这样可以通过两个元素差,求出i ~ j的子序列和。
#include <iostream>
#define N 10
using namespace std;
int main()
{
int array[10] = {31, -41,59, 26, -53, 58, 97, -93, -23, 84};
int temp[N+1];
int * cumarr =&temp[1];
cumarr[-1] = 0;
for( int i = 0; i < N;i++)
{
cumarr[i] =cumarr[i-1] + array[i];
}
int maxsofar = 0;
for( int i = 0; i < N;i++)
{
for(int j = i; j <N; j++)
{
int sum =cumarr[j] - cumarr[i-1];
maxsofar = max(maxsofar, sum);
}
}
cout << "thesum of subarray sum: " << maxsofar << endl;
return 0;
}
解法4:采用二分法的方法进行计算。
#include <iostream>
#define N 10
using namespace std;
int maxsum(int array[], int l, int u)
{
if(l > u)
{
return 0;
}
if(l == u)
{
return max(0,array[l]);
}
int m = (l + u) / 2;
int lmax = 0, sum = 0;
for( int i = m; i >= l;i--)
{
sum += array[i];
lmax = max(lmax, sum);
}
int rmax = sum = 0;
for( int i = m+1; i <=u; i++)
{
sum += array[i];
rmax = max(rmax, sum);
}
return max( lmax + rmax,max(maxsum(array, l, m), maxsum(array, m+1, u)));
}
int main()
{
int array[10] = {31, -41,59, 26, -53, 58, 97, -93, -23, 84};
int maxsofar =maxsum(array, 0, N-1);
cout << "thesum of subarray sum: " << maxsofar << endl;
return 0;
}
算法4:扫描算法,从最左端(元素x[0])开始,一直扫描到最右端(元素x[n-1]),记下所碰到过的最大总和子向量,最大值为最初是0。
#include <iostream>
#define N 10
using namespace std;
int main()
{
int array[10] = {31, -41,59, 26, -53, 58, 97, -93, -23, 84};
int maxsofar = 0;
int maxendinghere = 0;
for( int i = 0; i < N;i++)
{
maxendinghere =max(maxendinghere + array[i], 0);
maxsofar = max(maxsofar, maxendinghere);
}
cout << "thesum of subarray sum: " << maxsofar << endl;
return 0;
}
根据上述的四个算法的依次改进,我们可以得到如下的几个算法设计技术:
1.保存状态,避免重新计算。算法2和4中使用了简单的动态编程形式,使用一些空间来保存各个结果。
2.将信息预处理到数据结构中,算法3中的cumarr中允许对子向量中的总和进行快速计算。
3.分治算法,算法3中使用了本思想
4.扫描算法。通常要回答,如何将x[0..i-1]的解决方法扩展到x[0..i]的解决方案
5.累积 使用累积表,第i个元素包含了从0到i的所有元素的和
补充:
1.在算法3中,访问cumarr[-1]。在C语言中如何处理?
在C语言中定义数组为N+1个,然后让cumarr指针指向数组的第二个元素,也即下标为1的元素,这样就可以得到cumarr[-1]了
2.假设我们希望查找总和接近0的子向量,而不是查找具有最大总和的子向量。能够设计那种完成此任务的最有效的算法?可以使用那些算法设计技术?如果希望查找一个总和最接近某一个给定数的子向量,结果又如何?
使用一个累积和向量,对其进行排序,找出相邻两项差最小的即和最接近的0的子序列和。
#include<iostream>
using namespace std;
#define N 10
int comp(const void* a, const void* b)
{
int p = *(int *)a;
int q = *(int *)b;
if(p > q)
{
return 1;
}
else if(p < q)
{
return -1;
}
else
return 0;
}
int main()
{
int array[10] = {31, -41,59, 26, -53, 58, 97, -93, -23, 84};
int temp[N+1];
int * cumarr =&temp[1];
cumarr[-1] = 0;
for( int i = 0; i < N;i++)
{
cumarr[i] =cumarr[i-1] + array[i];
}
qsort( cumarr, 0, N-1,comp);
int minvalue = 0;
for( int i = 1; i < N;i++)
{
int temp =(int)abs(cumarr[i+1] - cumarr[i]);
minvalue = min(minvalue, temp);
}
cout << "Min:" << minvalue << endl;
return 0;
}
9. 代码优化
问题1 ——整数求余
2.3中给出了一个旋转一个向量可用的三种算法,在其中一个中要使用求余的运算:
k= (j + rotdist) % n;
而在计算机中,求余运算符 % 的开销十分大,尽管大多数的算术运算需要大约10纳秒的时间,但是求余运算要花费近100纳秒。可以改写代码如下:
k= j + rotdist >= n ? j + rotdist – n : j + rotdist;
问题2 ——函数,宏以及内联函数
将函数用宏或内联函数代替,可以节省掉函数调用开销。
注:在递归调用中,如果用宏替代函数调用,将造成性能明显降低,具体原因不明。
问题3 ——顺序查找
对未进行排序的顺序表,通过给数组设置“哨兵”,可以省略掉是否结尾的判断,从而增加查找速度。
int ssearch( t)
hold= x[n];
x[n]= t;
for(i = 0; ; i++)
ifx[i] == t
break;
x[n]= hold;
ifi == n && x[n] != t
return-1;
else
returni;
对循环进行展开也可以加速运行
二分查找的修改:
原始的查找方法:
l= 0; u = n – 1;
loop
ifl > u
p= -1; break;
m= ( l + u) / 2;
case
x[m]< t : l = m + 1;
x[m]== t : p = m; break;
x[m]> t : u = m - 1;
修改为如下的查找方式:
l= -1; u = n;
whilel+1 != u
m= ( l + u) / 2;
ifx[m] < t
l= m;
else
u= m;
p= u;
ifp >= n || x[p] != t
p= -1
减少了判断的次数,因此增加速率
对于1000个数字的特例:
i= 512
l = -1;
if(x[512] < t)
l = 1000 - 512;
while i != 1
i = i / 2;
if(x[l+i] < t)
l = l + i;
p = l + 1;
if p > 1000 || x[p] != t
p = -1;
补充:
7.给定一个非常长的字节序列(假设有10亿个或万亿个),你如何有效计算1位的总数?
计算每一个输入单元(8位或32位)中为1的位数,然后将各个单元中的1位个数相加,最后可以得到所包含的的1的总数。
检测32位的数中的1的格式:
unsignedint a = b;
if(a!= 0 && (a & (a-1)))
{
counter++;
}
a -= 1;
10. 数据空间压缩
压缩的方法:
1.可以将稀疏矩阵使用邻接表表示
2.不保存,重新计算(以时间换空间)
3.数据压缩
4.垃圾回收
By Andy @ 2013-7-15
本文探讨了算法优化的不同方法,包括改进二分查找、性能优化策略、数据结构利用、代码级优化等,提供了具体的实现案例。

4426

被折叠的 条评论
为什么被折叠?



