各种表的应用

目录

哈希表的简单介绍

有序表的简单介绍

链表

题目实战

     题目一   反转单向和双向链表

     题目二  打印两个有序链表的公共部分

     题目三  判断一个链表是否为回文结构

     题目四  将单向链表按某值划分成左边小、中间相等、右边大的形式

     题目五  复制含有随机指针节点的链表

     题目六  两个单链表相交的一系列问题


哈希表的简单介绍

(1)对于java中的哈希函数有HashSet和HashMap,其中HashSet函数是只有key,不带有伴随数据value,而HashMap是带有伴随数据value的,结构组织上没有区别,唯一区别就是有没有value代码展示区别如下:

// hashSet1的key是基础类型->int类型
		HashSet<Integer> hashSet1 = new HashSet<>();//只有key没有value
		hashSet1.add(3);//加入一个值
		System.out.println(hashSet1.contains(3));//查看这个值是否存在
		hashSet1.remove(3);//移去一个值
		System.out.println(hashSet1.contains(3));//查看这个值是否存在
// hashMap1的key是基础类型->String类型
		HashMap<String, Integer> hashMap1 = new HashMap<>();//同时有key和value
		String str1 = "key";
		String str2 = "key";
		hashMap1.put(str1, 1);//加入,str1为key,1为value
		System.out.println(hashMap1.containsKey(str1));//查询某个key是否存在
		System.out.println(hashMap1.containsKey(str2));
		System.out.println(hashMap1.get(str1));//拿出某个key
		System.out.println(hashMap1.get(str2));
        hashMap1.put(str2, 2);
		System.out.println(hashMap1.containsKey(str1));
		System.out.println(hashMap1.containsKey(str2));
		System.out.println(hashMap1.get(str1));
		System.out.println(hashMap1.get(str2));

		hashMap1.remove(str1);//删除某个key
		System.out.println(hashMap1.containsKey(str1));
		System.out.println(hashMap1.containsKey(str2));

       对于上述哈希表中增删改查的操作,无论数据量多大,时间复杂度都是常数级别的O\left ( 1 \right ),但是常数比较大。

(2)放入哈希表的东西,如果是基础类型,内部按值传递,内存占用就是这个东西的大小,因为会在哈希表中拷贝一份;如果不是基础类型,内部按引用传递,内存占用就是这个东西内存地址的大小,一律为八字节。

有序表的简单介绍

(1)对于java中的表示有序表函数有TreeSet和TreeMap,其中TreeSet函数是只有key,不带有伴随数据value,而TreeMap是带有伴随数据value的,结构组织上没有区别,唯一区别就是有没有value。代码展示区别如下:

// treeSet的key是非基础类型->Node类型
   TreeSet<Node> treeSet = new TreeSet<>();
        nodeA = new Node(5);
		nodeB = new Node(3);
		nodeC = new Node(7);
TreeMap<Integer, String> treeMap1 = new TreeMap<>();
		treeMap1.put(7, "我是7");
		treeMap1.put(5, "我是5");
		treeMap1.put(4, "我是4");
		treeMap1.put(3, "我是3");
		treeMap1.put(9, "我是9");
		treeMap1.put(2, "我是2");
        //以下是有序表的一些固定操作
        System.out.println(treeMap1.containsKey(5));
		System.out.println(treeMap1.get(5));
		System.out.println(treeMap1.firstKey() + ", 我最小");
		System.out.println(treeMap1.lastKey() + ", 我最大");
		System.out.println(treeMap1.floorKey(8) + ", 在表中所有<=8的数中,我离8最近");
		System.out.println(treeMap1.ceilingKey(8) + ", 在表中所有>=8的数中,我离8最近");
		System.out.println(treeMap1.floorKey(7) + ", 在表中所有<=7的数中,我离7最近");
		System.out.println(treeMap1.ceilingKey(7) + ", 在表中所有>=7的数中,我离7最近");
		treeMap1.remove(5);
		System.out.println(treeMap1.get(5) + ", 删了就没有了哦");

       它和哈希表的区别就是哈希表内部是无序组织key的,而有序表内部是有序组织key的,所以有很多哈希表没有的功能,上面代码就可以看出。性能上比哈希表差,有序表增删改查的操作时间复杂度都是O\left ( logN \right )

(2)红黑树、AVL数、size-balance-tree和跳表等都属于有序表结构,只是底层具体实现不同。

(3)放入有序表的东西,如果是基础类型,內部按值传递,内存占用就是这个东西的大小;如果不是基础类型,必须提供比较器,内部按引用传递,内存占用是这个东西内存地址的大小。

链表

面试时链表解题的方法论:

(1)对于笔试,不用太在乎空间复杂度,一切为了时间复杂度

(2)对于面试,时间复杂度依然放在第一位,但是一定要找到空间复杂度最省的方法

重要技巧:

(1)额外数据结构记录

(2)快慢指针

题目实战

    题目一   反转单向和双向链表

      分别实现反转单向链表和反转双向链表的函数,要求:如果链表的长度为N,时间复杂度要求为O\left ( N \right ),额外空间复杂度为O\left ( 1 \right )

public class Code02_ReverseList {

	public static class Node {//不含头节点,单向链表的建立
		public int value;
		public Node next;

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

	public static Node reverseList(Node head) {//反转单向链表
		Node pre = null;
		Node next = null;
		while (head != null) {
			next = head.next;
			head.next = pre;
			pre = head;
			head = next;
		}
		return pre;
	}

	public static class DoubleNode {//双向链表的建立
		public int value;
		public DoubleNode last;
		public DoubleNode next;

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

	public static DoubleNode reverseList(DoubleNode head) {//反转双向链表
		DoubleNode pre = null;
		DoubleNode next = null;
		while (head != null) {
			next = head.next;
			head.next = pre;
			head.last = next;
			pre = head;
			head = next;
		}
		return pre;
	}

	public static void printLinkedList(Node head) {//打印单向反转链表
		System.out.print("Linked List: ");
		while (head != null) {
			System.out.print(head.value + " ");
			head = head.next;
		}
		System.out.println();
	}

	public static void printDoubleLinkedList(DoubleNode head) {//打印双向反转链表
		System.out.print("Double Linked List: ");
		DoubleNode end = null;
		while (head != null) {
			System.out.print(head.value + " ");
			end = head;
			head = head.next;
		}
		System.out.print("| ");
		while (end != null) {
			System.out.print(end.value + " ");
			end = end.last;
		}
		System.out.println();
	}

	public static void main(String[] args) {//主函数
		Node head1 = new Node(1);
		head1.next = new Node(2);
		head1.next.next = new Node(3);
		printLinkedList(head1);
		head1 = reverseList(head1);
		printLinkedList(head1);

		DoubleNode head2 = new DoubleNode(1);
		head2.next = new DoubleNode(2);
		head2.next.last = head2;
		head2.next.next = new DoubleNode(3);
		head2.next.next.last = head2.next;
		head2.next.next.next = new DoubleNode(4);
		head2.next.next.next.last = head2.next.next;
		printDoubleLinkedList(head2);
		printDoubleLinkedList(reverseList(head2));

	}

}

        对于链表这一块的代码,一定要动手画一画就很容易理解。 

     题目二  打印两个有序链表的公共部分

       给定两个有序链表的头指针head1和head2,打印两个链表的公共部分。如果两个链表的长度之和为N,时间复杂度要求为O\left ( N \right ),额外空间复杂度要求为O\left ( 1 \right )

    public class Code03_PrintCommonPart {

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

	public static void printCommonPart(Node head1, Node head2) {//打印公共部分
		System.out.print("Common Part: ");
		while (head1 != null && head2 != null) {//当两个链表的头指针都不指向空时
            //两个指针指的值比较,谁小谁上面的指针向下移动,相等时打印所指向的值
			if (head1.value < head2.value) {
				head1 = head1.next;
			} else if (head1.value > head2.value) {
				head2 = head2.next;
			} else {
				System.out.print(head1.value + " ");
				head1 = head1.next;
				head2 = head2.next;
			}
		}
		System.out.println();
	}

	public static void printLinkedList(Node node) {//打印公共部分
		System.out.print("Linked List: ");
		while (node != null) {
			System.out.print(node.value + " ");
			node = node.next;
		}
		System.out.println();
	}

	public static void main(String[] args) {//主函数
		Node node1 = new Node(2);
		node1.next = new Node(3);
		node1.next.next = new Node(5);
		node1.next.next.next = new Node(6);

		Node node2 = new Node(1);
		node2.next = new Node(2);
		node2.next.next = new Node(5);
		node2.next.next.next = new Node(7);
		node2.next.next.next.next = new Node(8);

		printLinkedList(node1);
		printLinkedList(node2);
		printCommonPart(node1, node2);

	}

}

     题目三  判断一个链表是否为回文结构

       给定一个单链表的头节点head,请判断该链表是否为回文结构。要求如果链表长度为N,时间复杂度达到O\left ( N \right ),额外空间复杂度达到O\left ( 1 \right )

     (1)如果在笔试中遇到,不考虑空间复杂度的情况下,可以直接把这个结构放进栈里,然后依次弹出,同时原结构从前往后比对,如果栈空之前比对都相同,则是回文结构,否则不是。

// 需要n个额外空间
public static boolean isPalindrome1(Node head) {
	Stack<Node> stack = new Stack<Node>();
	Node cur = head;
	while (cur != null) {
		stack.push(cur);//依次入栈
		cur = cur.next;
	}
	while (head != null) {
		if (head.value != stack.pop().value) {//依次出栈比对
			return false;
		}
		head = head.next;
	}
	return true;//栈为空之前比对均相同,是回文结构
}

        (2)巧妙入栈可以更省空间一些,即对于一个结构,只入栈这个结构的一半,然后出栈比对。要想实现这个操作,需要用到一个技巧,也就是快慢指针,简单理解就是定义两个指针,两个指针的移动速度不同,快指针一次走两步,慢指针一次走一步,快指针走到最后时,慢指针指向结构的中间。

// 只需要n/2的空间
public static boolean isPalindrome2(Node head) {
	if (head == null || head.next == null) {
		return true;
	}//如果头指针为空或者头指针的下一个为空,是回文结构,直接返回true
	Node right = head.next;//慢指针
	Node cur = head;//快指针
	while (cur.next != null && cur.next.next != null) {//当快指针均没有指到最后
		right = right.next;//慢指针一次移动一步
		cur = cur.next.next;//快指针一次移动两步
	}//循环结束,慢指针指向中间位置
	Stack<Node> stack = new Stack<Node>();//引入栈
	while (right != null) {//当慢指针不为空
		stack.push(right);//慢指针所指的元素依次入栈
		right = right.next;//慢指针向后移动
	}
	while (!stack.isEmpty()) {//当栈不为空时
		if (head.value != stack.pop().value) {//出栈依次比对
			return false;
		}
		head = head.next;
	}
	return true;
}

       对于其他种类的快慢指针的问题,可以根据需求,让快指针先走几步或者慢指针先走几步,或者其他的操作,根据需要改写。 

        (3)不使用额外空间来解决这个问题,当面试中遇到这个问题采用此方式解决。 

// 不需要额外空间
public static boolean isPalindrome3(Node head) {
	if (head == null || head.next == null) {
		return true;
	}//如果头指针为空或者头指针下一个为空,为回文结构,直接返回
	Node n1 = head;
	Node n2 = head;
	while (n2.next != null && n2.next.next != null) { // 利用快慢指针,找到中间位置
		n1 = n1.next; // 慢指针
		n2 = n2.next.next; // 快指针
	}
    //将右半部分的指针反转过来
	n2 = n1.next; // n2指向右边部分的第一个节点
	n1.next = null; 
	Node n3 = null;
	while (n2 != null) { 
		n3 = n2.next; 
		n2.next = n1; 
		n1 = n2;//n1移动 
		n2 = n3; //n2移动
	}
	n3 = n1; // n3指向最后一个节点
	n2 = head;// n2指向第一个节点
	boolean res = true;
	while (n1 != null && n2 != null) { // 当两指针都没有指向空时,检查两个指针所指的元素是否相同
		if (n1.value != n2.value) {
			res = false;
			break;
		}
		n1 = n1.next; // n1向中间移动
		n2 = n2.next; //n2向中间移动
	}
	n1 = n3.next;
	n3.next = null;
	while (n1 != null) { // 右半部分的指针方向折过去
		n2 = n1.next;
		n1.next = n3;
		n3 = n1;
		n1 = n2;
	}
	return res;
}

     题目四  将单向链表按某值划分成左边小、中间相等、右边大的形式

       给定一个单链表的头节点head,节点的值类型是整型,再给定一个整数pivot。实现一个调整链表的函数,将链表调整为左部分都是值小于pivot的节点,中间部分都是值等于pivot的节点,右部分都是值大于pivot的节点。

       [进阶]在实现原问题功能的基础上增加如下的要求,要求调整后所有小于pivot的节点之间的相对顺序和调整前一样,调整后所有等于pivot的节点之间的相对顺序和调整前一样,调整后所有大于piovt的节点之间的相对顺序和调整前一样。同时时间复杂度达到O\left ( N \right ),额外空间复杂度达到O\left ( 1 \right )

     (1)针对原题目的第一种解决方式,在笔试中,不追求空间复杂度最小,直接将单链表的值放进node数组中,利用快速排序中的partition函数将小于pivot的放左边,等于的放中间,大于的放右边。

public static Node listPartition1(Node head, int pivot) {
	if (head == null) {
		return head;
	}//如果头指针指向空,直接返回头指针所指的值
	Node cur = head;
	int i = 0;
	while (cur != null) {
		i++;
		cur = cur.next;
	}//统计单链表的长度
	Node[] nodeArr = new Node[i];//定义一个和单链表长度相等的node数组
	i = 0;
	cur = head;
	for (i = 0; i != nodeArr.length; i++) {
		nodeArr[i] = cur;
		cur = cur.next;
	}//将单链表中的值依次放入数组中
	arrPartition(nodeArr, pivot);//使用partition函数划分
	for (i = 1; i != nodeArr.length; i++) {
		nodeArr[i - 1].next = nodeArr[i];
	}//将node数组中加上指向箭头方向
	nodeArr[i - 1].next = null;
	return nodeArr[0];//返回数组第一个node
}

public static void arrPartition(Node[] nodeArr, int pivot) {
	int small = -1;
	int big = nodeArr.length;
	int index = 0;
	while (index != big) {
		if (nodeArr[index].value < pivot) {
			swap(nodeArr, ++small, index++);
		} else if (nodeArr[index].value == pivot) {
			index++;
		} else {
			swap(nodeArr, --big, index);
		}
	}
}

public static void swap(Node[] nodeArr, int a, int b) {
	Node tmp = nodeArr[a];
	nodeArr[a] = nodeArr[b];
	nodeArr[b] = tmp;
}

        (2)题目进阶需要划分前后稳定,采用下面的方法实现。关于链表代码的理解多去看,去画,可以很好的理解。

public static Node listPartition2(Node head, int pivot) {
	Node sH = null; // small head
	Node sT = null; // small tail
	Node eH = null; // equal head
	Node eT = null; // equal tail
	Node bH = null; // big head
	Node bT = null; // big tail
	Node next = null; 
	// every node distributed to three lists
	while (head != null) {
		next = head.next;
		head.next = null;
		if (head.value < pivot) {
			if (sH == null) {
				sH = head;
				sT = head;
			} else {
				sT.next = head;
				sT = head;
			}
		} else if (head.value == pivot) {
			if (eH == null) {
				eH = head;
				eT = head;
			} else {
				eT.next = head;
				eT = head;
			}
		} else {
			if (bH == null) {
				bH = head;
				bT = head;
			} else {
				bT.next = head;
				bT = head;
			}
		}
		head = next;
	}
    // small and equal reconnect
	if (sT != null) {
		sT.next = eH;
		eT = eT == null ? sT : eT;
	}
	// all reconnect
	if (eT != null) {
		eT.next = bH;
	}
	return sH != null ? sH : eH != null ? eH : bH;
}

      题目五  复制含有随机指针节点的链表

         一种特殊的单链表节点类型描述如下:

class Node {
       int value;
       Node next;
       Node rand;
       Node(int val){
            value=val;
       }
}

       rand指针是单链表节点结构中新增的指针,rand可能指向链表中的任意一个节点,也可能指向null。给定一个由Node节点类型组成的无环单链表的头节点head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头节点。要求时间复杂度O\left ( N \right ),额外空间复杂度O\left ( 1 \right )

        (1)在笔试中,不考虑使用额外空间时可以使用哈希表进行求解,具体求解代码如下:

public static Node copyListWithRand1(Node head) {
	HashMap<Node, Node> map = new HashMap<Node, Node>();
	Node cur = head;
	while (cur != null) {//通过cur指针进行移动,将旧节点克隆一遍
		map.put(cur, new Node(cur.value));
		cur = cur.next;
	}
	cur = head;
	while (cur != null) {
        //cur老节点
        //map.get(cur)新节点
		map.get(cur).next = map.get(cur.next);//新节点的下一个连接的是老节点的下一个节点的克隆节点
		map.get(cur).rand = map.get(cur.rand);//新节点的rand指针指向的是老节点的rand指针所指的老节点的新节点
		cur = cur.next;
	}
	return map.get(head);//返回克隆链表的头
}

        (2)在面试中不使用额外空间的做法,具体代码如下:

public static Node copyListWithRand2(Node head) {
	if (head == null) {
		return null;
	}//如果头指针指向空,直接返回空
	Node cur = head;
	Node next = null;
	//克隆每一个node并且将每一个node的下一个连接为自己克隆的node
	while (cur != null) {
		next = cur.next;
		cur.next = new Node(cur.value);//克隆新节点
		cur.next.next = next;
		cur = next;
	}
	cur = head;
	Node curCopy = null;
	// 设置克隆节点的rand指针
	while (cur != null) {
		next = cur.next.next;
		curCopy = cur.next;
		curCopy.rand = cur.rand != null ? cur.rand.next : null;
		cur = next;
	}
	Node res = head.next;
	cur = head;
	// 分离出来克隆节点
	while (cur != null) {
		next = cur.next.next;
		curCopy = cur.next;
		cur.next = next;
		curCopy.next = next != null ? next.next : null;
		cur = next;
	}
	return res;
}

      题目六  两个单链表相交的一系列问题

        给定两个可能有环也可能无环的单链表,头节点head1和head2。实现一个函数,如果两个链表相交,请返回相交的第一个节点。如果不相交,返回null。要求如果两个链表长度之和为N,时间复杂度达到O\left ( N \right ),额外空间复杂度达到O\left ( 1 \right )

     (1) 首先补充一个判断一个单链表是否有环,没有直接返回false,有的话返回第一个入环节点的位置的方法。

       1.先介绍需要使用额外空间的解决方法,从头开始遍历每一个节点,放入哈希表中,依次遍历,同时查询此时哈希表中是否含有此节点,如果没有,继续查询,直到为空证明无环;如果有,第一次重复的节点就是第一个入环节点。    

       2.下面介绍的这个方法是不需要额外空间的解决方法,设置快慢指针,都从头开始,快指针一次走两步,慢指针一次走一步,如果快指针走到空,那么证明单链表没有环;而如果两个指针相遇,证明有环,此时快指针回到第一个位置,慢指针原地不动,然后两个指针都一次只走一步,开始移动,两者将在第一个入环节点处相遇。 

//找到链表第一个入环节点,如果无环,返回null
public static Node getLoopNode(Node head) {
	if (head == null || head.next == null || head.next.next == null) {
		return null;//如果head为空或者head下一个为空或者head下下一个节点为空,直接返回空
	}
	Node n1 = head.next; //慢指针
	Node n2 = head.next.next; //快指针
	while (n1 != n2) {
		if (n2.next == null || n2.next.next == null) {//如果快指针下一个,下下一个不指向空
			return null;
		}
		n2 = n2.next.next;
		n1 = n1.next;
	}
	n2 = head; //快指针指向头部
	while (n1 != n2) {
		n1 = n1.next;
		n2 = n2.next;
	}
	return n1;
}

    (2)判断完两个单链表是否有环以后,可以分为以下两种情况(其他情况均不可能存在):

       1.当两个单链表均没有环时,首先先判断两个单链表的尾节点地址是不是相同,如果两个单链表相交,那么尾节点的地址一定相同。如果不相同,则两者不相交,返回空。如果相同,寻找第一个相交节点,统计两条单链表的长度,然后开始扫描两个链表,让长链表先走差值步数,然后两个链表开始,依次扫描,直到两个指针所指的地址相同,则此时所指节点为第一个相交节点。

public static Node noLoop(Node head1, Node head2) {//两个单链表均无环
	if (head1 == null || head2 == null) {
		return null;
	}
	Node cur1 = head1;
	Node cur2 = head2;
	int n = 0;
	while (cur1.next != null) {//统计第一个单链表的长度
	    n++;
		cur1 = cur1.next;
	}//cur1指向尾节点
	while (cur2.next != null) {//计算第二个单链表的长度,得到的n是两者长度的差值
		n--;
		cur2 = cur2.next;
	}//cur2指向尾节点
	if (cur1 != cur2) {//如果两者尾节点地址不同,返回null
		return null;
	}
	cur1 = n > 0 ? head1 : head2;//根据n的正负得出哪一个链表更长,cur1指向长链表
	cur2 = cur1 == head1 ? head2 : head1;//cur2指向短链表
	n = Math.abs(n);//对n取绝对值
	while (n != 0) {
		n--;
		cur1 = cur1.next;
	}//让长链表走差值步数
	while (cur1 != cur2) {//对两个链表开始一一比对,相交时循环结束
		cur1 = cur1.next;
		cur2 = cur2.next;
	}
	return cur1;//返回第一个相交节点
}

         2.当两个链表均有环,具体实现代码如下:

public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {//两个单链表均是有环节点,输入的分别是两个链表的头节点和入环节点
	Node cur1 = null;
	Node cur2 = null;
	if (loop1 == loop2) {//当两个单链表的入环节点相同,和无环链表实现的方式相同
		cur1 = head1;
		cur2 = head2;
		int n = 0;
		while (cur1 != loop1) {
			n++;
			cur1 = cur1.next;
		}
		while (cur2 != loop2) {
			n--;
			cur2 = cur2.next;
		}
		cur1 = n > 0 ? head1 : head2;
		cur2 = cur1 == head1 ? head2 : head1;
		n = Math.abs(n);
		while (n != 0) {
			n--;
			cur1 = cur1.next;
		}
		while (cur1 != cur2) {
			cur1 = cur1.next;
			cur2 = cur2.next;
		}
		return cur1;
	} else {//第一个入环节点不同的情况
		cur1 = loop1.next;//指向第一个链表的入环节点下一个
		while (cur1 != loop1) {//第一个链表的入环节点继续往下走
			if (cur1 == loop2) {//如果和第二个链表的入环节点相遇,返回第一个链表的入环节点
				return loop1;
			}
			cur1 = cur1.next;
		}
		return null;
	}
}

        对于整个问题的完整实现代码如下:每一部分的时间复杂度都是O\left ( N \right ),空间复杂度也都是O\left ( 1 \right )

public class Code FindFirstIntersectNode {

    public static class Node {
		public int value;
		public Node next;

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

	public static Node getIntersectNode(Node head1, Node head2) {//调用判断主要操作的函数
		if (head1 == null || head2 == null) {
			return null;
		}
		Node loop1 = getLoopNode(head1);
		Node loop2 = getLoopNode(head2);
		if (loop1 == null && loop2 == null) {
			return noLoop(head1, head2);
		}
		if (loop1 != null && loop2 != null) {
			return bothLoop(head1, loop1, head2, loop2);
		}
		return null;
	}

	public static Node getLoopNode(Node head) {
		if (head == null || head.next == null || head.next.next == null) {
			return null;
		}
		Node n1 = head.next; // n1 -> slow
		Node n2 = head.next.next; // n2 -> fast
		while (n1 != n2) {
			if (n2.next == null || n2.next.next == null) {
				return null;
			}
			n2 = n2.next.next;
			n1 = n1.next;
		}
		n2 = head; // n2 -> walk again from head
		while (n1 != n2) {
			n1 = n1.next;
			n2 = n2.next;
		}
		return n1;
	}

	public static Node noLoop(Node head1, Node head2) {
		if (head1 == null || head2 == null) {
			return null;
		}
		Node cur1 = head1;
		Node cur2 = head2;
		int n = 0;
		while (cur1.next != null) {
			n++;
			cur1 = cur1.next;
		}
		while (cur2.next != null) {
			n--;
			cur2 = cur2.next;
		}
		if (cur1 != cur2) {
			return null;
		}
		cur1 = n > 0 ? head1 : head2;
		cur2 = cur1 == head1 ? head2 : head1;
		n = Math.abs(n);
		while (n != 0) {
			n--;
			cur1 = cur1.next;
		}
		while (cur1 != cur2) {
			cur1 = cur1.next;
			cur2 = cur2.next;
		}
		return cur1;
	}

	public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
		Node cur1 = null;
		Node cur2 = null;
		if (loop1 == loop2) {
			cur1 = head1;
			cur2 = head2;
			int n = 0;
			while (cur1 != loop1) {
				n++;
				cur1 = cur1.next;
			}
			while (cur2 != loop2) {
				n--;
				cur2 = cur2.next;
			}
			cur1 = n > 0 ? head1 : head2;
			cur2 = cur1 == head1 ? head2 : head1;
			n = Math.abs(n);
			while (n != 0) {
				n--;
				cur1 = cur1.next;
			}
			while (cur1 != cur2) {
				cur1 = cur1.next;
				cur2 = cur2.next;
			}
			return cur1;
		} else {
			cur1 = loop1.next;
			while (cur1 != loop1) {
				if (cur1 == loop2) {
					return loop1;
				}
				cur1 = cur1.next;
			}
			return null;
		}
	}

	public static void main(String[] args) {
		// 1->2->3->4->5->6->7->null
		Node head1 = new Node(1);
		head1.next = new Node(2);
		head1.next.next = new Node(3);
		head1.next.next.next = new Node(4);
		head1.next.next.next.next = new Node(5);
		head1.next.next.next.next.next = new Node(6);
		head1.next.next.next.next.next.next = new Node(7);

		// 0->9->8->6->7->null
		Node head2 = new Node(0);
		head2.next = new Node(9);
		head2.next.next = new Node(8);
		head2.next.next.next = head1.next.next.next.next.next; // 8->6
		System.out.println(getIntersectNode(head1, head2).value);

		// 1->2->3->4->5->6->7->4...
		head1 = new Node(1);
		head1.next = new Node(2);
		head1.next.next = new Node(3);
		head1.next.next.next = new Node(4);
		head1.next.next.next.next = new Node(5);
		head1.next.next.next.next.next = new Node(6);
		head1.next.next.next.next.next.next = new Node(7);
		head1.next.next.next.next.next.next = head1.next.next.next; // 7->4

		// 0->9->8->2...
		head2 = new Node(0);
		head2.next = new Node(9);
		head2.next.next = new Node(8);
		head2.next.next.next = head1.next; // 8->2
		System.out.println(getIntersectNode(head1, head2).value);

		// 0->9->8->6->4->5->6..
		head2 = new Node(0);
		head2.next = new Node(9);
		head2.next.next = new Node(8);
		head2.next.next.next = head1.next.next.next.next.next; // 8->6
		System.out.println(getIntersectNode(head1, head2).value);

	}

}

  • 11
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

互联网的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值