大家好,都吃晚饭了吗?我是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指针了)

java中好单链表的深拷贝 复杂链表的复制 java_java中好单链表的深拷贝


然后这个复制的节点的rand指针就是它前面的原配节点的rand指针的下一个节点(如图)

java中好单链表的深拷贝 复杂链表的复制 java_指针_02


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;
}

总结

没想到吧,就连最简单的复制操作都可以整出幺蛾子,这更加突出了掌握基础的重要性,没错,这些都是基础,面试中也是经常会出现,如果您被问到这个问题了,如果您没有往这些方向想的话,就已经输了一半了!