1. 最大子序列和的问题
输入样例:4 -3 5 -2 -1 2 6 -2
输出:11
1.1 二分法递归求解 - 时间复杂度:O(NlogN)
int getMax3 (int a, int b, int c) { int max = a; if (max < b) max = b; if (max < c) max = c; return max; } int getMaxSum(const vector&arr, int bgn, int end) { if (bgn >= end) return 0; int mid = (bgn + end) / 2; int leftMaxSum = getMaxSum(arr, bgn, mid); //此处的起始位置应设置为mid + 1,否则会造成无限递归 int rightMaxSum = getMaxSum(arr, mid + 1, end); int leftMaxBorder = 0, leftTmp = 0; for (int i = mid; i >= bgn; --i) { leftTmp += arr[i]; if (leftTmp > leftMaxBorder) leftMaxBorder = leftTmp; // if (leftTmp < 0) //这个地方不能提前退出,必须完全遍历 // break; } int rightMaxBorder = 0, rightTmp = 0; for (int i = mid + 1; i < end; ++i) { rightTmp += arr[i]; if (rightTmp > rightMaxBorder) rightMaxBorder = rightTmp; // if (rightTmp < 0) // break; } return getMax3(leftMaxSum, rightMaxSum, leftMaxBorder + rightMaxBorder); }
1.2 一次性遍历求解 - 时间复杂度:O(N)
int getMaxSubSum(const vector&arr, int bgn, int end) { int maxSum = 0; int sumTmp = 0; for (int i = bgn; i < end; ++i) { sumTmp += arr[i]; if (sumTmp > maxSum) maxSum = sumTmp; else if (sum < 0) sumTmp = 0; } return maxSum; }
2. 算法时间复杂度为O(logN)的典型问题:
2.1 对分查找(binary search) - 时间复杂度:(<= log2N)
int binSearch(const vector&arr, int bgn, int end, int target) //end-尾元素后一位置 { int ret = -1; while (bgn < end) { int mid = (bgn + end) / 2; if (target == arr[mid]) { ret = mid; break; } else if (target > arr[mid]) bgn = mid + 1; else end = mid; } return ret; }
2.2 两个整数最大公约数求解(欧几里德算法) - 时间复杂度:(<= 2logN)
unsigned int getGCD(unsigned int m, unsigned int n) { while (n > 0) { int rem = m % n; m = n; n = rem; } return m; }
这里其实运用了递归的思想:m与n的最大公因子即是n与rem(m%n)的最大公因子...,那么,只需要说明n与rem的最大公因子就是m与n的最大公因子即可使这个递归进行下去。所以问题是n与rem的最大公因子为什么就是m与n的最大公因子?
思路:假设m > n,又即使m < n,经过一次遍历后有m > n,m与n的最大公因子是A,则m = xA, n = yA。
rem = m%n -> m-zn(z = m/n),当rem=0,n即是两者的最大公因子;rem>0,rem=xA - zyA,很显然rem%A = 0
-> m与n的最大公因子即是n与rem的最大公因子,以此类推
2.3 幂运算 - 时间复杂度:(<= 2logN)
long long pow(long long x, unsigned int n) { if (0 == n) return 1; if (n % 2) return pow( x*x, n/2 ) * x; else return pow( x*x, n/2 ); }
以2^15为输入,则恰需要 2log(15) = 6 次乘法运算
以2^16为输入,则仅需要 4+1(<2log(16)=8) 次乘法运算,也可以修改下代码添加出口判断(if (1 == n))使之成为4次乘法运算,不过需要每次进入pow多判断一次
PS: 1. 书上以2^62为例,共需9次乘法运算,但所推演算法过程个人觉得有问题。实际过程应该是首先层层运算pow入口参数x*x的值,直至n == 0。然后,再逆序层层计算pow*x的值,直至算法结束运算
2. 代码中语句pow( x*x, n/2 )可以用pow( x, n/2 )*pow( x, n/2 )代替,但是效率会非常低,因其进行了大量的重复性工作