複製一個複雜鏈表,在複雜鏈表中,每一個結點除了有一個next指針指向下一個結點外,還有一個sbiling指向鏈表中的任意結點或者null。java
下圖是一個複雜鏈表的示例,Null的指針沒有畫出。
node
1).複製原始鏈表上的每個結點,並用next指針連起來。
2).複製sbiling指針。
可是複製sbiling指針時須要比較高的複雜度。
以上圖爲例,若是咱們要複製B對應B’的的sbiling指針,那就是要找到E’,想要找到E’只能根據
B到E要走的步數 = B’到E’要走的步數
然而又如D.sbiling = B,指向的結點在它的前面,而鏈表並無指向前一個元素的指針,因此,每次都只能根據從鏈表頭結點到目標的結點的步數來找到sbiling應該指向的元素。
這種方法顯然效率過低,時間複雜度達到了O(n*n)。算法
這是一個在時間上很高效的方法,在查找上,利用哈希表的高效性。可是缺點在於要用額外的空間。app
上述方法的時間主要花在定位結點的m_pSibling上面,咱們試着在這方面作優化。仍是分兩步:第一步仍然是複製原始鏈表上的每一個結點N,並建立 N’,而後把這些建立出來的結點連接起來。同時咱們把<N, N’>的配對信息放到一個哈希表中。第二步仍是設置複製後的鏈表上每一個結點的m_pSibling。若是在原始鏈表中結點N的m_pSibling 指向結點S,那麼在複製後的鏈表中,對應的N’應該指向S’。因爲有了哈希表,能夠用O(1)的時間根據S找到S’。這種方法至關於用空間換時間,以 O(n)的空間消耗把時間複雜度由O(n^2)下降到O(n)。dom
(注:哈希表中的每個配對(pair)的Key是原始鏈表的結點,Value是Key中 結點的對應的拷貝結點。這樣在哈希表中,就能夠在O(1)的時間裏,找到原始結點對應的拷貝出來的結點。好比想求得N’的m_pSibling所指向的 S’,能夠由N的m_pSibling求得S,而<S, S’>的配對信息就在哈希表中,能夠用O(1)時間求得。)ide
這個方法的巧妙之處在於利用鏈表結點自己記錄sbiling指針的位置。
分紅三個步驟
1).根據原始鏈表的每一個結點N建立對應的N’,並把N’連在N的後面。
以下圖:
優化
2)看到上圖咱們就應該知道這個算法的巧妙之處了,B’.sbiling就記錄在B.sbiling.next,這一步就是經過這個方法設置sbiling指針了。ui
3).將兩個鏈表斷開。this
package cglib;spa
class ComplexListNode
{
int data;
ComplexListNode next;
ComplexListNode sibling;
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("data = " + data);
sb.append(", next = " + (next == null ? "null" : next.data));
sb.append(", sbiling = " + (sibling == null ? "null" : sibling.data));
return sb.toString();
}
}
public class DeleteNode{
public static void copyList(ComplexListNode head){
ComplexListNode node = head;
while(node != null){
ComplexListNode copyNode = new ComplexListNode();
copyNode.data = node.data;//1的數據給01
copyNode.next = node.next;//1指向2的指針給01,至關於01指向2
copyNode.sibling = null;
//1->2->3,變成1->01->2->02->3->03
node.next = copyNode;//1指向01
node = copyNode.next;//而後以2開始又繼續下一個while循環
}
}
//1->01->2->02->3->03,1--->3,01--->03
public static void setSbiling(ComplexListNode head){
ComplexListNode node = head;
// 當前處理的結點sibling字段不爲空,則要設置其複製結點的sibling字段
while(node != null){
ComplexListNode copyNode = node.next;//01開始
if(node.sibling != null){//1指向3
copyNode.sibling = node.sibling.next;//01指向03,至關於1指向3,3的下一個結點就是03
}
node = copyNode.next;
}
}
//1->01->2->02->3->03,1--->3,01--->03
public static ComplexListNode disConnectList(ComplexListNode head){
ComplexListNode node = head;
ComplexListNode copyHead = null;
ComplexListNode copyNode = null;
if(node != null){// node 不爲空才能進行下面的while循環
copyHead = node.next;//01爲頭
copyNode = node.next;
node.next = copyNode.next;//1的下一個01變成01的下一個2
node = node.next;//而後將2做爲下一個結點進入下面的while循環
}
while(node != null){
copyNode.next = node.next;//01的下一個就是02, //把偶數位置的結點連接起來就是複製出來的新鏈表
copyNode = copyNode.next;//以02開始進行下一個, //把奇數位置的結點連接起來就是原始鏈表
node.next = copyNode.next;//2的下一個就是3,至關於02的下一個
node = node.next;//以3做爲結點進行下一個
}
return copyHead;
}
public static ComplexListNode copy(ComplexListNode head){
copyList(head);
setSbiling(head);
return disConnectList(head);
}
public static void main(String[] args) {
ComplexListNode head = new ComplexListNode();
head.data = 1;
ComplexListNode node2 = new ComplexListNode();
node2.data = 2;
ComplexListNode node3 = new ComplexListNode();
node3.data = 3;
ComplexListNode node4 = new ComplexListNode();
node4.data = 4;
ComplexListNode node5 = new ComplexListNode();
node5.data = 5;
head.next = node2;
head.sibling = node3;
node2.next = node3;
node2.sibling = node5;
node3.next = node4;
node4.next = node5;
node4.sibling = node2;
ComplexListNode copyHead = copy(head);
ComplexListNode node = copyHead;
while(node != null){
System.out.println(node);
node = node.next;
}
}
}
輸出:
data = 1, next = 2, sbiling = 3
data = 2, next = 3, sbiling = 5
data = 3, next = 4, sbiling = null
data = 4, next = 5, sbiling = 2
data = 5, next = null, sbiling = null
下面是map實現:
package cglib;
import java.util.HashMap;
class RandomListNode
{
int data;
RandomListNode next = null;
RandomListNode sibling = null;
RandomListNode(int data) {
this.data = data;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("data = " + data);
sb.append(", next = " + (next == null ? "null" : next.data));
sb.append(", sbiling = " + (sibling == null ? "null" : sibling.data));
return sb.toString();
}
}
public class DeleteNode{
public static RandomListNode Clone(RandomListNode pHead)
{
if(pHead == null)
return null;
HashMap<RandomListNode, RandomListNode> map = new HashMap<>();
RandomListNode newHead = new RandomListNode(pHead.data);
RandomListNode pre = pHead, newPre = newHead;//pre用於掃描原鏈表
//newHead指向複製鏈表,newPre用於掃描複製鏈表
map.put(pre, newPre);
while(pre.next != null){
newPre.next = new RandomListNode(pre.next.data);
pre = pre.next;
newPre = newPre.next;
map.put(pre, newPre);
}
pre = pHead;
newPre = newHead;
while(newPre != null){
newPre.sibling = map.get(pre.sibling);
pre = pre.next;
newPre = newPre.next;
}
return newHead;
}
public static void main(String[] args) {
RandomListNode head = new RandomListNode(1);
RandomListNode node2 = new RandomListNode(2);
RandomListNode node3 = new RandomListNode(3);
RandomListNode node4 = new RandomListNode(4);
RandomListNode node5 = new RandomListNode(5);
head.next = node2;
head.sibling = node3;
node2.next = node3;
node2.sibling = node5;
node3.next = node4;
node4.next = node5;
node4.sibling = node2;
RandomListNode copyHead = Clone(head);
RandomListNode node = copyHead;
while(node != null){
System.out.println(node);
node = node.next;
}
}
}
輸出:
data = 1, next = 2, sbiling = 3 data = 2, next = 3, sbiling = 5 data = 3, next = 4, sbiling = null data = 4, next = 5, sbiling = 2 data = 5, next = null, sbiling = null