JavaScript從反轉陣列到連結串列反轉

tonychen發表於2018-01-15

連結串列反轉之前我已經寫過相關的文章了,但是現在去看下之前寫過的文章,感覺太過於簡短,只是貼出了程式碼,並沒有把核心的東西講出來。趁著週末,還是把這道簡單的題重新梳理一下。

如何反轉一個陣列?

眾所周知,JavaScript的陣列提供了很多有用的運算元組的方法,其中Array.prototype.reverse方法就可以將陣列裡面的數字反轉。用reverse這個函式反轉陣列很簡單,這裡看下程式碼如何操作:

let array = [1, 2, 3, 4, 5]
array.reverse()     // [5, 4, 3, 2, 1]
複製程式碼

這樣的程式碼很簡單,但是我們依舊不知道是怎麼反轉的。下面來看下一種常見的思路————頭尾交換,如下圖:
陣列長度為3:

  • 1
  • 2
  • 3
  • 3
  • 2
  • 1

陣列長度為4:

  • 1
  • 2
  • 3
  • 4
  • 4
  • 2
  • 3
  • 1
  • 4
  • 3
  • 2
  • 1
陣列長度為5:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 5
  • 2
  • 3
  • 4
  • 1
  • 5
  • 4
  • 3
  • 2
  • 1

所以長度為 n的陣列需要交換 n / 2 + 1 次,由此我們可以得出下面的程式碼:

let array = [1, 2, 3, 4, 5]
for(let i = 0; i < array.length / 2; i ++){
    [array[i], array[array.length - i - 1]] = [array[array.length - i - 1], array[i]]
}
console.log(array)  // [5, 4, 3, 2, 1]
複製程式碼

怎麼反轉連結串列?

什麼是連結串列?我的理解是一個長度為n,無法通過下標來遍歷,只能通過當前節點來訪問下一個節點的鏈式結構。 那麼廢話不多說,先來構造一個簡單的連結串列:

//節點建構函式
function Node(val){
    this.val = val
    this.next = null
}
//定義連結串列
function List(array){
    this.head = null
    let i = 0,temp = null
    while(i < array.length){
        if( i === 0){
            this.head = new Node(array[i])
            temp = this.head
        }else{
            let newNode = new Node(array[i])
            temp.next = newNode
            temp = temp.next
        }
        i++
    }
}
//遍歷連結串列
function traverseList(listHead){
    while(listHead){
        console.log(listHead.val)    
        listHead = listHead.next
    }
}
複製程式碼

以上是一個連結串列的簡單實現,不懂的朋友可以翻看一下資料結構與演算法 接下來劃重點:連結串列只能由當前節點訪問下一個節點,無法通過下標來訪問連結串列元素
一開始沒有想到辦法,後來我用了一種比較奇葩的方法————將連結串列的值存進陣列,反轉陣列以後再重新賦值,程式碼如下:

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var reverseList = function (head) {
    let temp = head,
        result = []
    while (temp != null) {
        result.push(temp.val)
        temp = temp.next
    }
    temp = head, i = 0
    result.reverse()
    while (temp != null) {
        temp.val = result[i++]
        temp = temp.next
    }
    return head
};
複製程式碼

但是這顯然沒有利用到連結串列的特性————即由當前結點訪問下一個節點。後來我在LeetCode的討論看到這種思路————區域性反轉構成整體反轉 啥意思呢?比如說:

  • 1
  • 2
  • 3
  • 4
  • 2
  • 1
  • 3
  • 4
  • 3
  • 2
  • 1
  • 4
  • 4
  • 3
  • 2
  • 1
根據上面過程的啟發,我們很容易可以得到如下的程式碼:
var reverseList = function (head) {
    let pre = null
    while (head) {
        next = head.next
        head.next = pre
        pre = head
        head = next
    }
    return pre
};
複製程式碼

思路是不是很簡單呢?這樣簡單的思路當時我卻沒有想到...反思ing......

總結

從陣列的反轉到連結串列的反轉,我們可以得出一個結論:思維不能僵化(逃,貌似很普通的一個演算法————反轉,可以有很多種做法。路過的朋友如果還知道其他演算法,還請多多指教

相關文章