阿里巴巴测试开发校招二面的一个面试题
一个单链表除了next指针外,还带有一个随机指针(设为rand)指向任意元素,用最少的时间复杂度和最少的空间复制该链表。
将这个链表看成是一个图,一个有向的图,用一个二维的数组来记录这个图的每个节点的指向。然后根据这个数组来建立相应的图。由于需要知道每个节点的随机节点的指向,故需要遍历整个链表。所以n个节点,所以时间复杂度就是O(n^2)了。
回到宿舍后,和几个朋友一番讨论,想到一个O(n)的方法。
方法2:
一个单链表除了next指针外,还带有一个随机指针(设为rand)指向任意元素,用最少的时间复杂度和最少的空间复制该链表。
开始想了很久,只想到一个O(n^2)的方法,显然没能令面试官满意,面试官也没有告诉我正确的方法,只是对我的思路表示肯定。下面先介绍一个我的面试的时候想到的方法。
方法1:将这个链表看成是一个图,一个有向的图,用一个二维的数组来记录这个图的每个节点的指向。然后根据这个数组来建立相应的图。由于需要知道每个节点的随机节点的指向,故需要遍历整个链表。所以n个节点,所以时间复杂度就是O(n^2)了。
回到宿舍后,和几个朋友一番讨论,想到一个O(n)的方法。
方法2:
可以用O(n)的时间复杂度和O(1)的附加空间实现:根据源链表依次复制出新链表的对应节点,并将新节点插入到源节点后,新链表的rand指针指向原链表的rand;然后遍历一遍链表,将新链表的rand指针指向对应元素的next元素(即新链表中对应的rand指针元素);最后将新链表和源链表分离。
struct node_t {
int data;
struct node_t *next;
struct node_t *rand;
};
struct node_t * copylist(struct node_t **dest, struct node_t *src) {
struct node_t *p, *q;
p = src;
/* 在原节点后插入一个新的节点,原节点的next指针指向新节点,新节点的随机指针指向原节点的随机指针的节点 */
while (p != NULL) {
q = (struct node_t *)malloc(sizeof(struct node_t));
q->data = p->data;
q->next = p->next; q->rand = p->rand;
p->next = q;
p = q->next;
}
/* 处理新的节点,新节点的随机指针指向原节点下随机指针的下一个节点 */
p = src;
while (p != NULL) {
q = p->next;
q->rand = q->rand->next;
p = q->next;
}
p = src;
*dest = p->next;
/* 将原节点和新节点分离,恢复原节点 */
while (p != NULL) {
q = p->next;
p->next = q->next;
p = p->next;
if (p != NULL)
q->next = p->next;
}
return *dest;
}
方法2是改变next指针,也可以考虑改变随机指针,思路是一样的。