1
用数组结构实现大小固定的栈和队列(一面常见题目)
【思路】
栈:
用 index 控制 push() 和 pop() 。
当 index =0 时,不可以 pop();
当 index = 数组长度 时,不可以 push()。
队列:
循环队列,设置 start(重点)、end 和 size; //用size 解耦 start 和 end
用 size 控制 start 和 end 的 存取,size=0时,不可以操作 start ,size = 数组大小 时,不可以操作 end。
public class Array_To_Stack_Queue {
public static class ArrayStack {
private Integer[] arr;
private Integer index;
public ArrayStack(int initSize){
if(initSize < 0){
throw new IllegalArgumentException("The init size is less than 0");
}
this.arr = new Integer[initSize];
this.index = 0;
}
public void push(int obj){
if(index == arr.length){
throw new ArrayIndexOutOfBoundsException("The Stack is full");
}
arr[index++] = obj;
}
public Integer pop() {
if (index == 0){
throw new ArrayIndexOutOfBoundsException("The Stack is empty");
}
return arr[--index];
}
public Integer peek(){
if (index == 0){
return null;
}
return arr[index - 1];
}
}
public static class ArrayQueue {
private Integer[] arr;
private Integer size;
private Integer start;
private Integer end;
public ArrayQueue(int initSize){
if (initSize < 0) {
throw new IllegalArgumentException("The init size is less than 0");
}
arr = new Integer[initSize];
this.size = 0;
this.start = 0;
this.end = 0;
}
public Integer poll(){
if (size == 0){
throw new ArrayOutOfBoundException("The Queue is empty");
}
size--;
int tmp = arr[start];
start = nextIndex(arr.length, start);
return tmp;
}
public void push(int obj){
if (size == arr.length){
throw new ArrayOutOfBoundException("The Queue is full");
}
size++;
//my
arr[(end % arr.length)++] = obj;
//zcy
arr[end] = obj;
end = nextIndex(arr.length, end);
}
public Integer peek(){
if (size == 0){
return null;
}
return arr[start];
}
public int nextIndex(int size, int index){
return index == size - 1 ? 0 : index + 1;
}
}
}
2
实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作
【要求】
1、pop、push、getMin 操作的时间复杂度都是 O(1);
2、设计的栈类型可以使用现成的栈结构。
【思路】
未要求空间复杂度,
使用一个相同长度的辅助栈,记录压栈或出栈后对应更新的最小值。
【思路进阶】
节省辅助栈空间,
压栈:当前数 <= 辅助栈顶,才压入辅助栈道;
出栈:当前数 == 辅助栈顶, 辅助栈才出栈;
public class Array_To_Stack_Queue{
public static class GetMinStack1{
private Stack<Integer> stackData;
private Stack<Integer> stackMin;
public GetMinStack1(){
this.stackData = new Stack<Integer>();
this.stackMin = new Stack<Integer>();
}
public void push(int newNum){
this.stackData.push(newNum);
if(this.stackMin.isEmpty()){
this.stackMin.push(newNum);
}
else if (newNum < this.getMin()){
this.stackMin.push(newNUm);
}
else{
int newMIn = this.stackMin.peek();
this.stackMin.push(newMin);
}
}
public int pop(){
if(this.stackdata.isEmpty()){
throw new RuntimeException("The stack is empty.");
}
this.stackMin.pop();
return this.stackData.pop();
}
public int getMin(){
if(this.stackMin.isEmpty()){
throw new RuntimeException("The stack is empty.");
}
return this.stackMin.peek()
}
}
public static class GetMinStack2 {
private Stack<Integer> stackData;
private Stack<Integer> stackMin;
public GetMinStack2(){
this.stackData = new Stack<Integer>();
this.stackMin = new Stack<Integer>();
}
public void push(int newNum){
this.stackData.push(newNum);
if (this.stackMIn.isEmpty()){
this.stackMin.push(newNUM);
}
if (newNum <= this.getMin()){
this.stackMin.push(newNum);
}
}
public int pop(){
if(this.stackData.isEmpty()){
throw new RuntimeException("The stack is empty.");
}
if(this.stackData.peek() == this.getMin()){
this.stackMin.pop();
}
return this.stackData.pop()
}
public int getMin(){
if(this.stackMin.isEmpty()){
return new RuntimeException("The stack is empty.");
}
return this.stackMin.peek();
}
}
}
3
3-1 如何仅用队列结构实现栈结构?(套路:用队列做深度遍历)
3-2 如何仅用栈结构实现队列结构?()
【思路】
3-1:
使用一个相同的辅助队列;
压栈:正常操作;
出栈:每次将其中一个队列的前 length-1 存入另一个队列,取出最后一个数,然后两个队列交换引用;
peek:同出栈,但最后一个数也要存入另一个队列。
3-2:
使用一个相同的辅助栈,分别接收 push 和 pop;
压栈:正常压入 push 栈;
出栈:pop 栈为空时,将 push 栈全部倒入,pop 栈直接出栈;
peek:与出栈类似;
关键点:
倒的时机: pop 栈为空才入栈,且一次性倒完
public class Array_To_Stack_Queue{
public static class TwoQueuesStack{
private Queue<Integer> queue;
private Queue<Integer> help;
pulbic TwoQueuesStack() {
queue = new LinkedList<Integer>();
help = new LinkedeList<Integer>();
}
public void push(int pushInt){
this.queue.add(pushInt);
}
public int pop(){
if(this.queue.isEmpty()){
throw new RuntimeException("The stack is empty.");
}
while(this.queue.size() > 1){
this.help.add(this.queue.poll());
}
int result = this.queue.poll();
swap();
return result;
}
public int peek(){
if(this.queue.isEmpty()){
throw new RuntimeException("The stack is empty.");
}
while(this.queue.size() > 1){
this.help.add(this.queue.poll());
}
int result = this.queue.poll();
this.help.add(result);
swap();
return result;
}
pulbic void swap(){
Queue<Integer> tmp = help;
help = queue;
queue = help;
}
}
public static class TwoStacksQueue{
private Queue<Integer> stackPop;
private Queue<Integer> stackPush;
public TwoStacksQueue(){
stackPop = new Stack<Integer>();
stackPush = new Stack<Integer>();
}
public void goTo(){
if(stackPop.isEmpty()){
while(!stackPush.isEmpty()){
stackPop.push(stackPush.pop());
}
}
}
public void push(int pushInt){
stackPush.push(pushInt);
goTo();
}
public int poll(){
goTo();
if(stackPop.isEmpty() && stackPush.isEmpty()){
throw new RuntimeException("The queue is empty.");
}
return stackPop.pop();
}
public int peek(){
goTo(); //放在哪里都可以,因为有判断条件。
if(stackPop.isEmpty() && stackPush.isEmpty()){
throw new RuntimeException("The queue is empty.");
}
return stackPop.peek();
}
}
}
4
猫狗队列
【 题目】 宠物、 狗和猫的类如下:
public class Pet { private String type;
public Pet(String type) {
this.type = type; }
public String getPetType() {
return this.type; } }
public class Dog extends Pet {
public Dog() {
super(“dog”); } }
public class Cat extends Pet {
public Cat() {
super(“cat”);}}
实现一种狗猫队列的结构,要求如下:
用户可以调用 add 方法将 cat 类或 dog 类的实例放入队列中;
用户可以调用 pollAll 方法,将队列中所有实例按照进队列的先后顺序依次弹出;
用户可以调用 pollDog 方法,将队列中的 dog 类的实例按照进队列先后顺序依次弹出;
用户可以调用 pollCat 方法,将队列中的 cat 类的实例按照进队列先后顺序依次弹出;
用户可以调用 isEmpty 方法,检查队列中是否有实例;
用户可以调用 isDogEmpty 方法,检查队列中是否有 dog 类实例;
用户可以调用 isCatEmpty 方法,检查队列中是否有 cat 类实例。
【思路】
用两个队列分别存 cat 和 dog;
封装一个数据项表示总体进队顺序。
public class CatDogQueue {
public static class Pet{
private String type;
public Pet(String type){
this.type = type;
}
pulbic String getType(){
return this.type;
}
}
public static class Dog extends Pet {
public Dog(){
super("dog");
}
}
public static class Cat extends Pet {
public Cat() {
super("cat");
}
}
//上面的类没有表示进队顺序的数据项,因此封装一个新类
public static class EnterQueuePet {
private Pet pet;
private long index;
public EnterQueuePet(Pet pet, long index) {
this.pet = pet;
this.index = index;
}
public Pet getPet(){
return this.pet;
}
public long getIndex() {
return this.index;
}
public String getEnterPetType() {
return this.pet.getType();
}
}
public static class CatDogQueue {
private Queue<EnterQueuePet> catQ;
private Queue<EnterQueuePet> dogQ;
private long index;
public CatDogQueue() {
catQ = new LinkedList<EnterQueuePet>();
dogQ = new LinkedList<EnterQueuePet>();
this.index = 0;
}
public void add(Pet pet) {
if(pet.getType().equals("dog")){
this.dogQ.add(new EnterQueuePet(pet, index++));
}
else if(pet.getType().equals("cat")){
this.catQ.add(new EnterQueuePet(pet, index++));
}
else {
throw new RuntimeException("Err, not cat or dog");
}
}
public Dog pollDog() {
if(!this.dogQ.isEmpty()){
return (Dog) this.dogQ.poll().getPet();
}
else{
throw new RuntimeException("Dog queue is empty.");
}
}
public Cat pollCat() {
if(this.catQ.isEmpty()){
throw new RuntimeException("Cat queue is empty.");
}
else{
return (Cat) this.catQ.poll().getPet();
}
}
public Pet pollAll() {
if(!this.catQ.isEmpty() && !this.dogQ.isEmpty()){
if(this.catQ.peek().getIndex() < this.dogQ.peek().getIndex()){
return this.catQ.poll().getPet();
}
else{
return this.dogQ.poll().getPet();
}
}
else if(!this.catQ.isEmpty()){
return this.catQ.poll().getPet();
}
else if(!this.dogQ.isEmpty()){
retutn this.dogQ.poll().getPet();
}
else{
throw new RuntimeException("Err, queue is empty.");
}
}
public boolean isEmpty(){
if(!this.dogQ.isEmpty() || !this.catQ.isEmpty()){
return true;
}
else{
return false;
}
}
public boolean isDogEmpty(){
return this.dogQ.isEmpty() ? true : false;
}
public boolean isCatEmpty(){
return this.catQ.isEmpty() ? true : false;
}
}
}
5
转圈打印矩阵
【题目】给定一个整型矩阵 matrix,请按照转圈的方式打印它。
例如:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
打印结果为: 1 2 3 4 8 12 16 15 14 13 9 5 6 7 11 10
【要求】 额外空间复杂度为 O(1) 。
【思路】
宏观调度(借助全局点遍历)
用 2 个坐标标记矩阵的首尾2个角 ,用坐标x或y的变化来遍历;
考虑特殊情况:矩阵只有一列或一行;
每一圈遍历后更新首尾两点。
public class PrintMatrixSpiralOrder {
public static void spiralOrderPrint(int[][] matrix){
int row1 = 0;
int col1 = 0;
int row2 = matrix[0].length - 1;
int col2 = matrix.length - 1;
while(row1 <= row2 && col1 <= col2){
printEdge(matrix, row1++, col1++, row2--, col2--);
}
}
public static void printEdge(int[][] m, int row1, int col1, int row2, int col2) {
if(row1 == row2){
for(int i=col1; i <= col2; i++){
System.out.println(m[row1][i]);
}
}
else if (col1 == col2){
for(int i=row1; i <= row2; i++){
System.out.println(m[i][col1]);
}
}
else{
int curR = row1;
int curC = col1;
while(curC != col2){
System.out.print(m[row1][curC] + " ");
curC++;
}
while(curR != row2){
System.out.print(m[curR][col2] + " ");
curR++;
}
while(curC != col1){
System.out.print(m[row2][curC]);
curC--;
}
while(curR != row1){
System.out.print(m[curR][col1]);
curR--;
}
}
}
6
旋转正方形矩阵
【题目】给定一个整型正方形矩阵matrix ,请把该矩阵调整成顺时针旋转90度的样子
【要求】额外空间复杂度为O(1)。
import java.util.Arrays;
7
反转单向和双向链表
【题目】分别实现反转单向链表和反转双向链表的函数。
【要求】如果链表长度为N,时间复杂度要求为O(N),额外空间复杂度要求为O(1)。
public class ReverseList {
public static class Node {
private int value;
private Node next;
public Node(int value){
this.value = value;
}
}
public static class BiNode {
private int value;
private BiNode prior;
private BINode next;
public BiNode(int value){
this.value = value;
}
}
public static Node reverseList(Node head){
Node next = null;
Node pre = null;
while(head != null){
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
public static BiNode reverseBiList(Node head){
BiNode = next;
BiNode pre = null;
while(head != null){
next = head.next;
head.next = pre;
head.prior = next;
pre = head;
head = next;
}
}
}
8
“之”字型打印矩阵
【题目】给定一个矩阵matrix,按照“之”字型的方式打印这个矩阵,例如:
1 2 3 4
5 6 7 8
9 10 11 12 ,
打印结果为:1 2 5 9 6 3 4 7 10 11 8 12
【要求】额外空间复杂度为O(1)。
【思路】
宏观调度:
将任务拆分为打印对角线;
用两个坐标标记对角线两端,每次对角线打印结束同时更新坐标;
注意:打印对角线有两种方向。
public class PrintMatrixSpiralOrder {
public static void printZigZag(int[][] matrix){
int row1 = 0;
int col1 = 0;
int row2 = 0;
int col2 = 0;
int endR = matrix.length - 1;
int endC = matrix[0].length - 1;
boolean fromUp = false;
while(row1 < endR + 1){
printLevel(matrix, row1, col1, row2, col2, fromUp);
col1 = row1 == endR ? col + 1 : col1;
row1 = row1 == endR ? row1 : row1 + 1;
row2 = col2 == endC ? row2 + 1 : row2;
col2 = col2 == endC ? col2 : col2 + 1;
fromUp = !fromUp;
}
System.out.println();
}
public static void printLevel(int[][] m, int row1, int col1, int row2, int col2, boolean fromUp) {
if(fromup){
while(row1 <= row2){
System.out.print(m[row2++][col2--] + " ");
}
} else {
while(row1 <= row2){
System.out.print(m[row1--][col1++] + " ");
}
}
}
}
9
在行列都排序好的矩阵中找数
【题目】给定一个有 N*M 的整型矩阵matrix和一个整数K,matrix的每一行和每一列都是排好序的。实现一个函数,判断K是否在matrix中。
例如:
0 1 2 5
2 3 4 7
4 4 4 8
5 7 7 9
如果K为7,返回true;如果K为6,返回false。
【要求】时间复杂度O(N+M),额外空间复杂度O(1)。
【思路】
从右上角或者左下角开始遍历(只有右上或左下的数在垂直于平行方向分别表示小于或大于当前数,从而可以构造条件确定搜索路径)
public class FindNumInSortedMatrix {
public static boolean isContains(int[][] matrix, int K){
int row = matrix.length -1;
int col = 0;
while(row >= 0 && col <= matrix[0].length - 1){
if (matrix[row][col] == K){
return true;
}
else if (matrix[row][col] < K){
col++;
}
else{
row--;
}
}
return false;
}
}
10
打印两个有序链表的公共部分
【题目】给定两个有序链表的头指针head1和head2,打印两个链表的公共部分。
【思路】
从头节点开始比较,小的像后移动,如果相等则同时向后移动。
public class PrintCommonPart {
public static class Node {
private int value;
private Node next;
public Node(int value){
this.value = value;
}
}
public static void printCommonPart(Node head1, Node head2){
System.out.println("Common part:");
while(head1 != null && head2 != null){
if(head1.value == head2.value){
System.out.print(head1.value + " ");
head1 = head1.next;
head2 = head2.next;
}
else if (head1.value < head2.value){
head1 = head1.next;
}
else{
head2 = head2.next;
}
System.out.println();
}
}
}
11
判断一个链表是否为回文结构
【题目】 给定一个链表的头节点head,请判断该链表是否为回文结构。例如:
1->2->1, 返回true。
1->2->2->1, 返回true。
15->6->15, 返回true。
1->2->3, 返回false。
【进阶】如果链表长度为N,时间复杂度达到O(N),额外空间复杂度达到O(1)。
【思路】
基本1:
遍历链表存储到一个辅助栈中(逆序),然后正逆序比较,找公共部分。
基本2:
准备两个指针,一个每次走一步,一个每次走两步,遍历后一个在中点,一个在尾部,将后半部分压入辅助栈,然后比较公共部分。
进阶:
1、准备两个指针,一个每次走一步,一个每次走两步,遍历后一个在中点,一个在尾部;
2、将后半部分逆序;
3、从首尾开始向中点比较(将后半部分变回正序)。
public class IsPalindromeList {
public static class Node {
private int value;
private Node next;
public Node(int value){
this.value = value;
}
}
// need N extra space
public static boolean isPalindrome1(Node head){
if(head == null || head.next == null){
return true;
}
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;
}
// need N/2 extra space
public static boolean isPalindrome2(Node head){
if(head == null || head.next == null){
return true;
}
Stack<Node> stack = new Stack<Node>();
Node fast = head;
Node slow = head;
while(slow.next != null && fast.next.next != null){
slow = slow.next;
fast = fast.next.next;
}
while(slow != null){
stack.push(slow);
slow = slow.next;
}
while(!stack.isEmpty()){
if(head.value != stack.pop().value){
return false;
}
head = head.next;
}
return true;
}
// need O(1) extra space
public static boolean isPalindrome3(Node head){
if(head == null && head.next == null){
return true;
}
Node n1 = head;
Node n2 = head;
while(n1.next != null && n2.next.next != null){
n1 = n1.next;
n2 = n2.next.next;
}
n2 = n1.next; // right part first node
n1.next = null;
Node tmp = null;
while(n2 != null){
tmp = n2.next;
n2.next = n1;
n1 = n2;
n2 = tmp;
}
tmp = n1; // save last node
n2 = head; // left first node
boolean res = ture;
while (n1 != null && n2 != null){
if (n1.value != n2.value){
res = false;
break;
}
n1 = n1.next;
n2 = n2.next;
}
n1 = tmp.next;
tmp.next = null;
while(n1 != null) {
n2 = n1.next;
n1.next = tmp;
tmp = n1;
n1 = n2;
}
return res;
}
}
12
将单向链表按某值划分成左边小、中间相等、右边大的形式
【题目】 给定一个单向链表的头节点head,节点的值类型是整型,再给定一个整数pivot。实现一个调整链表的数,将链表调整为左部分都是值小于 pivot 的节点, 中间部分都是值等于pivot的节点, 右部分都是值大于 pivot的节点。
除这个要求外, 对调整后的节点顺序没有更多的要求。例如: 链表9->0->4->5-1,pivot=3。调整后链表可以是1->0->4->9->5,也可以是0->1->9->5->4。总之,满足左部分都是小于3的节点,中间部分都是等于3的节点(本例中这个部分为空), 右部分都是大于3的节点即可。 对某部分内部的节点顺序不做要求。
【进阶】在原问题的要求之上再增加如下两个要求。
在左、中、右三个部分的内部也做顺序要求,要求每部分的节点从左到右的顺序与原链表中节点的先后次序一致。例如: 链表9->0->4->5->1,pivot=3。调整后的链表是0->1->9->4->5。在满足原问题要求的同时,左部分节点从左到右为0、1。在原链表中也是先出现0,后出现1;中间部分在本例中为空,不再讨论;右部分节点 从左到右为9、4、 5。在原链表中也是先出现9, 然后出现4,最后出现5。如果链表长度为N, 时间复杂度请达到O(N), 额外空间复杂度请达到O(1)。
【思路】
基础:
遍历链表,用辅助数组存储,在数组中排好序放入链表。
进阶:
分别用 3 个引用 small 、equal 、big 来重新组织链表;
先遍历一次找到 small 、equal、 big 的第一个指向;
再遍历一次将满足条件的结点分别链接在small、equal、big后面;
最后将small、equal、big 链接起来。
注意:要考虑某部分为空的情况
public class SmalledEqualBigger {
public static class Node {
private int value;
private Node next;
public Node(int value){
this.value = value;
}
}
public static Node listPartition(Node head, int pivot){
Node sH = null; //small head
Node sT = null; //small tail
Node eH = null;
Node eT = null;
Node bH = null;
Node bT = null;
Node next = null; //save next node
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;
}
if(sT != null){
sT.next = eH;
eT = eT == null? sT : eT;
}
if(eT != null){
eT.next = bH;
}
return sH != null ? sH : eH != null ? eH : bH;
}
}
13
复制含有随机指针节点的链表
【 题目】 一种特殊的链表节点类描述如下:
public class Node { public int value;
public Node next;
public Node rand;
public Node(int data) {
this.value = data;
}
}
Node类中的value是节点值,next指针和正常单链表中next指针的意义一样,都指向下一个节点, rand指针是Node类中新增的指针,这个指针可能指向链表中的任意一个节点, 也可能指向null。给定一个由Node节点类型组成的无环单链表的头节点head, 请实现一个函数完成这个链表中所有结构的复制,并返回复制的新链表的头节点。
【进阶】不使用额外的数据结构,只用有限几个变量,且在时间复杂度为 O(N)内完成原问题要实现的函数。
【思路】
基本:
遍历链表Node 并拷贝为 Node’,用哈希表以(key, value) = (Node, Node’)存储,根据映射关系实现Node’ 节点之间的 rand 指向。
进阶:
遍历,将新的 Node’ 链接到对应 Node 的 next 上,变成一个大链表;
(用next这种空间结构替代哈希表)
遍历,根据 Node 的 rand 链接 Node’ 的 rand;
将两个链表分离。
public class CopyListWithRandom {
public static class Node {
private int value;
private Node next;
private Node rand;
public Node(int value){
this.value = value;
}
}
public static Node copyListWithRand1(Node head) {
if(head == null){
return null;
}
HashMap<Node, Node> map= new HashMap<Node, Node>();
Node cur = head;
whilr(cur != null){
map.put(cur, new Node(cur.value));
cur = cur.next;
}
cur = head;
whilr(cur != null){
map.get(cur).next = map.get(cur.next);
map.get(cur).rand = map.get(cur.rand);
cur = cur.next;
}
return map.get(head);
}
public static Node copyListWithRand2(Node head){
if(head == null){
return null;
}
Node cur = head;
Node next = null;
while (cur != null){
next = cur.next;
cur.next = new Node(cur.value);
cur.next.next = next;
cur = next;
}
cur = head;
// set copy node rand
while(cur != null){
next = cur.next.next;
cur.next.rand = cur.rand != null ? cur.rand.next : null;
cur = next;
}
cur = head;
Node res = head.next;
// split
while(cur != null){
next = cur.next.next;
cur.next.next = next != null ? next.next : null;
cur.next = next;
cur = next;
}
return res;
}
}
14
两个单链表相交的一系列问题 (链表最难级别)
【 题目】 在本题中,单链表可能有环,也可能无环。给定两个单链表的头节点 head1 和 head2,这两个链表可能相交,也可能不相交。请实现一个函数,如果两个链表相交,请返回相交的第一个节点;如果不相交,返回null 即可。【要求】如果链表1的长度为N,链表2的长度为M,时间复杂度请达到 O(N+M),额外空间复杂度请达到O(1)。
【思路】
判断有环:
将遍历过的结点加到HashSet 中,如果新的结点在 Set 中,证明有环;
此题额外空间复杂度 O(1),不可行;
进阶:
(快慢指针法)
设置快慢两个指针,一个每次走 2 步,另一个只走 1 步;
快指针走到 null,无环;两个指针相遇,有环;
如果相遇,快指针回到头节点,改为每次走一步,两个指针会在第一个入 环结点相遇,输出此节点为 loop;(数学结论,证明略,可以设环外长L,环内长R)
判断相交:
情况1:如果都无环
(用HashSet)
遍历其中一个链表,放入 HashSet 中;
遍历另一个链表,找出第一出现在 HashSet 中的节点;
(不用HashSet)
遍历找到 head1 、head2 的长度 len1、len2 和最后一个节点 end1、 end2;
如果 end1 和 end2 不是同一个,则不相交;
如果是同一个,则重头再次遍历,令较长链表先走|len1 - len2|步,则一 定会在第一个相交的节点相遇;
情况2:如果一个有环一个无环:
必然不会相交;
情况3:如果两个都有环;
如果 loop1 = loop2 , 将其看作两个无环链表相交;
如果 loop1 != loop2, 继续移动 loop1,如果在回到 loop1 前遇到 loop2,则相交,相交点为 loop1 或 loop2,如果没有遇到,则不相交;
public class FindFirstIntersectNode {
public static class Node {
private int value;
private Node next;
public Node(int value){
this.value = value;
}
}
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);
}else if (loop1 != null && loop2 != null){
return bothLoop(head1, loop1, head2, loop2);
}else{
return null;
}
}
public static Node getLoopNode(Node head){
if(head != null && head.next != null && head.next.next != null){
Node fast = head.next.next;
Node slow = head.next;
}
while(fast != slow){
if(fast.next == null || fast.next.next == null){
return null;
}
fast = fast.next.next;
slow = slow.next;
}
fast = head;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
public static Node noLoop(Node head1, Node head2){
if(head1 == null || head2 == null){
return null;
}
int len1 = 0;
int len2 = 0;
Node cur1 = head1;
Node cur2 = head2;
while (cur1 != null){
len1++;
cur1 = cur1.next;
}
while(cur2 != null){
len2++;
cur2 = cur2.next;
}
if(cur1 != cur2){
return null;
}
cur1 = len1 >= len2 ? head1 : head2;
cur2 = cur1 == head1? head2 : head2;
int n = len1 >= len2 ? len1 - len2 : len2 - len1;
while(n != 0){
cur1 = cur1.next;
n--;
}
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){
int len1 = 0;
int len2 = 0;
while(cur1 != loop1){
len1++;
cur1 = cur1.next;
}
while(cur2 != loop2){
len2++;
cur2 = cur2.loop;
}
cur1 = len1 >= len2 ? head1 : head2;
cur2 = cur1 == head1 ? head2 : head1;
int n = len1 >= len2 ? len1 - len2 : len2 - len1;
while(n != 0){
n--;
cur1 = cur.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;
}
}
}