题目
1)输入链表头节点,奇数长度返回中点,偶数长度返回上中点
2)输入链表头节点,奇数长度返回中点,偶数长度返回下中点
3)输入链表头节点,奇数长度返回中点前一个,偶数长度返回上中点前一个
4)输入链表头节点,奇数长度返回中点前一个,偶数长度返回下中点前一个
思路:快慢指针
// 奇数 arr[len/2]
// 偶数 arr[len/2-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;
}
// 奇数 arr[len/2]
// 偶数 arr[len/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;
}
// 奇数 arr[len/2-1]
// 偶数 arr[len/2-2]
// 中间偏上前一个
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;
}
// 奇数 arr[len/2-1]
// 偶数 arr[len/2-1]
// 中间偏上后一个
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;
}
题目:给定一个单链表的头节点head,请判断该链表是否为回文结构。
1)哈希表方法特别简单(笔试用)
2)改原链表的方法就需要注意边界了(面试用)
思路:快慢指针,将中间元素next指针指向null ,同时更不后半部分next指向前一节点。前后双指正向中间遍历,直到为任意指针为null,时间复杂度n ,空间复杂度1
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 node
n1 = n1.next; // n1 -> mid
n2 = n2.next.next; // n2 -> end
}
// n1 中点
n2 = n1.next; // n2 -> right part first node
n1.next = null; // mid.next -> null
Node n3 = null;
while (n2 != null) { // right part convert
n3 = n2.next; // n3 -> save next node
n2.next = n1; // next of right node convert
n1 = n2; // n1 move
n2 = n3; // n2 move
}
n3 = n1; // n3 -> save last node
n2 = head;// n2 -> left first node
boolean res = true;
while (n1 != null && n2 != null) { // check palindrome
if (n1.value != n2.value) {
res = false;
break;
}
n1 = n1.next; // left to mid
n2 = n2.next; // right to mid
}
n1 = n3.next;
n3.next = null;
while (n1 != null) { // recover list
n2 = n1.next;
n1.next = n3;
n3 = n1;
n1 = n2;
}
return res;
}
思路:空间换时间 将来链表后半部放入堆栈,空间复杂度O(n/2)
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;
}
题目:将单向链表按某值划分成左边小、中间相等、右边大的形式
思路:把链表放入数组里,在数组上做partition
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 void arrPartition(Node[] nodeArr, int pivot) {
int small = -1;
int big = nodeArr.length;
int index = 0;
while (index != big) {
if (nodeArr[index].value < pivot) {
swap(nodeArr, ++small, index++);
} else if (nodeArr[index].value == pivot) {
index++;
} else {
swap(nodeArr, --big, index);
}
}
}
public static void swap(Node[] nodeArr, int a, int b) {
Node tmp = nodeArr[a];
nodeArr[a] = nodeArr[b];
nodeArr[b] = tmp;
}
思路:分成小、中、大三部分,再把各个部分之间串起来
public static Node listPartition2(Node head, int pivot) {
if (head == null)
return null;
Node lesshead = new Node(0);
Node lesstail = lesshead;
Node equalhead = new Node(0);
Node equaltail = equalhead;
Node morehead = new Node(0);
Node moretail = morehead;
Node cur = head;
while (cur != null){
if(cur.value == pivot){
equaltail.next = cur;
equaltail = equaltail.next;
}else if (cur.value<pivot){
lesstail.next = cur;
lesstail = lesstail.next;
}else{
moretail = cur;
moretail = moretail.next;
}
cur = cur.next;
}
// 关键步骤在这里
equaltail.next = morehead.next;
lesstail.next = equalhead.next;
return lesshead.next;
}
题目:链表操作 – 单链表和双链表如何反转
思路;递归和双指针
// 单向链表
// 反转单向链表
// 递归方式
public static Node reverseLinkedList(Node head) {
if(head == null)
return null;
return process(head);
}
// 调用方保证head不为null
public static Node process(Node head){
if(head.next == null)
// 当前节点为末尾节点
return head;
Node process = process(head.next);
head.next.next = head;
return process;
}
// 双指针方式
public static Node reverseLinkedList2(Node head) {
if(head == null)
return null;
if(head.next == null)
return head;
Node pre = null;
Node cur = head;
Node next = null;
while (cur != null){
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
// 双向链表
// 双指针
public static DoubleNode reverseDoubleList(DoubleNode head) {
if (head == null)
return null;
if (head.next == null)
return head;
DoubleNode pre = null;
DoubleNode cur = head;
DoubleNode next = null;
while (cur != null) {
next = cur.next;
cur.next = pre;
cur.last = next;
// 更新 pre cur next
pre = cur;
cur = next;
}
return pre;
}
public static DoubleNode reverseDoubleList1(DoubleNode head) {
if (head == null)
return null;
if (head.next == null) {
return head;
}
return process(head, null);
}
// 递归方式实现
public static DoubleNode process(DoubleNode cur, DoubleNode pre) {
if (cur.next == null) {
cur.next = pre;
cur.last = null;
return cur;
}
DoubleNode next = cur.next;
cur.next = pre;
cur.last = next;
DoubleNode ans = process(next, cur);
return ans;
}