算法 (十一)链表相关:复制含有随机指针节点的链表

1、复制含有随机指针节点的链表

1. 1 简单描述

就是单链表的每一个节点不只有next,还多了个rand,指向其他的节点或者指向null

1. 2 思路

有两种方法,

  1. 第一种是利用外部辅助–hashMap,你来一个我存一个,并且是键值对,逐个对应,然后输出的时候再建立复制链表的节点之间的关系,空间复杂度为O(N)
  2. 第二种就是在源链表的每个节点后面插入我们要复制的节点,1 > 1_ > 2 > 2_ > 3 > 3_,其中带下标的是我们复制的链表节点,建立节点之间的关系,最后split,分割变成两个链表,空间复杂度为O(1)

具体请看如下代码:

package cn.nupt;

import java.util.HashMap;

/**
 * @Description: 复制含有随机指针节点的链表,返回复制节点的头节点
 *
 * @author PizAn
 * @Email pizan@foxmail.com
 * @date 2019年2月19日 下午8:25:07
 * 
 */
public class CopyListWithRandom {

	// 有两种方法,第一种是利用外部辅助--hashMap,你来一个我存一个,并且是键值对,一一对应,然后输出的时候再建立复制链表的节点之间的关系
	// 第二种就是O(1),1 > 1_ > 2 > 2_ > 3 > 3_,
	// 其中带下标的是我们复制的链表节点,建立节点之间的关系,最后split,分割变成两个链表

	// 这次是带有随机指针节点rand的链表
	public static class Node {
		public int value;
		public Node next;
		public Node rand;

		public Node(int data) {
			this.value = data;
		}
	}

	// 第一种是利用外部辅助--hashMap,需要O(N)空间复杂度
	public static Node copyListWithRandom01(Node head) {

		// 1、给出一个辅助hashMap,其键值对都是Node类型
		HashMap<Node, Node> hashMap = new HashMap<Node, Node>();

		// 2、将源链表当作键存入hashmap中,每个节点对应一个复制(新)节点

		Node cur = head; // 老规矩,用临时节点代替头节点,因为头节点下面可能要用,最好不要改变
		while (cur != null) { // 遍历链表
			hashMap.put(cur, new Node(cur.value)); // 这里就很巧妙,一个源链表的节点对应一个复制链表的节点,节点间的关系下面会给出
			cur = cur.next;

		}

		// 3、从hashmap里面取数,建立我们复制链表节点间的关系
		cur = head; // 重制cur,省一个变量

		while (cur != null) {
			hashMap.get(cur).next = hashMap.get(cur.next); // 看的出来么,很巧妙,当前值的next就是键的next所代表的数
			hashMap.get(cur).rand = hashMap.get(cur.rand);

		}

		return hashMap.get(head);

	}

	// 第二种就是O(1),1 > 1_ > 2 > 2_ > 3 > 3_ > null ,只用变量,空间复杂度为O(1)
	public static Node copyListWithRandom02(Node head) {

		if (head == null) {
			return head;
		}
		Node cur = head;
		Node next = null;// 搞一个用于交换的节点
		// 1、将1 > 2 > 3 > null变成1 > 1_ > 2 > 2_ > 3 > 3_ > null
		while (cur != null) {
			next = cur.next; // 先把2存储一下
			cur.next = new Node(cur.value);// 让cur指向我们复制的cur节点,即上面的1_
			cur.next.next = next; // 这个复制的节点指向2
			cur = next; // 调整头节点的位置,继续遍历

		}

		// 2、调整复制链表节点之间的关系
		cur = head;
		Node copNode;
		while (cur != null) {
			next = cur.next.next;
			copNode = cur.next;

			// 因为copNode的next还是没有变,这里只要把rand设置一下就行了
			copNode.rand = cur.rand == null ? null : cur.rand.next;

			cur = next; // 调整cur,继续遍历

		}

		// 3、split 1 > 1_ > 2 > 2_ > 3 > 3_ > null,变成两个链表

		cur = head;
		Node res = head.next;

		while (cur != null) {
			next = cur.next.next;
			copNode = cur.next;
			cur.next = next;
			copNode.next = next == null ? null : next.next;
			cur = next;

		}

		return res;

	}

	// 打印链表,这里按照next和rand各打印了一次
	public static void printRandLinkedList(Node head) {
		Node cur = head;
		System.out.print("order: ");
		while (cur != null) {
			System.out.print(cur.value + " ");
			cur = cur.next;
		}
		System.out.println();
		cur = head;
		System.out.print("rand:  ");
		while (cur != null) {
			// 和上面唯一不一样的就是这里,因为rand可能会指向null,所以这里弄个选择语句,指空就输出"- "
			System.out.print(cur.rand == null ? "- " : cur.rand.value + " ");
			cur = cur.next;
		}
		System.out.println();
	}

	public static void main(String[] args) {

		// 测试头节点为null的情况
		Node head = null;
		Node res1 = null;
		Node res2 = null;
		printRandLinkedList(head);
		res1 = copyListWithRandom01(head);
		printRandLinkedList(res1);
		res2 = copyListWithRandom02(head);
		printRandLinkedList(res2);
		printRandLinkedList(head);
		System.out.println("=========================");

		// 建立链表
		head = new Node(1);
		head.next = new Node(2);
		head.next.next = new Node(3);
		head.next.next.next = new Node(4);
		head.next.next.next.next = new Node(5);
		head.next.next.next.next.next = new Node(6);

		head.rand = head.next.next.next.next.next; // 1 -> 6
		head.next.rand = head.next.next.next.next.next; // 2 -> 6
		head.next.next.rand = head.next.next.next.next; // 3 -> 5
		head.next.next.next.rand = head.next.next; // 4 -> 3
		head.next.next.next.next.rand = null; // 5 -> null
		head.next.next.next.next.next.rand = head.next.next.next; // 6 -> 4

		// 测试正常的链表
		printRandLinkedList(head);
		res1 = copyListWithRandom01(head);
		printRandLinkedList(res1);
		res2 = copyListWithRandom02(head);
		printRandLinkedList(res2);
		printRandLinkedList(head);
		System.out.println("=========================");

	}

}

输出:

order: 
rand:  
order: 
rand:  
order: 
rand:  
order: 
rand:  
=========================
order: 1 2 3 4 5 6 
rand:  6 6 5 3 - 4 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值