单向链表:
class Node {
private int data;
private Node next;
public Node(int data) {
this.data = data;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public Node getNext() {
return next;
}
public Node setNext(Node next) {
this.next = next;
return this.next;
}
}
双向链表:
static class Node {
private Node pre;
private int data;
private Node next;
public Node(int data) {
this.data = data;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public Node getNext() {
return next;
}
public Node setNext(Node next) {
this.next = next;
this.next.pre = this;
return this.next;
}
public Node getPre() {
return pre;
}
public Node setPre(Node pre) {
this.pre = pre;
this.pre.next = this;
return this.pre;
}
}
一、单链表反转。时间复杂度O(n) 空间复杂度O(1)
两个引用分别指向当前节点的上一个节点(pre)和下一个节点(next),设置当前节点的下一个节点为pre,pre指向当前节点,当前节点指向next,遍历一遍。
public Node method(Node node) {
Node pre = null;
Node next;
while (node != null) {
next = node.getNext();
node.setNext(pre);
pre = node;
node = next;
}
return pre;
}
二、双向链表反转,时间复杂度O(n)空间复杂度O(1)
先记下下一个节点的引用,然后pre引用与next引用交换。
public Node method2(Node node) {
Node cur = node;
while (cur != null) {
Node temp = cur.next;
cur.next = cur.pre;
cur.pre = temp;
if (temp == null) {
break;
}
cur = temp;
}
return cur;
}
三、打印两个单向有序链表的公共部分
小的看下一个,相等则打印
public static void method(Node node1, Node node2) {
while (node1 != null && node2 != null) {
if (node1.getData() == node2.getData()) {
node1 = node1.getNext();
node2 = node2.getNext();
System.out.println(node1.getData());
} else if (node1.getData() < node2.getData()) {
node1 = node1.getNext();
} else {
node2 = node2.getNext();
}
}
}
四、判断链表是否回文。要求时间复杂度O(n),空间复杂度O(1)。
两个指针一个走一步一个走两步,将后半段逆序,再遍历比较
public static boolean method(Node node) {
//两个Node引用每次分别走一步和两步,走的快的抵达终点时,慢的恰好到中间(四个节点的话,到第二个节点)
Node node1 = node;
Node node2 = node;
while (node2.next != null && node2.next.next != null) {
node1 = node1.getNext();
node2 = node2.getNext().getNext();
}
//将后半段逆序
node2 = null;
Node node3;
while (node1 != null) {
node3 = node1.next;
node1.next = node2;
node2 = node1;
node1 = node3;
}
//遍历前半段和后半段
node3 = node;
while (node3 != null) {
if (node2.getData() != node3.getData()) {
return false;
}
node2 = node2.getNext();
node3 = node3.getNext();
}
return true;
}
五、将单向链表按某一个值划分成左边小于,中间相等,右边大于这个数的形式,且不改变各部分数相对位置。要求时间复杂度O(n),空间复杂度O(1).
将节点分成三部分,然后合在一起。
public static Node method(Node node, int num) {
Node before = new Node();
Node equal = new Node();
Node after = new Node();
Node firstBe = before;
Node firstEq = equal;
Node firstAf = after;
// 将小于、等于、大于的值分成三串
while (node != null) {
if (node.getData() < num) {
before.next = node;
before = before.next;
} else if (node.getData() == num) {
equal.next = node;
equal = equal.next;
} else {
after.next = node;
after = after.next;
}
node = node.next;
}
// 将三部分合在一起
before.next = firstEq.next;
equal.next = firstAf.next;
after.next = null;
return firstBe.next;
}
六、一个链表有next和radom指针,如下。深度拷贝(要有新的对象)此链表。
public class Node {
public int value;
public Node next;
public Node rand;
public Node(int data) { this.value = data; }
}
1、用哈希表,每一个原节点对应一个新节点,两次遍历依次建立next和random连接
public static Node method1(Node head) {
Map<Node, Node> map = new HashMap();
Node node = head;
while (node != null) {
map.put(node, new Node(node.getData()));
node = node.next;
}
node = head;
while (node != null) {
map.get(node).next = map.get(node.next);
node = node.next;
}
node = head;
while (node != null) {
map.get(node).random = map.get(node.random);
node = node.next;
}
return map.get(head);
}
2、不利用其他数据结构的话怎么办?
再每一个节点后插入一个新节点,处理完后再提取出来
public static Node method2(Node head) {
//每一个原节点后插入一个新节点
Node node = head;
while (node != null) {
Node temp = new Node(node.getData());
temp.next = node.next;
node.next = temp;
node = temp.next;
}
//建立对应的random连接
node = head;
while (node != null) {
if (node.random != null) {
node.next.random = node.random.next;
} else {
node.next.random = null;
}
node = node.next.next;
}
//将拷贝好的新节点按序提取出来,同时建立next连接
node = head.next.next;
Node copy = head.next;
Node copyBegin = copy;
while (node != null) {
copy.next = node.next;
copy = copy.next;
node = node.next.next;
}
return copyBegin;
}
七、判断一个链表是否有环,并返回入环节点
1、用哈希表
public static Node method1(Node head) {
HashSet<Node> set = new HashSet<>();
Node node = head;
while (node != null) {
if (set.contains(node)) {
return node;
}
set.add(node);
node = node.next;
}
return null;
}
2、双指针,一个走一步,一个走两步。相遇则相交。两个指针再分别指向起点和相遇点,每次都走一步,相遇时即为入环点。
public static Node method2(Node head) {
if (head.next == null) {
return null;
}
Node node1 = head.next;
Node node2 = head.next.next;
while (node1 != null && node2 != null && node2.next != null) {
if (node1 == node2) {
node2 = head;
while (node1 != node2) {
node2 = node2.next;
node1 = node1.next;
}
return node1;
}
node1 = node1.next;
node2 = node2.next.next;
}
return null;
}
八、判断两个无环链表是否相交,返回交点。
只需要判断最后一个节点是否地址相同。
public static Node noRingPoint(Node head1, Node head2) {
int len1 = 0;
int len2 = 0;
Node node1 = head1;
Node node2 = head2;
while (node1.next != null) {
node1 = node1.next;
len1++;
}
while (node2.next != null) {
node2 = node2.next;
len2++;
}
if (node1 == node2) {
// 两个链表相交,现在找出交点,node1指向长链表,node2指向短链表,num是差值。
int num = len2 > len1 ? len2 - len1 : len1 - len2;
node1 = len2 > len1 ? head2 : head1;
node2 = node1 == head1 ? head2 : head1;
// 长链表先走几步
while (num-- > 0) {
node1 = node1.next;
}
while (node1 != node2) {
node1 = node1.next;
node2 = node2.next;
}
return node1;
}
return null;
}
九、判断两个链表是否相交(可能有环,可能无环),返回交点。
1、两个都无环:如上
2、一个无环,一个有环:肯定不相交。
3、两个都有环:
3(1):
3(2):
3(3):
将两个链表各自遍历一遍,找到各自的入环点。
1、入环点相同,就是第3(2),两个有环链表相交。
2、入环点不同的话,让一个入环点一直走,另一个不动,若相遇,则两个有环链表相交。
以下是全部代码:
public static Node crossPoint(Node head1, Node head2) {
// 判断是否有环
Node node1 = isRing(head1);
Node node2 = isRing(head2);
// 两个都无环
if (node1 == null && node2 == null) {
return noRingPoint(head1, head2);
}
// 两个都有环
if (node1 != null && node2 != null) {
Node he1 = head1;
Node he2 = head2;
Node loop1 = node1;
Node loop2 = node2;
// 入环点相同
if (loop1 == loop2) {
return loop1;
}
// 入环点不同的情况
loop1 = node1.next;
while (loop1 != node1) {
if (loop1 == loop2) {
return node1;
}
loop1 = loop1.next;
}
return null;
}
// 一个有环,一个无环
return null;
}
public static Node noRingPoint(Node head1, Node head2) {
int len1 = 0;
int len2 = 0;
Node node1 = head1;
Node node2 = head2;
while (node1.next != null) {
node1 = node1.next;
len1++;
}
while (node2.next != null) {
node2 = node2.next;
len2++;
}
if (node1 == node2) {
// 两个链表相交,现在找出交点,node1指向长链表,node2指向短链表,num是差值。
int num = len2 > len1 ? len2 - len1 : len1 - len2;
node1 = len2 > len1 ? head2 : head1;
node2 = node1 == head1 ? head2 : head1;
// 长链表先走几步
while (num-- > 0) {
node1 = node1.next;
}
while (node1 != node2) {
node1 = node1.next;
node2 = node2.next;
}
return node1;
}
return null;
}
public static Node isRing(Node head) {
HashSet<Node> set = new HashSet<>();
Node node = head;
while (node != null) {
if (set.contains(node)) {
return node;
}
set.add(node);
node = node.next;
}
return null;
}