把一個鏈表交替切分紅兩個,系列目錄見 前言和目錄 。javascript
實現一個 alternatingSplit()
函數,把一個鏈表切分紅兩個。子鏈表的節點應該是在父鏈表中交替出現的。若是原鏈表是 a -> b -> a -> b -> a -> null
,則兩個子鏈表分別爲 a -> a -> a -> null
和 b -> b -> null
。java
var list = 1 -> 2 -> 3 -> 4 -> 5 -> null alternatingSplit(list).first === 1 -> 3 -> 5 -> null alternatingSplit(list).second === 2 -> 4 -> null
爲了簡化結果,函數會返回一個 Context 對象來保存兩個子鏈表,Context 結構以下所示:node
function Context(first, second) { this.first = first this.second = second }
若是原鏈表爲 null
或者只有一個節點,應該拋出異常。git
代碼以下:github
function alternatingSplit(head) { if (!head || !head.next) throw new Error('invalid arguments') return new Context(split(head), split(head.next)) } function split(head) { const list = new Node(head.data) if (head.next && head.next.next) list.next = split(head.next.next) return list }
這個解法的核心思路在於 split
,這個方法接收一個鏈表並返回一個以奇數位的節點組成的子鏈表。因此整個算法的解法就能很容易地用 new Context(split(head), split(head.next))
表示。算法
代碼以下:segmentfault
function alternatingSplitV2(head) { if (!head || !head.next) throw new Error('invalid arguments') return new Context(...splitV2(head)) } function splitV2(head) { if (!head) return [null, null] const first = new Node(head.data) const [second, firstNext] = splitV2(head.next) first.next = firstNext return [first, second] }
這裏的 splitV2
的做用跟整個算法的含義同樣 -- 接收一個鏈表並返回交叉分割的兩個子鏈表(以數組表示)。第一個子鏈表的頭天然是 new Node(head.data)
,第二個子鏈表呢?它實際上是 splitV2(head.next)
的第一個子鏈表(見第 4 行)。理解這個邏輯後就能明白遞歸過程。數組
代碼以下:函數
function alternatingSplitV3(head) { if (!head || !head.next) throw new Error('invalid arguments') const first = new Node() const second = new Node() const tails = [first, second] for (let node = head, idx = 0; node; node = node.next, idx = idx ? 0 : 1) { tails[idx].next = new Node(node.data) tails[idx] = tails[idx].next } return new Context(first.next, second.next) }
這個思路是,先用兩個變量表明子鏈表,而後對整個鏈表進行一次遍歷,分別把節點交替插入每一個子鏈表中。惟一須要考慮的就是在每一個循環體中判斷節點該插入哪一個鏈表。我用的是 idx
變量,在每輪循環中把它交替設置成 0
和 1
。也有人使用持續增加的 idx
配合取餘來作,好比 idx % 2
。作法有不少種,就不贅述了。測試
這裏也用了 dummy node 的技巧來簡化 「判斷首節點是否爲空」 的狀況。關於這個技巧能夠看看 Insert Nth Node