链表
- 单向链表:链表的链接方向是单向的
- 双向链表:链表的链接方向是双向的
- 链表的访问要通过顺序读取从头部开始
- java中的LinkedList就是双向链表,可以用于队列的实现:Queue queue = new LinkedList<>(); 单向链表的实现:
public static class Node {
public int value;
public Node next;
public Node (int val){
this.value = val;
}
}
双向链表的实现:
public static class DoubleNode {
public int value;
public DoubleNode last;
public DoubleNode next;
public DoubleNode(int val) {
this.value = val;
}
}
实现反转单向链表:
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);
}
public static class Node {
public int value;
public Node next;
public Node(int data) {
this.value = data;
}
}
//核心代码
//记住就行了,简记的方法
//定义两个Node类型,先设为null。
//先出现next,后出现pre
//多一个last(表示链接的上一个)
//方法需要返回值pre
//五轮,上面没讲逻辑只为记住,形成自己的记忆方法就行
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 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 main(String[] args) {
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));
}
public static class DoubleNode {
public int value;
public DoubleNode last;
public DoubleNode next;
public DoubleNode(int data) {
this.value = data;
}
}
//核心代码
//记住就行了,简记的方法
//定义两个Node类型,先设为null。
//先出现next,后出现pre
//多一个last(表示链接的上一个)
//方法需要返回值pre
//五轮,上面没讲逻辑只为记住,形成自己的记忆方法就行
//需要当前节点,下一个节点,上一个节点,一个中介
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 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();
}
面试时链表解题的方法论
- 笔试,不用太在乎空间复杂度,一切为了时间复杂度
- 面试,时间复杂度依然是第一位,但是一定要找到空间最省的方法
重要技巧
- 额外数据结构记录(哈希表、队列、栈、数组等容器)
- 快慢指针
下面举一个例子:判断一个单向链表是不是回文链表
时间复杂度O(N),空间复杂度O(N)的解法:用栈,所有的压入栈,在从头开始和栈的弹出相比较
public static void main(String[] args) {
Node head1 = new Node(1);
head1.next = new Node(2);
head1.next.next = new Node(3);
head1.next.next.next = new Node(3);
head1.next.next.next.next= new Node(2);
head1.next.next.next.next.next = new Node(1);
System.out.println(isPalindrome(head1));
printLinkedList(head1);
}
public static class Node {
public int value;
public Node next;
public Node(int data) {
this.value = data;
}
}
public static boolean isPalindrome(Node head) {
Stack<Node> stack = new Stack<>();
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;
}
时间复杂度O(N),空间复杂度O(N/2)的解法:用栈,一半压入栈,在从头开始和栈的弹出相比较
public static void main(String[] args) {
Node head1 = new Node(1);
head1.next = new Node(2);
head1.next.next = new Node(3);
head1.next.next.next = new Node(3);
head1.next.next.next.next= new Node(2);
head1.next.next.next.next.next = new Node(1);
System.out.println(isPalindrome(head1));
printLinkedList(head1);
}
public static class Node {
public int value;
public Node next;
public Node(int data) {
this.value = data;
}
}
public static boolean isPalindrome(Node head) {
if (head == null || head.next == null) {
return 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;
}
时间复杂度O(N),空间复杂度O(1)的解法:用快慢指针,将中间点之后的反转,一个从头开始,一个从后开始,依次比较,最后在反转回来
public static void main(String[] args) {
Node head1 = new Node(1);
head1.next = new Node(2);
head1.next.next = new Node(3);
head1.next.next.next = new Node(3);
head1.next.next.next.next= new Node(2);
head1.next.next.next.next.next = new Node(1);
System.out.println(isPalindrome(head1));
printLinkedList(head1);
}
public static class Node {
public int value;
public Node next;
public Node(int data) {
this.value = data;
}
}
public static boolean isPalindrome(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;//如果是奇数,它得到的就是中心的mid点,如果是偶数,它得到的就是中心上一个
n2 = n2.next.next;
}
//n2这个位置尴尬没什么用,而n1代表的是mid属于有用点
n2 = n1.next;//对n1(中心点)之后的进行翻转,此时n2就是开始点
n1.next = null;//作为之后前部分是从前往后的链表而后部分是从后往前的链表,交汇的停止点
//下面进行对mid点之后的部分进行翻转链表
//需要知道开始点,上一个点,下一个点,一个中介
//n3就是中介
Node n3= null;
//while截止条件是当前点是null
while (n2 != null){
n3 = n2.next;
n2.next = n1;
n1 = n2;
n2 = n3;
}
//n2的位置也很尴尬,没什么用,所以好复用
n3 = n1;//此时的n1记录的是最后的结点
n2 = head;//刚开始的头结点,下面就是从前向后和从后向前比,判断是不是回文数
while (n1 != null && n2 != null){
if(n1.value != n2.value){
return false;
}
n1 = n1.next;
n2 = n2.next;
}
//n1 和 n2 位置之后都没什么特殊意义了,都可以直接复用
//判断完成,将之前反转的链表反转回去
//当前点是n3之前保存的最后一个结点
//上一个结点可以自己设置,可以以n3的下一个节点作为当前点,那么n3就是上一个节点
Node n4 = null;//自己设置的上一个节点
while (n3 != null){
n2 = n3.next;
n3.next = n4;
n4 = n3;
n3 = n2;
}
//上一个结点可以自己设置,可以以n3的下一个节点作为当前点,那么n3就是上一个节点的写法
// n1 = n3.next;
// n3.next = null;
// while (n1 != null){
// n2 = n1.next;
// n1.next = n3;
// n3 = n1;
// n1 = n2;
// }
return true;
}
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();
}