輸入一個複雜鏈表(每個節點中有節點值,以及兩個指針,一個指向下一個節點,另一個特殊指針指向任意一個節點,虛線部分),返回結果爲複製後複雜鏈表的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);
}