算法是为求解一个问题需要遵循的,被清楚指定的简单指令的集合,判断一个算法的好坏,其平均运行时间一般是难以得到的,但是最坏情况的运行时间容易得出,一般用最坏情况下的运行时间来衡量一个算法的好坏。
1 最大子序列和问题
这里实验了三个算法复杂度分别是O()、O(N)和O(N)的算法
1.1 二次解法
即进行两次遍历求最大子序列和的方式进行处理。
/**
* 两个for循环,时间大小为O(N*N)
* @param arr
* @return
*/
public static int firstMethod (int [] arr) {
int maxSum = 0;
for (int i = 0 ; i < arr.length; i++ ) {
int thisSum = 0;
for (int j = i; j < arr.length; j++) {
thisSum += arr[j];
if (thisSum > maxSum) {
maxSum = thisSum;
}
}
}
return maxSum;
}
1.2 二分法
- 分治法解决最大子序列和问题
- 1、将数组分成两部分,找出前半部分的最大子序列和(max1)和后半部分的最大子序列和(max2)
- 2、找出包含最后一个元素的前半部分的最大序列和
- 3、找出包含第一个元素的后半部分的最大序列和
- 4、将第2步和第3步中的两个和相加,得出max3
- 5、返回max1、max2和max3三个数中的最大者
/**
* 分治法解决最大子序列和问题
* 1、将数组分成两部分,找出前半部分的最大子序列和(max1)和后半部分的最大子序列和(max2)
* 2、找出包含最后一个元素的前半部分的最大序列和
* 3、找出包含第一个元素的后半部分的最大序列和
* 4、将第2步和第3步中的两个和相加,得出max3
* 5、返回max1、max2和max3三个数中的最大者
* @param arr 数组
* @param left 开始下标
* @param right 结束下表
* @return 返回传入数组的最大子序列和
*/
public static int divide (int [] arr, int left, int right) {
//
if (left == right) {
if (arr[left] > 0) {
return arr[left];
} else {
return 0;
}
}
int center = (left + right) / 2;
int maxLeftSum = divide(arr, left, center);
int maxRightSum = divide(arr, center + 1, right);
// 找出包含最后一个元素的前半部分的最大序列和
int maxLeftBorderSum = 0;
int thisLeftSum = 0;
for (int i = center; i >= left; i--) {
thisLeftSum += arr[i];
if (thisLeftSum > maxLeftBorderSum) {
maxLeftBorderSum = thisLeftSum;
}
}
// 找出包含的一个元素的后半部分的最大序列和
int maxRightBorderSum = 0;
int thisRightSum = 0;
for (int i = center + 1; i <= right; i++) {
thisRightSum += arr[i];
if (thisRightSum > maxRightBorderSum) {
maxRightBorderSum = thisRightSum;
}
}
// 返回maxLeftSum、maxRIghtSum、maxLeftBorderSum + maxRightBorderSum三个数中的最大者
int max = maxLeftSum;
if (maxRightSum > maxLeftSum) {
max = maxRightSum;
}
if (maxLeftBorderSum + maxRightBorderSum > max) {
max = maxLeftBorderSum + maxRightBorderSum;
}
return max;
}
这里来看一下这个二分法的算法时间分析,令T(N)是求解大小为N的最大序列和问题所花费的时间;两个递归行,每行花费T(N/2)时间,两个循环刚好将整个数组遍历一次,即可以看成花费了O(N)的时间,最后得出结论T(N) = 2T(N/2) + N,
求解这个方程:
T(N) = 2T(N/2) + N, 两边同时除以N,得到, = + 1,依次可以得到这些等式:
- = + 1
- = + 1
… - = + 1
左边从N到2,一共有logN个这样的的等式,将logN个这样的等式左右两边分别相加,可以得到, = + logN, 从而会有:
T(N) = NlogN + N
1.3 最优解法
只需要遍历一次数组,即可获取最大子序列和
public static int bestMethod (int [] arr) {
int maxSum = 0;
int thisSum = 0;
for (int j = 0; j < arr.length; j++) {
thisSum += arr[j];
if (thisSum > maxSum) {
maxSum = thisSum;
} else if (thisSum < 0) { // 第i个数到第j个数的序列和小于0时,则可以舍弃这些数了,循环可以直接从j+1开始
thisSum = 0;
}
}
return maxSum;
}
这个算法实际上是改进的算法1,理解这个算法最关键的点在于,在算法1的基础上,当和之间的序列和小于0时,那么循环就可以直接推进到.
2、欧几里得算法
计算两个整数的最大公因数,该算法的关键在于算法表述连续计算余数,并把余数赋给n,直到余数是0为止
public static int gcd (int m, int n){
while (n != 0) {
int rem = m % n; // rem 表述余数
m = n;
n = rem;
}
return m;
}
3、二分法求幂运算
二分法求的值, 根据指数的运算可以得出: = = ,即可以看成把求解的问题大小缩小了一般,当然,在程序中,当n为奇数时, = ,
public static long pow(long x, int n) {
if (n == 0) return 1;
if (n == 1) return x;
if (n%2 == 0) {
return pow(x * x, n/2);
} else {
// 当n为奇数时,n/2 的值与(n -1)/ 2的相同
return pow(x * x, n/2) * x;
}
}
来源:CSDN
作者:张三金
链接:https://blog.csdn.net/zhangsanjin_122/article/details/104004610