数组相关算法的学习。
1、数组循环移位
设计:把含有N个元素的数组循环右移K位,要求时间复杂度0(N),且只允许使用两个附加变量。
假设原数组序列 abcdef1234 ,要求变换成的数组序列为 1234abcdef,即循环右移了4位。相比之后,其中有两段的顺序是不变的:
1234和 abcdef,可把这两段看成两个整体。右移K位的过程就是把数组的两部分交换一下。交换的过程通过以下步骤完成:
1)逆序排序 abcdef: abcdef1234 -->fedcba1234;2)逆序排序1234: fedcba1234 -->fedcba4321;
3)全部逆序1234: fedcba4321-->1234abcdef;
伪代码:
Reverse(int *arr,int b ,int e)//翻转一小段数组
{
for(;b < e;b++,e--)
{
int temp = arr[e];//使用了一个辅助变量
arr[e] = arr[b];
arr[b] = temp;
}
}
RightShift(int * arr,int N, int K)
{
K %= N;//右移的位置
Reverse(arr,0 ,N -K -1); //翻转前N-K个数
Reverse(arr,N - K ,N - 1);//翻转后K个数
Reverse(arr,0,N -1);//翻转前N个数(所有的数)
}
可以在线性时间内实现右移操作。
2、求数组的子数组之和的最大值
(1)一维数组
一个有N个整数的一维数组 (A[0],A[1],.......,A[n-2],A[n-1],),这个数组当然有很多的子数组,那么子数组之和的最大值是什么?
设计思考:
数组的开始和末尾的下标分别为i和j
1)当0 = i = j时,元素A[0]本身构成和最大的一段
2)当0 =i < j 时,和最大的一段以A[0]开始
3)当0 < i时,元素A[0]跟和最大的一段没有关系
由以上三种情况看出,可以将一个大问题(N个元素数组)转化为一个较小的问题(n - 1个元素的数组):
(A[1],..A[n-1])中的最大的一段之和为All[1]
(A[1],..A[n-1])中包含A[1]的和最大是一段为Start[1]。
根据上述分析的三种情况,(A[0],..A[n-1])中的问题的解All[0]是三种情况的最大值max{A[0], A[0]+start[1], All[1]}.可用动态规划方式解决。
#define max(x,y) ((x>y)?x:y)
int MaxSum(int * A,int n)
{
Start[n - 1] = A[n-1];//最后的Start数组成员(包含i的最大段)和All数组成员(i之后的最大段)
All[n - 1] = A[n - 1];
for(i = n - 2 ; i >= 0;i--)//依次往前计算
{
Start[i] = max(A[i], A[i] + Start[i + 1]);// Start[i]是包含A[i]的和最大段
All[i] = max(Start[i],All[i + 1]);//All[i]是i到n-1中的和最大段
}
return All[0];//遍历完数组,All[0] 中存放结果
}
复杂度是O(N)
(2)二维数组
一个有N个整数的二维数组,那么子数组之和的最大值是什么?(需要的是每个一维数组中的子数组的前后下标一致,如起始和终结都为 i和j)
其实这个问题是一维的,可以把每一列中第a行和第c行之间的元素看成一个整体。即求数组(BC[1],..,BC[M])中的最大的一段,其中
BC[i] = B[a][i] + ...+ B[c][i].
这样