複雜鏈表的複製-OC實現2種方式

輸入一個複雜鏈表(每個節點中有節點值,以及兩個指針,一個指向下一個節點,另一個特殊指針指向任意一個節點,虛線部分),返回結果爲複製後複雜鏈表的head。

解題思路:

1、遍歷鏈表,複製鏈表中的每個結點,並將複製的結點插入到該結點的後面。例如,原鏈表爲A->B->C, 遍歷完畢後,鏈表變爲A->A'->B->B'->C->C',其中A‘,B',C'是結點A,B,C的複製結點。

看圖中,藍色箭頭爲next指針:

  

複製結點後:

2、爲複製結點的random指針賦值

如果原結點的random指針指向的是結點B,那麼將複製結點的random指針指向結點B的複製結點B'。

圖中黑色箭頭爲random指針:

複製結點的random指針賦值後:

3、將鏈表的原始結點與複製結點分割至兩個鏈表,使原始結點構成一個鏈表,複製結點構成一個鏈表。

 

代碼實現:


- (void)viewDidLoad {
    [super viewDidLoad];

    [self copyNode];
    
}

- (void)copyNode {
 
    Node * root = [Node nodeWithArray:@[@(1),@(2),@(3),@(4),@(5),]];

    NSMutableArray<Node *> * array = [NSMutableArray array];
    Node * temp = root;
    while (temp) {
        [array addObject:temp];
        temp = temp.next;
    }
    root.sibling = array[2];
    array[1].sibling = array.lastObject;
    array[3].sibling = array[1];
    
    // 複製這個root鏈表, 生成一個全新的,但是一樣的鏈表,類似於數組的深拷貝
    // 1.在原始節點後面生成一個一模一樣的節點, A->a->B->b->....->F->f
    // 2.把a.sibling = A.sibling.next;
    // 3.把下標爲奇數的拆出成一個新鏈表
    NSLog(@"原始鏈表root  %@",root);
    
    temp = root;
    // 1.處理原始鏈表,生成一個這樣的結構,A->a->B->b->....->F->f
    while (temp) {
        
        Node * newNode = [[Node alloc] init];
        newNode.data = temp.data;
        newNode.next = temp.next;
        temp.next = newNode;
        
        temp = newNode.next;
    }
    NSLog(@"第一步root  %@",root);
    
    
    // 2.next指針處理好了, 處理sibling指針了
    temp = root;
    // 處理對應的a.sibling = A.sibling.next
    while (temp) {
        
        Node * copyNode = temp.next;
        copyNode.sibling = temp.sibling.next;
        temp = copyNode.next;
    }
    NSLog(@"第二步root  %@",root);
    
    // 3.把下標爲奇數的拆出成新鏈表,原始鏈表復原
    Node * result = root.next;
    Node * resultTemp = nil;

    temp = root;
    while (temp) {
        
        Node * copyNode = temp.next;
        resultTemp.next = copyNode;

        resultTemp = copyNode;
        
        // 原始鏈表復原,A->B->C->D->...
        temp.next = copyNode.next;
        temp = copyNode.next;
        
    }
    NSLog(@"第三步root  %@",root);
    NSLog(@"第三步result  %@",result);

}
#import <Foundation/Foundation.h>

@interface Node : NSObject

// 根據數組生成一個鏈表
+ (Node *)nodeWithArray:(NSArray *)array;

@property (nonatomic, assign) int data;
@property (nonatomic, strong) Node *next;
@property (nonatomic, strong) Node *sibling;/// 隨機的一個節點

@end

------
#import "Node.h"

@implementation Node

+ (Node *)nodeWithArray:(NSArray *)array {
    
    if (array.count==0) {
        return nil;
    }
    
    Node * first = [[Node alloc] init];
    first.data = [array.firstObject intValue];
    Node * preNode = first;
    for (int i = 1; i<array.count; i++) {
        Node * next = [[Node alloc] init];
        next.data = [array[i] intValue];
        preNode.next = next;
        preNode = next;
    }
    return first;
}

- (NSString *)description {
    return [NSString stringWithFormat:@"原始值:%d 下標值:%d  %@",self.data,self.sibling.data,self.next];
}

@end


上面的這種算法對思路的要求太高了, 除非以前專門想過, 否則在面試過程中想出這樣的算法幾乎不可能, 下面是更通用的一種思路.

對於A->B->C->.... 生成一個對應的a->b->c->... , a.data = A.data, a.sibling=A.sibling , 在生成a鏈表的同時, 用一個mapTable保存A(key)->a(value)的映射關係, 
此時a.sibling是指向A鏈表中的某個元素, 需要修改a.sibling指向成a鏈表中的元素,  藉助mapTable的映射關係(A->a,B->b,C->c,D-d), 在遍歷a組成的鏈表, 修改a.sibling = [mapTable objectForKey:a.sibling] , 即可完成a.sibling指向.

這種思路需要藉助一個O(n)的額外空間, 比上一種方式佔用內存更大一點. 但是思路比較清晰好理解, 操作也沒有那麼複雜 


- (void)viewDidLoad {
    [super viewDidLoad];

    [self copyNodeUseDic];    
}


- (void)copyNodeUseDic {
 
    Node * root = [Node nodeWithArray:@[@(1),@(2),@(3),@(4),@(5),]];

    NSMutableArray<Node *> * array = [NSMutableArray array];
    Node * temp = root;
    while (temp) {
        [array addObject:temp];
        temp = temp.next;
    }
    root.sibling = array[2];
    array[1].sibling = array.lastObject;
    array[3].sibling = array[1];
    
    // 複製這個root鏈表, 生成一個全新的,但是一樣的鏈表,類似於數組的深拷貝
    // 1.生成結果鏈表,用一個map建立A-a之間的映射關係,把a.sibling指向A.sibling
    // 2.根據映射關係修改a.sibling
    NSLog(@"原始鏈表root  \n%@",root);
    
    Node * result = nil;
    Node * resultPre = nil;

    NSMapTable * mapTable = [NSMapTable strongToStrongObjectsMapTable];
    temp = root;
    while (temp) {
        Node * newNode = [[Node alloc] init];
        newNode.data = temp.data;
        newNode.sibling = temp.sibling;
        // 建立 A->a之間的映射關係
        [mapTable setObject:newNode forKey:temp];
        
        resultPre.next = newNode;
        
        resultPre = newNode;
        if (result == nil) {
            result = newNode;
        }
        temp = temp.next;
    }
    
    NSLog(@"第一步result \n%@",result);
    // 根據映射關係,修改a.sibling值
    temp = result;
    while (temp) {
        
        // 比如A.sibling = B , 現在a.sibling也是指向B的,B在map對應b, 所以a.sibling = [mapTable objectForKey:a.sibling]
        if (temp.sibling) {
            temp.sibling = [mapTable objectForKey:temp.sibling];
        }
        temp = temp.next;
    }
    
    NSLog(@"第二步result \n%@",result);
    

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章