数组(字符数组)实现原地循环移位

数组(字符数组)原地循环移位

需求描述

  循环移位就是指一个数组假设这个数组的头和尾连接,构成一个圈,每个元素相对自己当前的位置做一个平移。举个例子 [ 5 , 6 , 7 , 8 , 1 , 2 , 3 , 4 ] [5,6,7,8,1,2,3,4] 左移四位,则左移的结果是 [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ] [1,2,3,4,5,6,7,8] 。这算是一个基本的计算机操作,现在相当于从软件的层面实现循环左移。

算法分析

  很多人能够发现,移动几位,能够对应将数组分为两个片段,然后将两个片段分别拷贝进数组即可。但是这种方法因为需要进行数组的拷贝,相当于是占用了 O ( n ) O(n) 级别的空间,但是如果想要这个算法是原地的,就需要寻找其他方法。
  原地移位相当于已经是一个固定套路了,这种定义求解方式也是需要平时积累的,出发点是大家先看一个理论,我们假设数组是A,则移动导致数组被划分为两个片段 A 1 , A 2 A_1,A_2 ,这个 A 1 A_1 A 2 A_2 从何处被分开,我们以左移k位为例(因为是循环移位,右移 k k 位相当于左移 A . l e n g t h k A.length-k 位),而且左移 k k 位和左移 k ± A . l e n g t h k \pm A.length 位结果也是一样的,所以只需要讨论 0 k < A . l e n g t h 0≤k<A.length 即可。很容易证明,左移k位即从下标为k处划分, A 1 = A [ 0 : k ] , A 2 = A [ k : ] A_1=A[0:k],A_2=A[k:] 。我们知道移位的结果就是 [ A 2 A 1 ] [A_2A_1]
   [ A 2 A 1 ] = [ A 1 T A 2 T ] T [A_2A_1]=\left[A_1^TA_2^T\right]^T ,数组的逆序也满足和转置一样的关系,所以想要得到 [ A 2 A 1 ] [A_2A_1] 可以通过三次转置得到,则结果需要数组进行三次逆序操作,第一次对 A 1 A_1 逆序,第二次对 A 2 A_2 逆序,时间复杂度是O(n),此时得到了 [ A 1 T A 2 T ] \left[A_1^TA_2^T\right] ,然后再对整个数组逆序,此时得到结果 [ A 2 A 1 ] [A_2A_1] ,这一过程时间复杂度是 O ( n ) O(n) 。显然数组的逆序是可以在 O ( n ) O(n) 的时间复杂度下原地完成的,所以几次逆序都是原地的,则空间复杂度是 O ( 1 ) O(1) 。整个过程完成原地循环移位,没有增加时间复杂度的量级。
  这种思想可以当做定式套路记忆一下,说不定哪天就有算法题遇到了。

reverse函数的代码

void reverse(int array[], int begin, int end)
{
	int tmp;
	while (begin < end)
	{
		tmp = array[begin];
		array[begin] = array[end];
		array[end] = tmp;
		begin++;
		end--;
	}
}

应用举例

  牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

  这道题如果要求空间复杂度为 O ( 1 ) O(1) 的话,则需要采用字符数组来处理,先对整个字符串进行逆序,然后寻找空格,将字符串划分为单词,然后对单词做一个逆序。

发布了36 篇原创文章 · 获赞 4 · 访问量 47万+

猜你喜欢

转载自blog.csdn.net/m0_38065572/article/details/104133880