在本身剛剛畢業不久的時候,去了一家公司面試,面試官現場考了我這道題,我記憶深入,當時沒有想到思路,毫無疑問被面試官當成菜鳥了。
最近恰好在研究數組的各類算法實現,就想到這道題,能夠拿來實現一下,記念本身逝去的青春。javascript
假設有這樣一個數組java
[1,2,3,4,5]
如今想要左移或者右移N位,好比移動1位面試
//左移1位 [2,3,4,5,1] //右移1位 [5,1,2,3,4]
這樣一道題目,你先不要看我下面的代碼,本身思考一下如何實現它,無論是複雜的仍是簡單的方法。
能夠先告訴你我用了2行代碼實現左、右移動元素。算法
[1,2,3,4,5] => [null,1,2,3,4] and [5,null,null,null,null] => [5,1,2,3,4]
這裏能夠當作2個數組,一個是沒有到達邊界的元素移動[null,1,2,3,4],一個是到達了邊界的元素移動[5,null,null,null,null],當元素到達邊界,就會往數組的初始位置移動,造成了一個循環的過程。json
很明顯,若是咱們將這2個移動後的數組合並起來,就是需求的結果。數組
一樣符合2個移動後的數組合並起來爲結果的狀況測試
[1,2,3,4,5] => [null,null,1,2,3] and [4,5,null,null,null] => [4,5,1,2,3]
[1,2,3,4,5] => [1,2,3,4,5] and [] //若是沒有,就假設爲空數組
假設移動1位的狀況
上面的步驟,咱們找到了規律,接下來要作的是找到2個數組,須要用到slice截取數組元素。
截取第一個數組code
arr.slice(0,-1) // [1,2,3,4]
截取第二個數組ip
arr.slice(-1) // [5]
合併數組源碼
arr.slice(-1).concat(arr.slice(0,-1)) // [5,1,2,3,4]
這樣你就實現了移動1位的狀況,接着,你繼續拿+5和-5範圍內的數字進行測試,發現均可以正常移動,當數字大於5或者小於-5的時候,代碼就無效了,始終輸出[1,2,3,4,5]
arr.slice(-6).concat(arr.slice(0,-6)) // [1,2,3,4,5]
咱們再加上一個小技巧,求餘數,假設是移動6,那麼,實際上和移動1是相同的,咱們就能夠根據公式求餘數
n = n%arr.length // n = 6%5 餘1
同理,當移動-6時
n = n%arr.length // n = -6%5 餘-1
接着帶入公式,發現輸出所有都正確了!!
思路分析完了,應該很清晰了吧,源碼在下面、
arr表示原始數組,n表示移動的距離,能夠是正數、能夠是0、也能夠是負數、正數表示右移,負數表示左移,0表示不移動。
function moveElement(arr, n) { if(Math.abs(n)>arr.length) n = n%arr.length return arr.slice(-n).concat(arr.slice(0,-n)) } // moveElement(arr, 9) // moveElement(arr, 0) // moveElement(arr, -9)
下次面試要是繼續碰到這道題,可能我又當場忘記思路了?
看到有評論討論不一樣方案的實現,這些都很厲害,沒有惟一的答案,而思考解決方案的時候,要考慮的是時間複雜度,移動數組的元素都會形成數組的從新排列。
第一步方案我以爲應該是找到最小移動位置的代價,即移動2和移動2n是同樣的,咱們就只須要移動2,不須要再移動n,求餘數的做用在於此,根據移動的位置切分出2個數組,不須要移動元素,最後我用的是concat合併2個數組,返回一個新的數組副本,這樣就避免了移動元素。
還有一種方案是將2個數組使用new Set(array1)和new Set(array2)設置爲集合,集合是key、value的散列表,能夠用最少的代價移動位置,不致使重排,用集合移動完以後,再Array.from()轉換回數組。
切忌,不要嘗試去直接修改原數組的元素位置,這樣作代價很是大,尤爲是數組長度很長的時候!!