package com.zuoshen.jichurumen.class04;
import java.util.*;
/**
* @author ShiZhe
* @create 2022-02-25 21:35
*/
public class code01 {
/**
* Node结构体
* 单链表的节点结构
*/
public static class Node {
public int value;
public Node next;
public Node rand;
// 构造函数
public Node(int data) {
this.value = data;
}
}
/**
* 双链表的节点结构
*/
public static class DoubleNode {
public int value;
public DoubleNode last;
public DoubleNode next;
// 构造函数
public DoubleNode(int data) {
this.value = data;
}
}
/**
* 单链表反转
* @param head
* @return
*/
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;
}
/**
* 打印单链表
* @param head
*/
public static void printLinkedList(Node head) {
System.out.println("Linked List: ");
while (head != null) {
System.out.print(head.value + " ");
head = head.next;
}
System.out.println();
}
/**
* 双链表反转
* @param head
* @return
*/
public static DoubleNode reverseList(DoubleNode head) {
DoubleNode pre = null;
DoubleNode next =null;
while (head != null) {
next = head.next;
head.last = next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
/**
* 打印双链表
* @param head
*/
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();
}
/**
* 打印两个有序链表的公共部分
* @param head1
* @param head2
*/
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();
}
/**
* 判断一个链表是否为回文结构
* need n extra space:新建一个栈,链表全部放入栈中,然后一一弹出与head相比,head++。
* need n/2 extra space:新建一个栈,利用2个指针,一个next,一个next.next,
* 使某一指针指向空时,一指针指向回文的右边,将右边放入栈中,一一弹出与head相比较,head++。
* need O(1) extra space:反转右边的单链表。利用2个指针一一对比。
* @param head
* @return
*/
public static boolean isPalindrome(Node head) {
if (head == null || head.next == null) {
return false;
}
// 定义指向右半部分的辅助节点
Node right = head;
// 定义2倍速跳转的辅助节点
Node next = head;
// next指针更快,只用判断next指针不越界
while (next.next != null && next.next.next != null) {
// 一倍速,right到中间
right = right.next;
// 二倍速,next到末尾
next = next.next.next;
}
// 右半部分的开头
next = right.next;
// 反转右半部分链表的辅助节点
Node pre = null;
// 从中间断开
right.next = null;
// 反转右边单链表
while (next != null) {
pre = next;
next = next.next;
pre.next = right;
right = pre;
}
// 将左边的头赋值给next
next = head;
// 返回结果
boolean res = true;
// 检查回文
while (right != next) {
if (next.value != right.value) {
res = false;
break;
}
next = next.next;
right = right.next;
}
// 恢复反转的右部分单链表
right = pre;
Node tmp = null;
while (right != next) {
right = right.next;
pre.next = tmp;
tmp = pre;
pre = right;
}
pre.next = tmp;
return res;
}
/**
* 将单向链表按某值划分成左边小、中间相等、右边大的形式
* 时间复杂度请达到O(N),额外空间复杂度请达到O(N)。
* 方法一:类似于荷兰国旗问题,新建一个Node类型的数组,进行排序,然后再建立指针。
* 时间复杂度请达到O(N),额外空间复杂度请达到O(1)。
* 方法二:申请6个辅助节点,标识各个3个区间(小于、等于、大于)的头和尾。
* @param head
* @param pivot
* @return
*/
public static Node listPartition(Node head, int pivot) {
// 小于头
Node sH = null;
// 小于尾
Node sT = null;
// 等于头
Node eH = null;
// 等于尾
Node eT = null;
// 大于头
Node bH = null;
// 大于尾
Node bT = null;
// 辅助节点
Node next = null;
// 将所有节点划分给3条链
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为空,将st赋值给eT
eT = eT == null ? sT : eT;
}
if (eT != null) {
eT.next = bH;
}
return sH != null ? sH : eH != null ? eH : bH;
}
/**
* 复制含有随机指针节点的链表
* 时间复杂度O(N),额外空间复杂度O(1)
* 方法一:hashMap,遍历新建节点,再遍历新建连接。额外空间是O(N)
* 方法二:将新节点放在老节点的next中,节省了hashMap,使额外空间为O(1),遍历放入新节点,再遍历构建新节点的rand,再遍历分离
* @param head
* @return
*/
public static Node copyListWithRand(Node head) {
if (head == null) {
return null;
}
Node cur = head;
Node next =null;
// 将新建节点插入在老节点的next中
while (cur != null) {
next = cur.next;
cur.next = new Node(cur.value);
cur.next.next = next;
cur =next;
}
// 构建新节点的rand
cur = head;
Node curCopy = null;
while (cur != null) {
next = cur.next.next;
curCopy = cur.next;
curCopy.rand = cur.rand != null ? cur.rand.next : null;
cur =next;
}
// 分离
cur = head;
Node res = head.next;
while (cur != null) {
next = cur.next.next;
curCopy = cur.next;
cur.next = next;
curCopy.next = next != null ? next.next : null;
cur = next;
}
return res;
}
/**
* 比较器
*/
public static class NodeComparator implements Comparator<Node> {
// 根据Node的value大小来排序,官方默认下面的返回为升序。
// < return -1
// = return 0
// > return 1
// 需要为降序则return的值与官方相反
@Override
public int compare(Node o1, Node o2) {
return o1.value - o2.value;
}
}
/**
* 带有rand节点的链表打印
* @param head
*/
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) {
System.out.print(cur.rand == null ? "- " : cur.rand.value + " ");
cur = cur.next;
}
System.out.println();
}
/**
* 两个单链表相交的一系列问题
* @param head1
* @param head2
* @return
*/
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;
}
/**
* 获取环节点
* @param head
* @return
*/
public static Node getLoopNode(Node head) {
if (head == null || head.next == null || head.next.next == null) {
return null;
}
// n1 -> slow
Node n1 = head.next;
// n2 -> fast
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 -> walk again from head,这步很重要。
n2 = head;
while (n1 != n2) {
n1 = n1.next;
n2 = n2.next;
}
return n1;
}
/**
* 第一种情况:均无环
*
* @param head1
* @param head2
* @return
*/
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
cur1 = n > 0 ? head1 : head2;
// 将节点数少的赋值给cur1
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;
}
/**
* 第二种情况:均有环
* @param head1
* @param loop1
* @param head2
* @param loop2
* @return
*/
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) {
Node nodeA = null;
Node nodeB = null;
Node nodeC = null;
// hashSet1的key是基础类型->int类型
HashSet<Integer> hashSet1 = new HashSet<>();
hashSet1.add(3);
System.out.println(hashSet1.contains(3));
hashSet1.remove(3);
System.out.println(hashSet1.contains(3));
System.out.println("========1=========");
// hashSet2的key是非基础类型->Node类型
nodeA = new Node(1);
nodeB = new Node(1);
HashSet<Node> hashSet2 = new HashSet<>();
hashSet2.add(nodeA);
System.out.println(hashSet2.contains(nodeA));
System.out.println(hashSet2.contains(nodeB));
hashSet2.remove(nodeA);
System.out.println(hashSet2.contains(nodeA));
System.out.println("========2=========");
// hashMap1的key是基础类型->String类型
HashMap<String, Integer> hashMap1 = new HashMap<>();
String str1 = "key";
String str2 = "key";
hashMap1.put(str1, 1);
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.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);
System.out.println(hashMap1.containsKey(str1));
System.out.println(hashMap1.containsKey(str2));
System.out.println("========3=========");
// hashMap2的key是非基础类型->Node类型
// 如果不是基础类型,内部按引用传递
nodeA = new Node(1);
nodeB = new Node(1);
HashMap<Node, String> hashMap2 = new HashMap<>();
hashMap2.put(nodeA, "A节点");
System.out.println(hashMap2.containsKey(nodeA));
System.out.println(hashMap2.containsKey(nodeB));
System.out.println(hashMap2.get(nodeA));
System.out.println(hashMap2.get(nodeB));
hashMap2.put(nodeB, "B节点");
System.out.println(hashMap2.containsKey(nodeA));
System.out.println(hashMap2.containsKey(nodeB));
System.out.println(hashMap2.get(nodeA));
System.out.println(hashMap2.get(nodeB));
System.out.println("========4=========");
// treeSet的key是非基础类型->Node类型
// 如果不是基础类型,必须提供比较器
nodeA = new Node(5);
nodeB = new Node(3);
nodeC = new Node(7);
TreeSet<Node> treeSet = new TreeSet<>();
// 以下的代码会报错,因为没有提供Node类型的比较器
try {
treeSet.add(nodeA);
treeSet.add(nodeB);
treeSet.add(nodeC);
} catch (Exception e) {
System.out.println("错误信息:" + e.getMessage());
}
treeSet = new TreeSet<>(new NodeComparator());
// 以下的代码没问题,因为提供了Node类型的比较器
try {
treeSet.add(nodeA);
treeSet.add(nodeB);
treeSet.add(nodeC);
System.out.println("这次节点都加入了");
} catch (Exception e) {
System.out.println(e.getMessage());
}
System.out.println("========5=========");
// 展示有序表常用操作
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) + ", 删了就没有了哦");
System.out.println("========6=========");
// 单链表反转与输出
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));
// 打印两个有序链表的公共部分
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);
// 判断回文
Node head3 = null;
head3 = new Node(1);
head3.next = new Node(2);
head3.next.next = new Node(3);
head3.next.next.next = new Node(2);
head3.next.next.next.next = new Node(1);
printLinkedList(head3);
System.out.println(isPalindrome(head3));
printLinkedList(head3);
System.out.println("=========================");
// 将单向链表按某值划分成左边小、中间相等、右边大的形式
Node head4 = new Node(7);
head4.next = new Node(9);
head4.next.next = new Node(1);
head4.next.next.next = new Node(8);
head4.next.next.next.next = new Node(5);
head4.next.next.next.next.next = new Node(2);
head4.next.next.next.next.next.next = new Node(5);
printLinkedList(head4);
head4 = listPartition(head4, 5);
printLinkedList(head4);
// 复制含有随机指针节点的链表
Node head5 = new Node(1);
head5.next = new Node(2);
head5.next.next = new Node(3);
head5.next.next.next = new Node(4);
head5.next.next.next.next = new Node(5);
head5.next.next.next.next.next = new Node(6);
head5.rand = head5.next.next.next.next.next; // 1 -> 6
head5.next.rand = head5.next.next.next.next.next; // 2 -> 6
head5.next.next.rand = head5.next.next.next.next; // 3 -> 5
head5.next.next.next.rand = head5.next.next; // 4 -> 3
head5.next.next.next.next.rand = null; // 5 -> null
head5.next.next.next.next.next.rand = head5.next.next.next; // 6 -> 4
printRandLinkedList(head5);
Node res = copyListWithRand(head5);
printRandLinkedList(res);
// 两个单链表相交的一系列问题
// 1->2->3->4->5->6->7->null
Node head6 = new Node(1);
head6.next = new Node(2);
head6.next.next = new Node(3);
head6.next.next.next = new Node(4);
head6.next.next.next.next = new Node(5);
head6.next.next.next.next.next = new Node(6);
head6.next.next.next.next.next.next = new Node(7);
// 0->9->8->6->7->null
Node head7 = new Node(0);
head7.next = new Node(9);
head7.next.next = new Node(8);
head7.next.next.next = head6.next.next.next.next.next; // 8->6
System.out.println(getIntersectNode(head6, head7).value);
// 1->2->3->4->5->6->7->4...
head6 = new Node(1);
head6.next = new Node(2);
head6.next.next = new Node(3);
head6.next.next.next = new Node(4);
head6.next.next.next.next = new Node(5);
head6.next.next.next.next.next = new Node(6);
head6.next.next.next.next.next.next = new Node(7);
head6.next.next.next.next.next.next = head6.next.next.next; // 7->4
// 0->9->8->2...
head7 = new Node(0);
head7.next = new Node(9);
head7.next.next = new Node(8);
head7.next.next.next = head6.next; // 8->2
System.out.println(getIntersectNode(head6, head7).value);
// 0->9->8->6->4->5->6..
head7 = new Node(0);
head7.next = new Node(9);
head7.next.next = new Node(8);
head7.next.next.next = head6.next.next.next.next.next; // 8->6
System.out.println(getIntersectNode(head6, head7).value);
}
}