在 JavaScript 中旋转数组中的元素

Posted

技术标签:

【中文标题】在 JavaScript 中旋转数组中的元素【英文标题】:Rotate the elements in an array in JavaScript 【发布时间】:2010-12-31 09:53:54 【问题描述】:

我想知道旋转 javascript 数组最有效的方法是什么。

我想出了这个解决方案,其中正数 n 将数组向右旋转,负数 n 向左旋转 (-length < n < length):

Array.prototype.rotateRight = function( n ) 
  this.unshift( this.splice( n, this.length ) );

然后可以这样使用:

var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
months.rotate( new Date().getMonth() );

我上面的原始版本有一个缺陷,正如下面 cmets 中 Christoph 指出的那样,正确的版本是(额外的返回允许链接):

Array.prototype.rotateRight = function( n ) 
  this.unshift.apply( this, this.splice( n, this.length ) );
  return this;

是否有更紧凑和/或更快的解决方案,可能是在 JavaScript 框架的上下文中? (以下提议的版本都不是更紧凑或更快)

有没有内置数组旋转的 JavaScript 框架? (仍然没有人回答)

【问题讨论】:

我不明白你的例子应该做什么。为什么不直接使用months[new Date().getMonth()] 来获取当前月份的名称? @Jean:代码已损坏:按照您的操作方式,它会将拼接的元素作为一个数组而不是单独移动;您必须使用 apply() 才能使您的实施工作 今天轮换将修改月份以显示此列表(Dec 在第一位):["Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov"] @Christoph,你是对的,这不能作为通用旋转功能。它仅在用于立即转换为字符串时才有效。 @Jean:您可以通过 Array.prototype.unshift.apply(this, this.splice(...)) 修复您的版本 - 我的版本做同样的事情,但使用 push() 而不是 unshift() 【参考方案1】:

您可以使用push()pop()shift()unshift() 方法:

function arrayRotate(arr, reverse) 
  if (reverse) arr.unshift(arr.pop());
  else arr.push(arr.shift());
  return arr;

用法:

arrayRotate([1, 2, 3, 4, 5]);       // [2, 3, 4, 5, 1];
arrayRotate([1, 2, 3, 4, 5], true); // [5, 1, 2, 3, 4];

如果您需要count 参数,请参阅我的其他答案:https://***.com/a/33451102?????

【讨论】:

它没有计数,但根据使用情况,这不是什么大问题。 @JustGage,我发布了另一个支持计数的答案***.com/questions/1985260/… 请注意:请注意此方法具有侵入性,它会操纵作为参数发送的数组。不难解决duplicating the array before send it 这是打字稿中的一个版本,左右变量stackblitz.com/edit/typescript-vtcmhp【参考方案2】:

类型安全的通用版本,可改变数组:

Array.prototype.rotate = (function() 
    // save references to array functions to make lookup faster
    var push = Array.prototype.push,
        splice = Array.prototype.splice;

    return function(count) 
        var len = this.length >>> 0, // convert to uint
            count = count >> 0; // convert to int

        // convert count to value in range [0, len)
        count = ((count % len) + len) % len;

        // use splice.call() instead of this.splice() to make function generic
        push.apply(this, splice.call(this, 0, count));
        return this;
    ;
)();

在 cmets 中,Jean 提出了代码不支持 push()splice() 重载的问题。我不认为这真的很有用(见 cmets),但一个快速的解决方案(虽然有点 hack)是替换该行

push.apply(this, splice.call(this, 0, count));

用这个:

(this.push || push).apply(this, (this.splice || splice).call(this, 0, count));

在 Opera 10 中使用 unshift() 代替 push() 几乎快两倍,而在 FF 中的差异可以忽略不计;代码:

Array.prototype.rotate = (function() 
    var unshift = Array.prototype.unshift,
        splice = Array.prototype.splice;

    return function(count) 
        var len = this.length >>> 0,
            count = count >> 0;

        unshift.apply(this, splice.call(this, count % len, len));
        return this;
    ;
)();

【讨论】:

Array.prototype 方法的良好缓存! +1 非常好的防弹实现,但总的来说,我更愿意依赖异常,或者只是简单的错误响应,用于错误的使用。这是为了保持代码干净和快速。用户有责任传递正确的参数或承担后果。我不喜欢对好用户的惩罚。除此之外,这是完美的,因为它确实根据要求修改了数组,并且它不会受到我的实现中的缺陷的影响,即未移动数组而不是您提到的单个元素。返回 this 也更好地允许链接。所以谢谢。 这里的闭包成本是多少,只是为了缓存推送和拼接? @Jean:好吧,闭包捕获了整个作用域链;只要外部函数是***的,影响应该可以忽略不计,无论如何它是 O(1),而查找 Array 方法的调用次数是 O(n);优化实现可能会内联查找,因此不会有任何收益,但是对于我们不得不处理很长一段时间的相当愚蠢的解释器,在较低范围内缓存变量可能会产生重大影响 这不会旋转大型数组。我今天检查了一下,它只能处理一个长度为 250891 个项目的数组。显然,您可以通过 apply 方法传递的参数数量受到一定的调用堆栈大小的限制。在 ES6 术语中,扩展运算符也会遇到相同的堆栈大小问题。下面我给出一个做同样事情的地图方法。它速度较慢,但​​适用于数百万个项目。您也可以使用简单的 for 或 while 循环来实现 map,它会变得更快。【参考方案3】:

我可能会这样做:

Array.prototype.rotate = function(n) 
    n = n % this.length;
    return this.slice(n, this.length).concat(this.slice(0, n));


编辑    这是一个修改器版本:

Array.prototype.rotate = function(n) 
    n = n % this.length;
    while (this.length && n < 0) n += this.length;
    this.push.apply(this, this.splice(0, n));
    return this;

【讨论】:

记住这个函数保持原始数组不变 需要修改原来的Array(this),就像push、pop、shift、unshift、concat、splice一样。除此之外,这是有效的。 @Gumbo,为什么是 while 循环?我们不需要使 n 为正值, splice 也适用于负值。最后,这是正确的,但几乎是 Christoph 版本,它首先做到了,没有过载警告。 @Gumbo,更正我之前的评论,负数仅适用于拼接(n.this.length)版本。在您的版本中使用 splice( 0, n ) 需要一个正整数。 我在 return 语句之前添加了n = n % this.length 来处理负数和/或超出边界的数字。【参考方案4】:

这个函数可以双向工作,也可以处理任何数字(即使数字大于数组长度):

function arrayRotate(arr, count) 
  count -= arr.length * Math.floor(count / arr.length);
  arr.push.apply(arr, arr.splice(0, count));
  return arr;

用法:

for(let i = -6 ; i <= 6 ; i++) 
  console.log(arrayRotate(["?","?","?","?","?"], i), i);

结果:

[ "?", "?", "?", "?", "?" ]    -6
[ "?", "?", "?", "?", "?" ]    -5
[ "?", "?", "?", "?", "?" ]    -4
[ "?", "?", "?", "?", "?" ]    -3
[ "?", "?", "?", "?", "?" ]    -2
[ "?", "?", "?", "?", "?" ]    -1
[ "?", "?", "?", "?", "?" ]    0
[ "?", "?", "?", "?", "?" ]    1
[ "?", "?", "?", "?", "?" ]    2
[ "?", "?", "?", "?", "?" ]    3
[ "?", "?", "?", "?", "?" ]    4
[ "?", "?", "?", "?", "?" ]    5
[ "?", "?", "?", "?", "?" ]    6

【讨论】:

使用这个函数我遇到了一个问题,因为它改变了原始数组。我在这里找到了一些解决方法:***.com/questions/14491405 @ognockocaten 这不是问题,这就是这个函数的工作原理。如果您想保持原始数组不变,请先克隆它:var arr2 = arrayRotate(arr.slice(0), 5) 这确实是一个很好的答案,它帮助了我。但是,最好提供一个不会改变原始数组的替代方案。当然,仅创建原始副本很容易,但这不是最佳做法,因为它会不必要地使用内存。 @Hybridwebdev 使用const immutatableArrayRotate = (arr, count) =&gt; ArrayRotate(arr.clone(), count)【参考方案5】:

其中许多答案似乎过于复杂且难以阅读。 我想我没有看到任何人使用 splice 和 concat...

function rotateCalendar()
    var cal=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
    cal=cal.concat(cal.splice(0,new Date().getMonth()));
    console.log(cal);  // return cal;


console.log 输出(*5 月生成):

["May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr"]

至于紧凑性,我可以提供几个通用的单行函数(不包括 console.log | return 部分)。只需在参数中输入数组和目标值即可。

我将这些函数合二为一,用于一个四人纸牌游戏程序,其中数组为 ['N','E','S','W']。我将它们分开,以防有人想复制/粘贴以满足他们的需要。出于我的目的,我在寻找在游戏的不同阶段(Pinochle)下轮到谁来玩/行动时使用这些功能。我没有费心测试速度,所以如果其他人愿意,请随时告诉我结果。

*注意,函数之间的唯一区别是“+1”。

function rotateToFirst(arr,val)  // val is Trump Declarer's seat, first to play
    arr=arr.concat(arr.splice(0,arr.indexOf(val)));
    console.log(arr); // return arr;

function rotateToLast(arr,val)  // val is Dealer's seat, last to bid
    arr=arr.concat(arr.splice(0,arr.indexOf(val)+1));
    console.log(arr); // return arr;


组合函数...

function rotateArray(arr,val,pos)
    // set pos to 0 if moving val to first position, or 1 for last position
    arr=arr.concat(arr.splice(0,arr.indexOf(val)+pos));
    return arr;

var adjustedArray=rotateArray(['N','E','S','W'],'S',1);

调整数组=

W,N,E,S

【讨论】:

:) 顺便说一句我用过here @Wes 您对我的回答的针锋相对的批评无法解释为什么不应使用 concatsplice()。这似乎是一个自我推销的评论,它将研究人员从一个赞成的答案引导到一个非赞成的答案。当你有理由举报你时,你应该在你的举报评论中包含这一点。您的新答案没有试图解释自己,也没有试图解释为什么应该避免我的答案中的技术。对于所提出的问题,我认为 concat&splice 没有任何问题——独特月份的数组,旋转到前面。 我的答案已更新,提供了更详细的解释。我在您删除您的反对票之前发表了上述评论,现在我将其删除,因为您已纠正此事。有关 concat 和 splice 的完整说明,请参阅我的解决方案。简而言之,他们做了很多工作,每个都返回一个新数组。此外,.indexOf 方法是 O(n) 线性搜索。因此,在 splice 内部使用(在 concat 内部使用),您的时间复杂度几乎为 O(n^2)。当你的代码靠得很近时,也很难阅读它的价值。只是我的一些想法。 这些函数看起来是嵌套的,但它们的返回值只传递给父函数一次。所以,是的,即使它们的 O 时间复杂度为 n,它们也不会以嵌套方式迭代。这意味着O(n ^ 2) 是一个错误的断言。如果三个函数是O(n),那么总的最坏情况是O(n + n + n)。更有吸引力的是,时间复杂度很小的是o(1 + 1 + 1)——这可能在条件上优于对每个元素执行条件评估的映射/循环技术(o(n)O(n))。我的答案没有迭代条件。【参考方案6】:

切片和解构的简单解决方案:

const rotate = (arr, count = 1) => 
  return [...arr.slice(count, arr.length), ...arr.slice(0, count)];
;

const arr = [1,2,3,4,5];

console.log(rotate(arr, 1));  // [2, 3, 4, 5, 1]
console.log(rotate(arr, 2));  // [3, 4, 5, 1, 2]
console.log(rotate(arr, -2)); // [4, 5, 1, 2, 3]
console.log(rotate(arr, -1)); // [5, 1, 2, 3, 4]

【讨论】:

这真是太好了!您可以通过设置默认值来避免检查count === 0。这将允许您将其设为单线:const rotate = (arr, n = 1) =&gt; [...arr.slice(n, arr.length), ...arr.slice(0, n)];【参考方案7】:

使用 ES6 的 spread 做一个不可变的例子......

[...array.slice(1, array.length), array[0]]

[array[array.items.length -1], ...array.slice(0, array.length -1)]

虽然它可能不是最有效的,但它很简洁。

【讨论】:

【参考方案8】:

这里有一个非常简单的方法来移动数组中的项目:

function rotate(array, stepsToShift) 

    for (var i = 0; i < stepsToShift; i++) 
        array.unshift(array.pop());
    

    return array;

【讨论】:

【参考方案9】:

当我找不到现成的 sn-p 以“今天”开始日期列表时,我这样做了(不太通用,可能远没有上面的例子那么精致,但完成了工作):

//returns 7 day names with today first
function startday() 
    const days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
    let today = new Date();
    let start = today.getDay(); //gets day number
    if (start == 0)  //if Sunday, days are in order
        return days
    
    else  //if not Sunday, start days with today
        return days.slice(start).concat(days.slice(0,start))
    

感谢比我更好的程序员进行了一点重构,它比我最初的尝试短了一两行,但欢迎任何关于效率的进一步 cmet。

【讨论】:

【参考方案10】:

@Christoph,您编写了一个干净的代码,但比我找到的这个代码慢 60%。在 jsPerf 上查看结果:http://jsperf.com/js-rotate-array/2 [编辑] 好的,现在有更多的浏览器,一个不明显的女巫方法是最好的

var rotateArray = function(a, inc) 
    for (var l = a.length, inc = (Math.abs(inc) >= l && (inc %= l), inc < 0 && (inc += l), inc), i, x; inc; inc = (Math.ceil(l / inc) - 1) * inc - l + (l = inc))
    for (i = l; i > inc; x = a[--i], a[i] = a[i - inc], a[i - inc] = x);
    return a;
;

var array = ['a','b','c','d','e','f','g','h','i'];

console.log(array);
console.log(rotateArray(array.slice(), -1)); // Clone array with slice() to keep original

【讨论】:

@molokocolo 直观地说,您的解决方案会运行得更慢,因为增量会更高,因为您使用的是循环。我已将您的测试用例更新为 5,但它似乎运行得更慢:jsperf.com/js-rotate-array/3 我不知道 rotateArray() 函数在做什么 ^^ 只是它的工作:) (这是一个奇怪的代码!)Chrome 的行为几乎与 Firefox 完全相反...... 这个方法很有趣,它通过交换数组中的元素引用来工作。数组大小永远不会改变,这就是为什么它非常快但使用循环的缺点。它显示的有趣之处在于 Firefox 在循环方面是最快的,而 chrome 在数组操作方面是最快的。 在分析代码后,我发现了一个弱点,表明此解决方案仅对较小的数组和某些旋转计数更快:jsperf.com/js-rotate-array/5 在所有浏览器中,最快的仍然是带有 splice/unshift 的 Google Chrome解决方案。这意味着这实际上是一个数组方法优化问题,Chrome 比其他浏览器做得更好。【参考方案11】:

见http://jsperf.com/js-rotate-array/8

function reverse(a, from, to) 
  --from;
  while (++from < --to) 
    var tmp = a[from];
    a[from] = a[to];
    a[to] = tmp;
  


function rotate(a, from, to, k) 
  var n = to - from;
  k = (k % n + n) % n;
  if (k > 0) 
    reverse(a, from, from + k);
    reverse(a, from + k, to);
    reverse(a, from, to);
  

【讨论】:

【参考方案12】:
function rotate(arr, k) 
for (var i = 0; i < k+1; i++) 
    arr.push(arr.shift());

return arr;

//k work as an index array
console.log(rotate([1, 2, 7, 4, 5, 6, 7], 3)); //[5,6,7,1,2,7,4]
console.log(rotate([-1, -100, 3, 99], 2));     //[99,-1,-100,3]

【讨论】:

【参考方案13】:
// Example of array to rotate
let arr = ['E', 'l', 'e', 'p', 'h', 'a', 'n', 't'];

// Getting array length
let length = arr.length;

// rotation < 0 (move left), rotation > 0 (move right)
let rotation = 5;

// Slicing array in two parts
let first  = arr.slice(   (length - rotation) % length, length); //['p', 'h', 'a' ,'n', 't']
let second = arr.slice(0, (length - rotation) % length); //['E', 'l', 'e']

// Rotated element
let rotated = [...first, ...second]; // ['p', 'h', 'a' ,'n', 't', 'E', 'l', 'e']

一行代码:

let rotated = [...arr.slice((length - rotation) % length, length), ...arr.slice(0, (length - rotation) % length)];

【讨论】:

这种技术比我简洁的“连续拼接”单线技术有什么优势吗? 如果你有一个包含重复元素的数组会发生什么?如果您不知道要应用旋转的元素的值怎么办?使用此解决方案,您无需指定要应用旋转的元素。此外,根据旋转变量,您可以向左(旋转 0)旋转整个数组。 我之所以这么问,是因为这个页面上有这么多答案,研究人员可能很难确定他们应该在自己的项目中使用哪一个以及为什么。我的答案只能依靠旋转偏移; indexOf() 只是迎合发布的问题。 所以你的答案超出了发布问题的范围,并以通用方式回答。【参考方案14】:

接受的答案有一个缺陷,即无法处理大于调用堆栈大小的数组,这取决于会话,但应该大约为 100~300K 项。例如,在我尝试的当前 Chrome 会话中,它是 250891。在许多情况下,您甚至可能不知道数组可能动态增长到什么大小。所以这是一个严重的问题。

为了克服这个限制,我想一种有趣的方法是利用Array.prototype.map() 并通过以循环方式重新排列索引来映射元素。此方法采用一个整数参数。如果此参数为正,它将在增加索引时旋转,如果为负,则在减少索引方向上旋转。这只有 O(n) 时间复杂度,并且将返回一个新数组而不会改变它所调用的数组,同时处理数百万个项目而没有任何问题。让我们看看它是如何工作的;

Array.prototype.rotate = function(n) 
var len = this.length;
return !(n % len) ? this
                  : n > 0 ? this.map((e,i,a) => a[(i + n) % len])
                          : this.map((e,i,a) => a[(len - (len - i - n) % len) % len]);
;
var a = [1,2,3,4,5,6,7,8,9],
    b = a.rotate(2);
console.log(JSON.stringify(b));
    b = a.rotate(-1);
console.log(JSON.stringify(b));

其实我在以下两件事上受到批评后;

    没有必要为正输入或负输入设置条件,因为它揭示了违反 DRY 的情况。您可以使用一个映射来执行此操作,因为每个负 n 都有一个正等效项(完全正确..) Array 函数应该更改当前数组或创建一个新数组,您的函数可以根据是否需要移位来执行(完全正确..)

我决定修改代码如下;

Array.prototype.rotate = function(n) 
var len = this.length;
return !(n % len) ? this.slice()
                  : this.map((e,i,a) => a[(i + (len + n % len)) % len]);
;
var a = [1,2,3,4,5,6,7,8,9],
    b = a.rotate(10);
console.log(JSON.stringify(b));
    b = a.rotate(-10);
console.log(JSON.stringify(b));

再说一遍;当然,像Array.prototype.map() 这样的 JS 仿函数与用纯 JS 编码的等效函数相比速度很慢。为了获得超过 100% 的性能提升,如果我需要在生产代码中旋转数组,就像我在尝试 String.prototype.diff() 时使用的那样,以下可能是我的选择 Array.prototype.rotate()

Array.prototype.rotate = function(n)
  var len = this.length,
      res = new Array(this.length);
  if (n % len === 0) return this.slice();
  else for (var i = 0; i < len; i++) res[i] = this[(i + (len + n % len)) % len];
  return res;
;

【讨论】:

shift() 和 splice(),尽管它们可能很慢,但实际上比使用 map() 或任何使用函数参数的方法要快。 shift() 和 splice() 更具声明性,从长远来看也更容易优化。 @Jean Vincent 是的,你是对的.. map 实际上可能会变慢,但我相信理想的旋转逻辑是这样的。我只是试图展示合乎逻辑的方法。 Map 可以很容易地用 for 循环简化,并会显着提高速度......然而,实际情况是,公认的答案有一种在考虑其他语言的情况下开发的方法。它不适合 JS.... 当数组大小大于调用堆栈大小时甚至不会运行。 (就我而言,在本次会议中,结果仅限于 250891 项)所以就是这样..! 您是正确的关于应用的调用堆栈大小可以超过,这是一个比速度更有效的论点,考虑编辑您的答案以通过改进缩进来强调这一点。【参考方案15】:

这个函数比小数组的公认答案要快一点,但对于大数组来说要快得多。这个函数还允许旋转大于数组长度的任意次数,这是原始函数的一个限制。

最后,接受的答案与描述的相反方向旋转。

const rotateForEach = (a, n) => 
    const l = a.length;
    a.slice(0, -n % l).forEach(item => a.push( item ));
    return a.splice(n % l > 0 ? (-n % l) : l + (-n % l));

以及等效的功能(似乎也有一些性能优势):

const rotateReduce = (arr, n) => 
    const l = arr.length;
    return arr.slice(0, -n % l).reduce((a,b) => 
        a.push( b );
        return a;
    , arr).splice(n % l> 0 ? l + (-n % l) : -n % l);
;

您可以查看performance breakdown here.

【讨论】:

不幸的是它不起作用,它将数组缩小到零大小,这解释了为什么它在第一次运行后比任何其他正确的实现都要快得多,尤其是在更大的数组上。在您的基准测试中,如果您交换测试顺序并在最后显示 a.length,您会发现问题。【参考方案16】:

编辑:: 嘿,事实证明发生了太多的迭代。没有循环,没有分支。

对于任何大小的 n,仍然适用于负 n 右旋转和正 n 左旋转, 无突变

function rotate(A,n,l=A.length) 
  const offset = (((n % l) + l) %l)
  return A.slice(offset).concat(A.slice(0,offset))

这是用于咯咯笑的代码高尔夫版本

const r = (A,n,l=A.length,i=((n%l)+l)%l)=>A.slice(i).concat(A.slice(0,i))

EDIT1::* 无分支、无变异的实现。

嘿,原来我有一个不需要它的分支。这是一个有效的解决方案。 负数 = 向右旋转 |num| 正数 = 左旋转 num

function r(A,n,l=A.length) 
  return A.map((x,i,a) => A[(((n+i)%l) + l) % l])

方程((n%l) + l) % l 精确映射任意大的 n 值的正数和负数

原创

左右旋转。向左旋转正n,向右旋转负n

适用于 n 的大量输入。

没有突变模式。这些答案的突变太多了。

此外,与大多数答案相比,操作更少。没有弹出,没有推送,没有拼接,没有移位。

const rotate = (A, num ) => 
   return A.map((x,i,a) => 
      const n = num + i
      return n < 0 
        ? A[(((n % A.length) + A.length) % A.length)]
        : n < A.length 
        ? A[n] 
        : A[n % A.length]
   )

 const rotate = (A, num) => A.map((x,i,a, n = num + i) => 
  n < 0
    ? A[(((n % A.length) + A.length) % A.length)]
    : n < A.length 
    ? A[n] 
    : A[n % A.length])

//test
rotate([...Array(5000).keys()],4101)   //left rotation
rotate([...Array(5000).keys()],-4101000)  //right rotation, num is negative

// will print the first index of the array having been rotated by -i
// demonstrating that the rotation works as intended
[...Array(5000).keys()].forEach((x,i,a) => 
   console.log(rotate(a,-i)[0])
) 
// prints even numbers twice by rotating the array by i * 2 and getting the first value
//demonstrates the propper mapping of positive number rotation when out of range
[...Array(5000).keys()].forEach((x,i,a) => 
   console.log(rotate(a,i*2)[0])
)

解释:

将 A 的每个索引映射到索引偏移处的值。在这种情况下

offset = num

如果offset &lt; 0offset + index + positive length of A 将指向反向偏移量。

如果offset &gt; 0 and offset &lt; length of A 则简单地将当前索引映射到A 的偏移索引。

否则,将偏移量和长度取模以映射数组边界中的偏移量。

offset = 4offset = -4 为例。

offset = -4A = [1,2,3,4,5] 时,对于每个索引,offset + index 将使幅度(或Math.abs(offset))更小。

我们先解释一下负n的索引的计算。 A[(((n % A.length) + A.length) % A.length)+0] 并被吓倒了。 别这样。我在 Repl 中花了 3 分钟才解决这个问题。

    我们知道n 是否定的,因为案例是n &lt; 0。如果数字大于 Array 的范围,n % A.length 会将其映射到该范围内。 n + A.length 将该数字添加到 A.length 以正确偏移 n 金额。 我们知道n 是否定的,因为案例是n &lt; 0n + A.length 将该数字添加到 A.length 以抵消 n 正确的数量。

    接下来使用模数将其映射到 A 的长度范围。第二个模数是将计算结果映射到可索引范围中所必需的

    第一个索引:-4 + 0 = -4。 A.length = 5。A.length - 4 = 1。A2 为 2。映射索引 0 到 2。[2,... ]

    下一个索引,-4 + 1 = -3。 5 + -3 = 2。A2 是 3。将索引 1 映射到 3。[2,3... ]

同样的过程适用于offset = 4。 当offset = -4A = [1,2,3,4,5]时,对于每个索引,offset + index会使幅度变大。

    4 + 0 = 0。将 A[0] 映射到 A[4] 处的值。 [5...] 4 + 1 = 5, 索引时 5 超出范围,所以将 A2 映射到 5 / 5 余数的值,即 0。A2 = 值 一个[0]。 [5,1...] 重复。

【讨论】:

【参考方案17】:

2021 年 2 月更新

一个单行函数来执行数组元素的向右旋转向左旋转

向左旋转

const arrRotateLeft = (a,n) =>while (n>0) a.push(a.shift());n--;return a;;

向右旋转

const arrRotateRight= (a,n) =>while (n>0) a.unshift(a.pop());n--;return a;;

const arrRotateLeft = (a,n)=>while (n>0) a.push(a.shift());n--;return a;;

const arrRotateRight= (a,n)=>while (n>0) a.unshift(a.pop());n--;return a;;


//=========== Test rotate Left =================
console.log(arrRotateLeft([1,2,3,4,5,6],0));       // [1,2,3,4,5,6]   <== rotate in this direction
console.log(arrRotateLeft([1,2,3,4,5,6],1));       // [2,3,4,5,6,1]
console.log(arrRotateLeft([1,2,3,4,5,6],2));       // [3,4,5,6,1,2]
console.log(arrRotateLeft([1,2,3,4,5,6],3));       // [4,5,6,1,2,3]
console.log(arrRotateLeft([1,2,3,4,5,6,7,8],5));   // [6,7,8,1,2,3,4,5]


//=========== Test rotate Right =================
console.log(arrRotateRight([1,2,3,4,5,6],0));      // [1,2,3,4,5,6]   ==> rotate in this direction
console.log(arrRotateRight([1,2,3,4,5,6],1));      // [6,1,2,3,4,5]
console.log(arrRotateRight([1,2,3,4,5,6],2));      // [5,6,1,2,3,4]
console.log(arrRotateRight([1,2,3,4,5,6],3));      // [4,5,6,1,2,3]
console.log(arrRotateRight([1,2,3,4,5,6,7,8],5));  // [4,5,6,7,8,1,2,3]

【讨论】:

那么这个计算复杂度是多少?我不是 javascript SME,所以我可能会误解您的方法。这是一个循环,每次迭代调用两个函数吗?这比我的“拼接”方法有优势吗?这只是对先前答案的修改吗? ***.com/a/55757285/2943403【参考方案18】:

为了将数组向左旋转 'd' 个位置,我们可以使用 unshift() 和 pop()。

   var arr=[1,2,3,4,5],d=4;
   for(var i=d;i<arr.length;i++)
    arr.unshift(arr.pop());
    console.log(arr);

对于黑客等级,解决DS左右旋转问题解决方案(javascript,java)查看我下面的文章

https://webdev99.com/left-rotationproblem-solving-data-structures/

【讨论】:

【参考方案19】:
Follow a simpler approach of running a loop to n numbers and shifting places upto that element.

function arrayRotateOne(arr, n) 
  for (let i = 0; i < n; i++) 
    arr.unshift(arr.pop());
  
  return arr;

console.log( arrayRotateOne([1,2,3,4,5,6],2));



function arrayRotateOne(arr,n) 
  for(let i=0; i<n;i++)
      arr.push(arr.shift());
      console.log('execute',arr)
    
     return arr;
 

console.log(arrayRotateOne([1,2,3,4,5,6],2));

【讨论】:

【参考方案20】:

非变异解决方案

var arr = ['a','b','c','d']
arr.slice(1,arr.length).concat(arr.slice(0,1)

有突变

var arr = ['a','b','c','d']
arr = arr.concat(arr.splice(0,1))

【讨论】:

【参考方案21】:

我正在分享我用于在轮播上旋转的解决方案。 当数组大小小于displayCount 时,它可能会中断,但是您可以添加额外的条件以在它很小时停止旋转,或者也可以连接主数组 *displayCount 次。

function rotate(arr, moveCount, displayCount) 
  const size = arr.length;

  // making sure startIndex is between `-size` and `size`
  let startIndex = moveCount % size;
  if (startIndex < 0) startIndex += size; 

  return [...arr, ...arr].slice(startIndex, startIndex + displayCount);


// move 3 to the right and display 4 items
// rotate([1,2,3,4,5], 3, 4) -> [4,5,1,2]

// move 3 to the left and display 4 items
// rotate([1,2,3,4,5], -3, 4) -> [3,4,5,1]

// move 11 to the right and display 4
// rotate([1,2,3,4,5], 3, 4) -> [2,3,4,5]

【讨论】:

【参考方案22】:

不要使用concatsplice 或其他任何东西。使用.mapwhile 循环以简单、有效的方式向左或向右旋转。这个想法是迭代原始数组的长度。在每次迭代中,更新要添加到新旋转数组的旋转中下一个值的索引。这个解决方案是 O(n) 时间复杂度——两个例子中只有一个循环。

地图:

function Rotate(arr, n) 
  if (n === 0) 
    return arr;
  

  var left = n < 0;
  n = Math.abs(left ? n : arr.length - n);

  return arr.map(() => 
    n = n < arr.length ? n : 0;

    return arr[n++];
  );


var array = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var places = 4;

console.log(Rotate(array, places));//[12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
console.log(Rotate(array, -places));//[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3]

同时:

function Rotate(arr, n) 
  if (n === 0) 
    return arr;
  

  var rotatedArray = [];
  var left = n < 0;
  n = Math.abs(left ? n : arr.length - n);

  while (rotatedArray.length < arr.length) 
    n = n < arr.length ? n : 0;
    rotatedArray.push(arr[n++]);
  

  return rotatedArray;

spliceconcat 方法是 O(n) 操作;前者删除然后返回一个“拼接”值的数组,后者返回一个包含合并数组值的新数组。所以,如果你同时使用它们,你做的工作太多: O(n) * 2 和两个新复制的数组。坦率地说,使用简单的while 循环更容易了解发生的逻辑。

【讨论】:

【参考方案23】:

如何增加一个计数器,然后将除法的余数除以数组长度以获得您应该在的位置。

var i = 0;
while (true);

    var position = i % months.length;
    alert(months[position]);
    ++i;

除此之外的语言语法应该可以正常工作。

【讨论】:

这不会以任何方式旋转阵列。 True(如答案中所述),但它不必旋转数组。 但是这个的重点是根据标题旋转一个数组,而不是从某个偏移量显示一个数组。如果您使用相同的循环来重建新数组,它将比使用本机 Array 方法的其他版本慢得多。另外,您忘记了跳出无限循环。【参考方案24】:

如果您的数组会很大和/或您要旋转很多,您可能需要考虑使用链表而不是数组。

【讨论】:

同意,但问题与数组有关,更具体地说与扩展数组动词有关。【参考方案25】:

@molokoloco 我需要一个可以配置为沿一个方向旋转的函数——true 表示向前,false 表示向后。我创建了一个 sn-p,它接受一个方向、一个计数器和一个数组,并输出一个对象,其中计数器在适当的方向以及先前、当前和下一个值递增。它不会修改原始数组。

我还根据你的 sn-p 计时,虽然它并不快,但它比你的比较快 - 慢 21% http://jsperf.com/js-rotate-array/7。

function directionalRotate(direction, counter, arr) 
  counter = direction ? (counter < arr.length - 1 ? counter + 1 : 0) : (counter > 0 ? counter - 1 : arr.length - 1)
  var currentItem = arr[counter]
  var priorItem = arr[counter - 1] ? arr[counter - 1] : arr[arr.length - 1]
  var nextItem = arr[counter + 1] ? arr[counter + 1] : arr[0]
  return 
    "counter": counter,
    "current": currentItem,
    "prior": priorItem,
    "next": nextItem
  

var direction = true // forward
var counter = 0
var arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'];

directionalRotate(direction, counter, arr)

【讨论】:

这不会旋转阵列。要回答这个问题,您需要返回一个数组,它位于柜台的第一项。【参考方案26】:

我来晚了,但我有一块砖可以补充这些好的答案。 我被要求编写这样一个函数,我首先做到了:

Array.prototype.rotate = function(n)

    for (var i = 0; i < n; i++)
    
        this.push(this.shift());
    
    return this;

但是当n 很大时,它的效率似乎低于跟随:

Array.prototype.rotate = function(n)

    var l = this.length;// Caching array length before map loop.

    return this.map(function(num, index) 
        return this[(index + n) % l]
    );

【讨论】:

【参考方案27】:

我不确定这是否是最有效的方式,但我喜欢它的读取方式,它对于大多数大型任务来说已经足够快了,因为我已经在生产环境中对其进行了测试......

function shiftRight(array) 
  return array.map((_element, index) => 
    if (index === 0) 
      return array[array.length - 1]
     else return array[index - 1]
  )


function test() 
  var input = [
    name: ''
  , 10, 'left-side'];
  var expected = ['left-side', 
    name: ''
  , 10]
  var actual = shiftRight(input)

  console.log(expected)
  console.log(actual)



test()

【讨论】:

【参考方案28】:

原生、快速、小型、语义化、适用于旧引擎并且“可咖喱”。

function rotateArray(offset, array) 
    offset = -(offset % array.length) | 0 // ensure int
    return array.slice(offset).concat(
        array.slice(0, offset)
    )

【讨论】:

【参考方案29】:

** 使用最新版本的 JS,我们可以轻松构建它 **

 Array.prototype.rotateLeft = function (n) 
   this.unshift(...this.splice(-(n), n));
    return this
  

这里移动:转数,一个数组你可以传递随机数

let a = [1, 2, 3, 4, 5, 6, 7];
let moves = 4;
let output = a.rotateLeft(moves);
console.log("Result:", output)

【讨论】:

【参考方案30】: JS 中的

Array 具有以下内置方法,可以很容易地用于旋转数组,显然这些方法在本质上是不可变的。

push:将项目插入到数组的末尾。 pop:从数组末尾移除项目。 unshift:将项目插入到数组的开头。 shift:从数组的开头删除项目。

下面的解决方案(ES6)有两个参数,数组需要旋转,n,数组应该旋转的次数。

const rotateArray = (arr, n) => 
  while(arr.length && n--) 
    arr.unshift(arr.pop());
  
  return arr;


rotateArray(['stack', 'overflow', 'is', 'Awesome'], 2) 
// ["is", "Awesome", "stack", "overflow"]

它可以添加到 Array.prototype 中,并且可以在您的整个应用程序中使用

Array.prototype.rotate = function(n) 
 while(this.length && n--) 
   this.unshift(this.pop());
 
 return this;

[1,2,3,4].rotate(3); //[2, 3, 4, 1]

【讨论】:

以上是关于在 JavaScript 中旋转数组中的元素的主要内容,如果未能解决你的问题,请参考以下文章

旋转数组中的元素查找

lintcode:寻找旋转排序数组中的最小值 II

《剑指offer》— JavaScript旋转数组的最小数字

c语言输入一个递增排序的数组的一个旋转,输出旋转数组中的最小元素

排序旋转整数数组,搜索算法[重复]

旋转数组中的最小数字