接下来的21天,也就是金九我会在CSDN上更新21道精彩的、应用及变种十分广泛的算法题作为考研专业课的数据结构的准备,尽管初试考察的是手写算法,但是其中许多算法思想(方法论)的培养、使用时的“下笔如有神”是需要通过刷题真正运行实现来发现、总结其中规律的,等到后期冲刺阶段再进行手写及适当的记忆,希望能在这个过程中能够跟大家一起成长。
【day 1-leetcode189】给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
力扣https://leetcode.cn/problems/rotate-array/
读题 拿到这题相信不少朋友首先想到的方法是建立一个额外的数组,来存储后k位数据,然后将前n-k位执行后移k位的操作,但是跑起来报错了,原因是k可能会大于n,进而想到了下面这种方法
方法一 建立新的数组,依次将每个元素放置到新数组的对应位置上(缺点:空间复杂上O(n))
举例
输入: nums = [1,2,3,4,5,6,7], k = 3 输出: [5,6,7,1,2,3,4]
1的对应位置就是 0+k,而5的对应位置则是(4+k)%length,这样依次遍历就可以获得正确的数组,可以按要求覆盖原数组。 下给出C语言代码
void rotate(int* nums, int numsSize, int k) {
int newArr[numsSize];
for (int i = 0; i < numsSize; i++) {
newArr[(i + k) % numsSize] = nums[i];
}
for (int i = 0; i < numsSize; i++) {
nums[i] = newArr[i];
}
}
方法二 不采用新数组,环形放置元素
举例
输入: nums = [1,2,3,4,5,6,7], k = 3 输出: [5,6,7,1,2,3,4]
这个方法怎么去理解呢?很像希尔排序第一趟的思想,将这些元素进行了分治,因为元素的移动是不会出现交叉的,就像如果隔一个来移动只会奇数跟奇数换位置,同理隔着三个,1,4,7换了位置,2,5以及3,6换了位置,
这里的难点是怎么进行移动的时候保存被覆盖的元素,例如将1移动3位,4被1覆盖了,就应该用pre将其存起来,覆盖7的时候拿pre和7交换就行了以此类推,因此如果想写成循环就要给在大循环里给pre赋1,小循环里进行交换pre和nums[(i+k)%numsize].
具体不再赘述,重点讲解第三个方法,我写博客的目的也是为了跟大家一起汇总这些算法,这个方法的讲解可以去leetcode有很多人进行过了详解。
方法三 这个题其实类似王道书上一道题目
“a1,a2,a3,b1,b2”变换为“b1,b2,a1,a2,a3”,我们还可以采用三次原地逆置的方法来实现
第一步 全部逆置, 将其变为 b2,b1,a3,a2,a1 ,第二步,b逆置:b1,b2,a3,a2,a1 第三步 a逆置a1,a2,a3,b1,b2。
如何进行线性表原地逆置? 本质上采用双指针法,一个从头开始一个从尾开始,进行遍历。也可以像下面代码一样来写
void inverse(Sqlist &L,int i,int j) {
for (i; i < i + (j-i+1) / 2; i++) {//判断
int temp = L.data[i];
L.data[i] = L.data[j];
L.data[j--] = temp;
}
}
//[a1,a2,a3,b1,b2] 变成[b1,b2,a1,a2,a3]
void inverse_all(Sqlist& L, int i, int j) {
inverse(L,0,i+j-1);//i+j是长度
inverse(L, 0, j-1);//下标是0-1
inverse(L, j, i + j-1);
}