大家好,都吃晚饭了吗?我是Kaiqisan,是一个已经走出社恐的一般生徒,今天讲讲复杂链表的复制
一个链表在原来的基础上新增一个指针,随机指向任意一个节点(可能是null)
然后要求你复制链表
随机链表的生成
// 复杂链表
public class RandList extends LinkList {
RandList rand = null; // 随机元素指针
RandList next = null; // 下一个元素指针
public RandList(int val) {
super(val);
}
// 自己封装的方法,方便遍历链表
public void readList() {
RandList temp = this;
while (temp != null) {
System.out.print(temp.val);
System.out.print(temp.next == null ? "" : "->");
temp = temp.next;
}
}
}
int[] arr = {3, 2, 4, 5, 6, 7, 2};
RandList list = new RandList(arr[0]);
RandList head = list;
for (int i = 1; i < arr.length; i++) {
list.next = new RandList(arr[i]);
list = list.next;
}
RandList _head = head;
RandList temp;
for (int i = 0; i < arr.length; i++) {
temp = head;
int rand = (int) (Math.random() * arr.length);
while (rand > 0) {
rand--;
temp = temp.next;
}
if (temp != _head) {
_head.rand = temp;
}
_head = _head.next;
}
思路
如果这是普通的链表,直接new一个新链表然后遍历,逐个链接,但如果是上述的复杂链表,还有额外的随机节点,所以如果还是使用前面的遍历复制的方法的话,在出现随机节点指向后面的节点的时候,就无法实现这个随机节点的复制。
所以要另寻出路
- 方法1:使用map结构
使用HashMap,第一次遍历把当前的节点存入键(key),然后在当前的值(value)中存入和键中的元素值一样的节点(此时值中的节点没有next 和 rand指针)
然后第二次遍历给值(value)中的链表给定指针
static RandList doCopy(RandList preList) {
HashMap<RandList, RandList> map = new HashMap<>();
RandList _head = preList;
while (_head != null) {
map.put(_head, new RandList(_head.val));
_head = _head.next;
}
_head = preList;
while (_head != null) {
map.get(_head).next = map.get(_head.next);
map.get(_head).rand = map.get(_head.rand);
_head = _head.next;
}
return map.get(preList);
}
- 方法2:有丝分裂法(自己命名的)
先如图所示先复制链表的元素(为了方便,先不画rand指针了)
然后这个复制的节点的rand指针就是它前面的原配节点的rand指针的下一个节点(如图)
Node(1).rand = Node(3)
Node(1`).rand = Node(1).rand.next = Node(3).next
然后根据这个原理编排好所有的“被复制”节点的rand指针的信息,最后把这个新的链表重新从整个长链表中抽离出来
static RandList doCopy2(RandList preList) {
RandList _head = preList;
RandList temp;
RandList temp2;
// 元素复制
while (_head != null) {
temp = _head;
_head = _head.next;
temp2 = new RandList(temp.val);
temp.next = temp2;
temp2.next = _head;
}
// 重新赋值,开始赋值rand节点和抽离
_head = preList;
RandList res = preList.next;
while (_head != null) {
temp = _head;
// 分割两个链表
temp2 = _head.next;
_head = _head.next.next;
temp.next = temp2.next;
temp2.rand = temp.rand == null ? null : temp.rand.next;
}
return res;
}
总结
没想到吧,就连最简单的复制操作都可以整出幺蛾子,这更加突出了掌握基础的重要性,没错,这些都是基础,面试中也是经常会出现,如果您被问到这个问题了,如果您没有往这些方向想的话,就已经输了一半了!