文章目录
- 找出链表中类中点的位置
- 判断该链表是否为回文结构
- 将单向链表按某值划分成左边小、中间相等、右边大的形式
- 给定一个有next指针和rand指针的Node 组成的无环单链表头节点,实现一个函数完成链表的复制,返回新链表的头节点
- 给定两个可能有环也可能无环的单链表,头节点head1和head2 实现一个函数,如果两个链表相交,返回相交的第一个结点,不相交返回null
- 能不能不给单链表的头节点,只给想删除的结点,就能做到在链表上把这个点删掉?
找出链表中类中点的位置
快慢指针
1)输入链表头节点,奇数长度返回中点,偶数长度返回上中点
2)输入链表头节点,奇数长度返回中点,偶数长度返回下中点
3)输入链表头节点,奇数长度返回中点前一个,偶数长度返回上中点前一个
4)输入链表头节点,奇数长度返回中点前一个,偶数长度返回下中点前一个
public class Code01_LinkedListMid { public static class Node {public int value;public Node next;public Node(int v) {value = v;}}// 1)public static Node midOrUpMidNode(Node head) {if (head == null || head.next == null || head.next.next == null) {return head;}// 链表有3个点或以上Node slow = head.next;Node fast = head.next.next;while (fast.next != null && fast.next.next != null) {slow = slow.next;fast = fast.next.next;}return slow;}// 2)public static Node midOrDownMidNode(Node head) {if (head == null || head.next == null) {return head;}Node slow = head.next;Node fast = head.next;while (fast.next != null && fast.next.next != null) {slow = slow.next;fast = fast.next.next;}return slow;}// 3)public static Node midOrUpMidPreNode(Node head) {if (head == null || head.next == null || head.next.next == null) {return null;}Node slow = head;Node fast = head.next.next;while (fast.next != null && fast.next.next != null) {slow = slow.next;fast = fast.next.next;}return slow;}// 4)public static Node midOrDownMidPreNode(Node head) {if (head == null || head.next == null) {return null;}if (head.next.next == null) {return head;}Node slow = head;Node fast = head.next;while (fast.next != null && fast.next.next != null) {slow = slow.next;fast = fast.next.next;}return slow;}// 1)public static Node right1(Node head) {if (head == null) {return null;}Node cur = head;ArrayList<Node> arr = new ArrayList<>();while (cur != null) {arr.add(cur);cur = cur.next;}return arr.get((arr.size() - 1) / 2);}// 2)public static Node right2(Node head) {if (head == null) {return null;}Node cur = head;ArrayList<Node> arr = new ArrayList<>();while (cur != null) {arr.add(cur);cur = cur.next;}return arr.get(arr.size() / 2);}// 3)public static Node right3(Node head) {if (head == null || head.next == null || head.next.next == null) {return null;}Node cur = head;ArrayList<Node> arr = new ArrayList<>();while (cur != null) {arr.add(cur);cur = cur.next;}return arr.get((arr.size() - 3) / 2);}// 4)public static Node right4(Node head) {if (head == null || head.next == null) {return null;}Node cur = head;ArrayList<Node> arr = new ArrayList<>();while (cur != null) {arr.add(cur);cur = cur.next;}return arr.get((arr.size() - 2) / 2);}
}
判断该链表是否为回文结构
给定一个单链表的头节点head,请判断该链表是否为回文结构。
1)栈方法特别简单 (笔试用)
2)改原链表的方法就需要注意边界了 (面试用)
public class Code02_IsPalindromeList {public static class Node {public int value;public Node next;public Node(int data) {this.value = data;}}// need n extra space 栈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;}// need n/2 extra space 栈 快慢指针public static boolean isPalindrome2(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;}// 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 (n2.next != null && n2.next.next != null) { // find mid noden1 = n1.next; // n1 -> midn2 = n2.next.next; // n2 -> end}// n1 中点n2 = n1.next; // n2 -> right part first noden1.next = null; // mid.next -> nullNode n3 = null;while (n2 != null) { // right part convertn3 = n2.next; // n3 -> save next noden2.next = n1; // next of right node convertn1 = n2; // n1 moven2 = n3; // n2 move}n3 = n1; // n3 -> save last noden2 = head;// n2 -> left first nodeboolean res = true;while (n1 != null && n2 != null) { // check palindromeif (n1.value != n2.value) {res = false;break;}n1 = n1.next; // left to midn2 = n2.next; // right to mid}n1 = n3.next;n3.next = null;while (n1 != null) { // recover listn2 = n1.next;n1.next = n3;n3 = n1;n1 = n2;}return res;}
}
将单向链表按某值划分成左边小、中间相等、右边大的形式
- 把链表放入数组里,在数组上做partition (笔试用)
- 分成小、中、大三部分,再把各个部分之间串起来(面试用)(有稳定性)
public class Code03_SmallerEqualBigger {public static class Node {public int value;public Node next;public Node(int data) {this.value = data;}}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];i = 0;cur = head;for (i = 0; i != nodeArr.length; i++) {nodeArr[i] = cur;cur = cur.next;}arrPartition(nodeArr, pivot);for (i = 1; i != nodeArr.length; i++) {nodeArr[i - 1].next = nodeArr[i];}nodeArr[i - 1].next = null;return nodeArr[0];}public static Node listPartition2(Node head, int pivot) {Node sH = null; // small headNode sT = null; // small tailNode eH = null; // equal headNode eT = null; // equal tailNode mH = null; // big headNode mT = null; // big tailNode next = null; // save next node// every node distributed to three listswhile (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 (mH == null) {mH = head;mT = head;} else {mT.next = head;mT = head;}}head = next;}// 小于区域的尾巴,连等于区域的头,等于区域的尾巴连大于区域的头if (sT != null) { // 如果有小于区域sT.next = eH;eT = eT == null ? sT : eT; // 下一步,谁去连大于区域的头,谁就变成eT}// 下一步,一定是需要用eT 去接 大于区域的头// 有等于区域,eT -> 等于区域的尾结点// 无等于区域,eT -> 小于区域的尾结点// eT 尽量不为空的尾巴节点if (eT != null) { // 如果小于区域和等于区域,不是都没有eT.next = mH;}return sH != null ? sH : (eH != null ? eH : mH);}
}
给定一个有next指针和rand指针的Node 组成的无环单链表头节点,实现一个函数完成链表的复制,返回新链表的头节点
1)使用hashmap*
2)不使用hashmap
https://leetcode.com/problems/copy-list-with-random-pointer/
import java.util.HashMap;
public class CopyListWithRandom {public static class Node {int val;Node next;Node random;public Node(int val) {this.val = val;this.next = null;this.random = null;}}public static Node copyRandomList1(Node head) {// key 老节点// value 新节点HashMap<Node, Node> map = new HashMap<Node, Node>();Node cur = head;while (cur != null) {map.put(cur, new Node(cur.val));cur = cur.next;}cur = head;while (cur != null) {// cur 老// map.get(cur) 新// 新.next -> cur.next克隆节点找到map.get(cur).next = map.get(cur.next);map.get(cur).random = map.get(cur.random);cur = cur.next;}return map.get(head);}public static Node copyRandomList2(Node head) {if (head == null) {return null;}Node cur = head;Node next = null;// 1 -> 2 -> 3 -> null// 1 -> 1' -> 2 -> 2' -> 3 -> 3'while (cur != null) {next = cur.next;cur.next = new Node(cur.val);cur.next.next = next;cur = next;}cur = head;Node copy = null;// 1 1' 2 2' 3 3'// 依次设置 1' 2' 3' random指针while (cur != null) {next = cur.next.next;copy = cur.next;copy.random = cur.random != null ? cur.random.next : null;cur = next;}Node res = head.next;cur = head;// 老 新 混在一起,next方向上,random正确// next方向上,把新老链表分离while (cur != null) {next = cur.next.next;copy = cur.next;cur.next = next;copy.next = next != null ? next.next : null;cur = next;}return res;}}
给定两个可能有环也可能无环的单链表,头节点head1和head2 实现一个函数,如果两个链表相交,返回相交的第一个结点,不相交返回null
(包含三类面试题的综合题)(除了以下做法,还可以使用hashSet更加简单的实现)
1)找到链表第一个入环节点,如果无环,返回null
2)如果两个链表都无环,返回第一个相交节点,如果不想交,返回null
3)两个有环链表,返回第一个相交节点,如果不想交返回null
public class 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;}// 找到链表第一个入环节点,如果无环,返回nullpublic static Node getLoopNode(Node head) {if (head == null || head.next == null || head.next.next == null) {return null;}// n1 慢 n2 快Node slow = head.next; // n1 -> slowNode fast = head.next.next; // n2 -> fastwhile (slow != fast) {if (fast.next == null || fast.next.next == null) {return null;}fast = fast.next.next;slow = slow.next;}// slow fast 相遇fast = head; // n2 -> walk again from headwhile (slow != fast) {slow = slow.next;fast = fast.next;}return slow;}// 如果两个链表都无环,返回第一个相交节点,如果不想交,返回nullpublic 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;}// n : 链表1长度减去链表2长度的值cur1 = n > 0 ? head1 : head2; // 谁长,谁的头变成cur1cur2 = cur1 == head1 ? head2 : head1; // 谁短,谁的头变成cur2n = Math.abs(n);while (n != 0) {n--;cur1 = cur1.next;}while (cur1 != cur2) {cur1 = cur1.next;cur2 = cur2.next;}return cur1;}// 两个有环链表,返回第一个相交节点,如果不想交返回nullpublic 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;}}
}
能不能不给单链表的头节点,只给想删除的结点,就能做到在链表上把这个点删掉?
不能