目录
1. 反转链表(简单)
1.1. 题目描述
1.2. 解题思路
方法一:迭代(推荐使用)
public class Solution {
public ListNode ReverseList(ListNode head) {
//处理空链表
if(head == null)
return null;
ListNode cur = head;
ListNode pre = null;
while(cur != null){
//断开链表,要记录后续一个
ListNode temp = cur.next;
//当前的next指向前一个
cur.next = pre;
//前一个更新为当前
pre = cur;
//当前更新为刚刚记录的后一个
cur = temp;
}
return pre;
}
}
方法二:递归(扩展思路)
public class Solution {
public ListNode ReverseList(ListNode head) {
//递归结束条件
if(head == null || head.next == null)
return head;
//反转下一个
ListNode newHead = ReverseList(head.next);
//逆转本级节点
head.next.next = head;
//尾部设置空节点
head.next = null;
return newHead;
}
}
方法三:使用栈解决
import java.util.Stack;
public class Solution {
public ListNode ReverseList(ListNode head) {
Stack<ListNode> stack= new Stack<>();
//把链表节点全部摘掉放到栈中
while (head != null) {
stack.push(head);
head = head.next;
}
if (stack.isEmpty())
return null;
ListNode node = stack.pop();
ListNode dummy = node;
//栈中的结点全部出栈,然后重新连成一个新的链表
while (!stack.isEmpty()) {
ListNode tempNode = stack.pop();
node.next = tempNode;
node = node.next;
}
//最后一个结点就是反转前的头结点,一定要让他的next
//等于空,否则会构成环
node.next = null;
return dummy;
}
}
方法四:双链表求解
public ListNode ReverseList(ListNode head) {
//新链表
ListNode newHead = null;
while (head != null) {
//先保存访问的节点的下一个节点,保存起来
//留着下一步访问的
ListNode temp = head.next;
//每次访问的原链表节点都会成为新链表的头结点,
//其实就是把新链表挂到访问的原链表节点的
//后面就行了
head.next = newHead;
//更新新链表
newHead = head;
//重新赋值,继续访问
head = temp;
}
//返回新链表
return newHead;
}
2. 链表内指定区间反转(中等)
2.1. 题目描述
2.2. 解题思路
方法一:头插法迭代(推荐使用)
Java实现代码:
import java.util.*;
public class Solution {
public ListNode reverseBetween (ListNode head, int m, int n) {
//加个表头
ListNode res = new ListNode(-1);
res.next = head;
//前序节点
ListNode pre = res;
//当前节点
ListNode cur = head;
//找到m
for(int i = 1; i < m; i++){
pre = cur;
cur = cur.next;
}
//从m反转到n
for(int i = m; i < n; i++){
ListNode temp = cur.next;
cur.next = temp.next;
temp.next = pre.next;
pre.next = temp;
}
//返回去掉表头
return res.next;
}
}
方法二:递归(扩展思路)
Java实现代码:
import java.util.*;
public class Solution {
ListNode temp = null;
public ListNode reverse(ListNode head, int n){
//只颠倒第一个节点,后续不管
if(n == 1){
temp = head.next;
return head;
}
//进入子问题
ListNode node = reverse(head.next, n - 1);
//反转
head.next.next = head;
//每个子问题反转后的尾拼接第n个位置后的节点
head.next = temp;
return node;
}
public ListNode reverseBetween (ListNode head, int m, int n) {
//从第一个节点开始
if(m == 1)
return reverse(head, n);
//缩减子问题
ListNode node = reverseBetween(head.next, m - 1, n - 1);
//拼接已翻转
head.next = node;
return head;
}
}
3. 链表中的节点每k个一组翻转(中等)
3.1. 题目描述
3.2. 解题思路
方法一:递归(推荐使用)
Java实现代码:
import java.util.*;
public class Solution {
public ListNode reverseKGroup (ListNode head, int k) {
//找到每次翻转的尾部
ListNode tail = head;
//遍历k次到尾部
for(int i = 0; i < k; i++){
//如果不足k到了链表尾,直接返回,不翻转
if(tail == null)
return head;
tail = tail.next;
}
//翻转时需要的前序和当前节点
ListNode pre = null;
ListNode cur = head;
//在到达当前段尾节点前
while(cur != tail){
//翻转
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
//当前尾指向下一段要翻转的链表
head.next = reverseKGroup(tail, k);
return pre;
}
}
方法二:模拟法
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode hair = new ListNode(0);
hair.next = head;
ListNode pre = hair;
while (head != null) {
ListNode tail = pre;
// 查看剩余部分长度是否大于等于 k
for (int i = 0; i < k; ++i) {
tail = tail.next;
if (tail == null) {
return hair.next;
}
}
ListNode nex = tail.next;
ListNode[] reverse = myReverse(head, tail);
head = reverse[0];
tail = reverse[1];
// 把子链表重新接回原链表
pre.next = head;
tail.next = nex;
pre = tail;
head = tail.next;
}
return hair.next;
}
public ListNode[] myReverse(ListNode head, ListNode tail) {
ListNode prev = tail.next;
ListNode p = head;
while (prev != tail) {
ListNode nex = p.next;
p.next = prev;
prev = p;
p = nex;
}
return new ListNode[]{tail, head};
}
}
4. 排序链表(中等)
4.1. 题目描述
4.2. 解题思路
方法一:自顶向下归并排序
class Solution {
public ListNode sortList(ListNode head) {
return sortList(head, null);
}
public ListNode sortList(ListNode head, ListNode tail) {
if (head == null) {
return head;
}
if (head.next == tail) {
head.next = null;
return head;
}
ListNode slow = head, fast = head;
while (fast != tail) {
slow = slow.next;
fast = fast.next;
if (fast != tail) {
fast = fast.next;
}
}
ListNode mid = slow;
ListNode list1 = sortList(head, mid);
ListNode list2 = sortList(mid, tail);
ListNode sorted = merge(list1, list2);
return sorted;
}
public ListNode merge(ListNode head1, ListNode head2) {
ListNode dummyHead = new ListNode(0);
ListNode temp = dummyHead, temp1 = head1, temp2 = head2;
while (temp1 != null && temp2 != null) {
if (temp1.val <= temp2.val) {
temp.next = temp1;
temp1 = temp1.next;
} else {
temp.next = temp2;
temp2 = temp2.next;
}
temp = temp.next;
}
if (temp1 != null) {
temp.next = temp1;
} else if (temp2 != null) {
temp.next = temp2;
}
return dummyHead.next;
}
}
方法二:自底向上归并排序
class Solution {
public ListNode sortList(ListNode head) {
if (head == null) {
return head;
}
int length = 0;
ListNode node = head;
while (node != null) {
length++;
node = node.next;
}
ListNode dummyHead = new ListNode(0, head);
for (int subLength = 1; subLength < length; subLength <<= 1) {
ListNode prev = dummyHead, curr = dummyHead.next;
while (curr != null) {
ListNode head1 = curr;
for (int i = 1; i < subLength && curr.next != null; i++) {
curr = curr.next;
}
ListNode head2 = curr.next;
curr.next = null;
curr = head2;
for (int i = 1; i < subLength && curr != null && curr.next != null; i++) {
curr = curr.next;
}
ListNode next = null;
if (curr != null) {
next = curr.next;
curr.next = null;
}
ListNode merged = merge(head1, head2);
prev.next = merged;
while (prev.next != null) {
prev = prev.next;
}
curr = next;
}
}
return dummyHead.next;
}
public ListNode merge(ListNode head1, ListNode head2) {
ListNode dummyHead = new ListNode(0);
ListNode temp = dummyHead, temp1 = head1, temp2 = head2;
while (temp1 != null && temp2 != null) {
if (temp1.val <= temp2.val) {
temp.next = temp1;
temp1 = temp1.next;
} else {
temp.next = temp2;
temp2 = temp2.next;
}
temp = temp.next;
}
if (temp1 != null) {
temp.next = temp1;
} else if (temp2 != null) {
temp.next = temp2;
}
return dummyHead.next;
}
}
5. 合并两个排序的链表(简单)
5.1. 题目描述
5.2. 解题思路
方法一:双指针迭代(推荐使用)
Java实现代码:
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
//一个已经为空了,直接返回另一个
if(list1 == null)
return list2;
if(list2 == null)
return list1;
//加一个表头
ListNode head = new ListNode(0);
ListNode cur = head;
//两个链表都要不为空
while(list1 != null && list2 != null){
//取较小值的节点
if(list1.val <= list2.val){
cur.next = list1;
//只移动取值的指针
list1 = list1.next;
}else{
cur.next = list2;
//只移动取值的指针
list2 = list2.next;
}
//指针后移
cur = cur.next;
}
//哪个链表还有剩,直接连在后面
if(list1 != null)
cur.next = list1;
else
cur.next = list2;
//返回值去掉表头
return head.next;
}
}
方法二:双指针递归(扩展思路)
Java实现代码:
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
//一个已经为空了,返回另一个
if(list1 == null)
return list2;
if(list2 == null)
return list1;
//先用较小的值的节点
if(list1.val <= list2.val){
//递归往下
list1.next = Merge(list1.next, list2);
return list1;
}else{
//递归往下
list2.next = Merge(list1, list2.next);
return list2;
}
}
}
6. 合并k个已排序的链表(较难)
6.1. 题目描述
6.2. 解题思路
方法一:归并排序思想(推荐使用)
图示:
Java实现代码:
import java.util.ArrayList;
public class Solution {
//两个链表合并函数
public ListNode Merge(ListNode list1, ListNode list2) {
//一个已经为空了,直接返回另一个
if(list1 == null)
return list2;
if(list2 == null)
return list1;
//加一个表头
ListNode head = new ListNode(0);
ListNode cur = head;
//两个链表都要不为空
while(list1 != null && list2 != null){
//取较小值的节点
if(list1.val <= list2.val){
cur.next = list1;
//只移动取值的指针
list1 = list1.next;
}else{
cur.next = list2;
//只移动取值的指针
list2 = list2.next;
}
//指针后移
cur = cur.next;
}
//哪个链表还有剩,直接连在后面
if(list1 != null)
cur.next = list1;
else
cur.next = list2;
//返回值去掉表头
return head.next;
}
//划分合并区间函数
ListNode divideMerge(ArrayList<ListNode> lists, int left, int right){
if(left > right)
return null;
//中间一个的情况
else if(left == right)
return lists.get(left);
//从中间分成两段,再将合并好的两段合并
int mid = (left + right) / 2;
return Merge(divideMerge(lists, left, mid), divideMerge(lists, mid + 1, right));
}
public ListNode mergeKLists(ArrayList<ListNode> lists) {
//k个链表归并排序
return divideMerge(lists, 0, lists.size() - 1);
}
}
方法二:优先队列(扩展思路)
Java实现代码:
import java.util.*;
public class Solution {
public ListNode mergeKLists(ArrayList<ListNode> lists) {
//小顶堆
Queue<ListNode> pq = new PriorityQueue<>((v1, v2) -> v1.val - v2.val);
//遍历所有链表第一个元素
for(int i = 0; i < lists.size(); i++){
//不为空则加入小顶堆
if(lists.get(i) != null)
pq.add(lists.get(i));
}
//加一个表头
ListNode res = new ListNode(-1);
ListNode head = res;
//直到小顶堆为空
while(!pq.isEmpty()){
//取出最小的元素
ListNode temp = pq.poll();
//连接
head.next = temp;
head = head.next;
//每次取出链表的后一个元素加入小顶堆
if(temp.next != null)
pq.add(temp.next);
}
//去掉表头
return res.next;
}
}
7. 判断链表中是否有环(简单)
7.1. 题目描述
7.2. 解题思路
方法:双指针(推荐使用)
Java实现代码:
public class Solution {
public boolean hasCycle(ListNode head) {
//先判断链表为空的情况
if(head == null)
return false;
//快慢双指针
ListNode fast = head;
ListNode slow = head;
//如果没环快指针会先到链表尾
while(fast != null && fast.next != null){
//快指针移动两步
fast = fast.next.next;
//慢指针移动一步
slow = slow.next;
//相遇则有环
if(fast == slow)
return true;
}
//到末尾则没有环
return false;
}
}
8. 链表中环的入口结点(中等)
8.1. 题目描述
8.2. 解题思路
方法:双指针(推荐使用)
Java实现代码:
public class Solution {
//判断有没有环,返回相遇的地方
public ListNode hasCycle(ListNode head) {
//先判断链表为空的情况
if(head == null)
return null;
//快慢双指针
ListNode fast = head;
ListNode slow = head;
//如果没环快指针会先到链表尾
while(fast != null && fast.next != null){
//快指针移动两步
fast = fast.next.next;
//慢指针移动一步
slow = slow.next;
//相遇则有环,返回相遇的位置
if(fast == slow)
return slow;
}
//到末尾说明没有环,返回null
return null;
}
public ListNode EntryNodeOfLoop(ListNode pHead) {
ListNode slow = hasCycle(pHead);
//没有环
if(slow == null)
return null;
//快指针回到表头
ListNode fast = pHead;
//再次相遇即是环入口
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
9. 相交链表(简单)
9.1. 题目描述
9.2. 解题思路
方法一:哈希集合
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
Set<ListNode> visited = new HashSet<ListNode>();
ListNode temp = headA;
while (temp != null) {
visited.add(temp);
temp = temp.next;
}
temp = headB;
while (temp != null) {
if (visited.contains(temp)) {
return temp;
}
temp = temp.next;
}
return null;
}
}
方法二:双指针
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
ListNode pA = headA, pB = headB;
while (pA != pB) {
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
}
}
10. 环形链表II(中等)
10.1. 题目描述
10.2. 解题思路
方法一:哈希表
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode pos = head;
Set<ListNode> visited = new HashSet<ListNode>();
while (pos != null) {
if (visited.contains(pos)) {
return pos;
} else {
visited.add(pos);
}
pos = pos.next;
}
return null;
}
}
方法二:快慢指针
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null) {
return null;
}
ListNode slow = head, fast = head;
while (fast != null) {
slow = slow.next;
if (fast.next != null) {
fast = fast.next.next;
} else {
return null;
}
if (fast == slow) {
ListNode ptr = head;
while (ptr != slow) {
ptr = ptr.next;
slow = slow.next;
}
return ptr;
}
}
return null;
}
}
11. 链表中倒数最后k个结点(简单)
11.1. 题目描述
11.2. 解题思路
方法一:快慢双指针(推荐使用)
Java实现代码:
import java.util.*;
public class Solution {
public ListNode FindKthToTail (ListNode pHead, int k) {
int n = 0;
ListNode fast = pHead;
ListNode slow = pHead;
//快指针先行k步
for(int i = 0; i < k; i++){
if(fast != null)
fast = fast.next;
//达不到k步说明链表过短,没有倒数k
else
return slow = null;
}
//快慢指针同步,快指针先到底,慢指针指向倒数第k个
while(fast != null){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
方法二:先找长度再找最后k(扩展思路)
Java实现代码:
import java.util.*;
public class Solution {
public ListNode FindKthToTail (ListNode pHead, int k) {
int n = 0;
ListNode p = pHead;
//遍历链表,统计链表长度
while(p != null){
n++;
p = p.next;
}
//长度过小,返回空链表
if(n < k)
return null;
p = pHead;
//遍历n-k次
for(int i = 0; i < n - k; i++)
p = p.next;
return p;
}
}
12. 两个链表的第一个公共结点(简单)
12.1. 题目描述
12.2. 解题思路
方法:双指针
Java实现代码:
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode l1 = pHead1, l2 = pHead2;
while(l1 != l2){
l1 = (l1==null)?pHead2:l1.next;
l2 = (l2==null)?pHead1:l2.next;
}
return l1;
}
}
13. 链表相加(二)(中等)
13.1. 题目描述
13.2 解题思路
方法:反转链表法(推荐使用)
Java实现代码:
import java.util.*;
public class Solution {
//反转链表
public ListNode ReverseList(ListNode pHead) {
if(pHead == null)
return null;
ListNode cur = pHead;
ListNode pre = null;
while(cur != null){
//断开链表,要记录后续一个
ListNode temp = cur.next;
//当前的next指向前一个
cur.next = pre;
//前一个更新为当前
pre = cur;
//当前更新为刚刚记录的后一个
cur = temp;
}
return pre;
}
public ListNode addInList (ListNode head1, ListNode head2) {
//任意一个链表为空,返回另一个
if(head1 == null)
return head2;
if(head2 == null)
return head1;
//反转两个链表
head1 = ReverseList(head1);
head2 = ReverseList(head2);
//添加表头
ListNode res = new ListNode(-1);
ListNode head = res;
//进位符号
int carry = 0;
//只要某个链表还有或者进位还有
while(head1 != null || head2 != null || carry != 0){
//链表不为空则取其值
int val1 = head1 == null ? 0 : head1.val;
int val2 = head2 == null ? 0 : head2.val;
//相加
int temp = val1 + val2 + carry;
//获取进位
carry = temp / 10;
temp %= 10;
//添加元素
head.next = new ListNode(temp);
head = head.next;
//移动下一个
if(head1 != null)
head1 = head1.next;
if(head2 != null)
head2 = head2.next;
}
//结果反转回来
return ReverseList(res.next);
}
}
14. 两数相加(中等)
14.1. 题目描述
14.2. 解题思路
方法一:模拟法
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode head = null, tail = null;
int carry = 0;
while (l1 != null || l2 != null) {
int n1 = l1 != null ? l1.val : 0;
int n2 = l2 != null ? l2.val : 0;
int sum = n1 + n2 + carry;
if (head == null) {
head = tail = new ListNode(sum % 10);
} else {
tail.next = new ListNode(sum % 10);
tail = tail.next;
}
carry = sum / 10;
if (l1 != null) {
l1 = l1.next;
}
if (l2 != null) {
l2 = l2.next;
}
}
if (carry > 0) {
tail.next = new ListNode(carry);
}
return head;
}
}
15. 单链表的排序(中等)
15.1. 题目描述
15.2. 解题思路
方法一:归并排序(推荐使用)
Java实现代码:
import java.util.*;
public class Solution {
//合并两段有序链表
ListNode merge(ListNode pHead1, ListNode pHead2) {
//一个已经为空了,直接返回另一个
if(pHead1 == null)
return pHead2;
if(pHead2 == null)
return pHead1;
//加一个表头
ListNode head = new ListNode(0);
ListNode cur = head;
//两个链表都要不为空
while(pHead1 != null && pHead2 != null){
//取较小值的节点
if(pHead1.val <= pHead2.val){
cur.next = pHead1;
//只移动取值的指针
pHead1 = pHead1.next;
}else{
cur.next = pHead2;
//只移动取值的指针
pHead2 = pHead2.next;
}
//指针后移
cur = cur.next;
}
//哪个链表还有剩,直接连在后面
if(pHead1 != null)
cur.next = pHead1;
else
cur.next = pHead2;
//返回值去掉表头
return head.next;
}
public ListNode sortInList (ListNode head) {
//链表为空或者只有一个元素,直接就是有序的
if(head == null || head.next == null)
return head;
ListNode left = head;
ListNode mid = head.next;
ListNode right = head.next.next;
//右边的指针到达末尾时,中间的指针指向该段链表的中间
while(right != null && right.next != null){
left = left.next;
mid = mid.next;
right = right.next.next;
}
//左边指针指向左段的左右一个节点,从这里断开
left.next = null;
//分成两段排序,合并排好序的两段
return merge(sortInList(head), sortInList(mid));
}
}
方法二:转化为数组排序(扩展思路)
import java.util.*;
public class Solution {
public ListNode sortInList (ListNode head) {
ArrayList<Integer> nums = new ArrayList();
ListNode p = head;
//遍历链表,将节点值加入数组
while(p != null){
nums.add(p.val);
p = p.next;
}
p = head;
//对数组元素排序
Collections.sort(nums);
//遍历数组
for(int i = 0; i < nums.size(); i++){
//将数组元素依次加入链表
p.val = nums.get(i);
p = p.next;
}
return head;
}
}
16. 判断一个链表是否为回文结构(简单)
16.1. 题目描述
16.2. 解题思路
方法一:数组复制反转法(前置知识)
Java实现代码:
import java.util.*;
public class Solution {
public boolean isPail (ListNode head) {
ArrayList<Integer> nums = new ArrayList();
//将链表元素取出一次放入数组
while(head != null){
nums.add(head.val);
head = head.next;
}
ArrayList<Integer> temp = new ArrayList();
temp = (ArrayList<Integer>) nums.clone();
//准备一个数组承接翻转之后的数组
Collections.reverse(temp);
for(int i = 0; i < nums.size(); i++){
int x = nums.get(i);
int y = temp.get(i);
//正向遍历与反向遍历相同
if(x != y)
return false;
}
return true;
}
}
方法二:数组复制双指针(前置知识)
Java实现代码:
import java.util.*;
public class Solution {
public boolean isPail (ListNode head) {
ArrayList<Integer> nums = new ArrayList();
//将链表元素取出一次放入数组
while(head != null){
nums.add(head.val);
head = head.next;
}
//双指针指向首尾
int left = 0;
int right = nums.size() - 1;
//分别从首尾遍历,代表正序和逆序
while(left <= right){
int x = nums.get(left);
int y = nums.get(right);
//如果不一致就是不为回文
if(x != y)
return false;
left++;
right--;
}
return true;
}
}
方法三:长度法找中点(推荐使用)
Java实现代码:
import java.util.*;
public class Solution {
//反转链表指针
ListNode reverse(ListNode head) {
//前序节点
ListNode prev = null;
while(head != null){
//断开后序
ListNode next = head.next;
//指向前序
head.next = prev;
prev = head;
head = next;
}
return prev;
}
public boolean isPail (ListNode head) {
ListNode p = head;
int n = 0;
//找到链表长度
while(p != null){
n++;
p = p.next;
}
//中点
n = n / 2;
p = head;
//遍历到中点位置
while(n > 0){
p = p.next;
n--;
}
//中点处反转
p = reverse(p);
ListNode q = head;
while(p != null){
//比较判断节点值是否相等
if(p.val != q.val)
return false;
p = p.next;
q = q.next;
}
return true;
}
}
方法四:双指针找中点(推荐使用)
Java实现代码:
import java.util.*;
public class Solution {
//反转链表指针
ListNode reverse(ListNode head) {
//前序节点
ListNode prev = null;
while(head != null){
//断开后序
ListNode next = head.next;
//指向前序
head.next = prev;
prev = head;
head = next;
}
return prev;
}
public boolean isPail (ListNode head) {
//空链表直接为回文
if(head == null)
return true;
//准备快慢双指针
ListNode slow = head;
ListNode fast = head;
//双指针找中点
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
//中点处反转
slow = reverse(slow);
fast = head;
while(slow != null){
//比较判断节点值是否相等
if(slow.val != fast.val)
return false;
fast = fast.next;
slow = slow.next;
}
return true;
}
}
方法五:栈逆序(扩展思路)
Java实现代码:
import java.util.*;
public class Solution {
public boolean isPail (ListNode head) {
ListNode p = head;
Stack<Integer> s = new Stack();
//辅助栈记录元素
while(p != null){
s.push(p.val);
p = p.next;
}
p = head;
//正序遍历链表,从栈中弹出的内容是逆序的
while(!s.isEmpty()){
//比较是否相同
if(p.val != s.pop())
return false;
p = p.next;
}
return true;
}
}
17. 链表的奇偶重排(中等)
17.1. 题目描述
17.2. 解题思路
方法:双指针(推荐使用)
Java实现代码:
import java.util.*;
public class Solution {
public ListNode oddEvenList (ListNode head) {
//如果链表为空,不用重排
if(head == null)
return head;
//even开头指向第二个节点,可能为空
ListNode even = head.next;
//odd开头指向第一个节点
ListNode odd = head;
//指向even开头
ListNode evenhead = even;
while(even != null && even.next != null){
//odd连接even的后一个,即奇数位
odd.next = even.next;
//odd进入后一个奇数位
odd = odd.next;
//even连接后一个奇数的后一位,即偶数位
even.next = odd.next;
//even进入后一个偶数位
even = even.next;
}
//even整体接在odd后面
odd.next = evenhead;
return head;
}
}
18. 删除有序链表中重复的元素-I(简单)
18.1. 题目描述
18.2. 解题思路
方法:遍历删除(推荐使用)
Java实现代码:
import java.util.*;
public class Solution {
public ListNode deleteDuplicates (ListNode head) {
//空链表
if(head == null)
return null;
//遍历指针
ListNode cur = head;
//指针当前和下一位不为空
while(cur != null && cur.next != null){
//如果当前与下一位相等则忽略下一位
if(cur.val == cur.next.val)
cur.next = cur.next.next;
//否则指针正常遍历
else
cur = cur.next;
}
return head;
}
}
19. 删除有序链表中重复的元素-II(中等)
19.1. 题目描述
19.2. 解题思路
方法一:直接比较删除(推荐使用)
Java实现代码:
import java.util.*;
public class Solution {
public ListNode deleteDuplicates (ListNode head) {
//空链表
if(head == null)
return null;
ListNode res = new ListNode(0);
//在链表前加一个表头
res.next = head;
ListNode cur = res;
while(cur.next != null && cur.next.next != null){
//遇到相邻两个节点值相同
if(cur.next.val == cur.next.next.val){
int temp = cur.next.val;
//将所有相同的都跳过
while (cur.next != null && cur.next.val == temp)
cur.next = cur.next.next;
}
else
cur = cur.next;
}
//返回时去掉表头
return res.next;
}
}
方法二:哈希表(扩展思路)
Java实现代码:
import java.util.*;
public class Solution {
public ListNode deleteDuplicates (ListNode head) {
//空链表
if(head == null)
return null;
Map<Integer,Integer> mp = new HashMap<>();
ListNode cur = head;
//遍历链表统计每个节点值出现的次数
while(cur != null){
if(mp.containsKey(cur.val))
mp.put(cur.val, (int)mp.get(cur.val) + 1);
else
mp.put(cur.val,1);
cur = cur.next;
}
ListNode res = new ListNode(0);
//在链表前加一个表头
res.next = head;
cur = res;
//再次遍历链表
while(cur.next != null){
//如果节点值计数不为1
if(mp.get(cur.next.val) != 1)
//删去该节点
cur.next = cur.next.next;
else
cur = cur.next;
}
//去掉表头
return res.next;
}
}
20. 删除链表的中间节点(中等)
20.1. 题目描述
20.2. 解题思路
解题思路: 使用 slow 记录慢指针,fast 记录快指针。
当 fast 的再一次移动就结束时,说明 slow 的再一次移动也将到达中间点,
那么这个时候就可以直接用 slow.next = slow.next.next 来去掉中间节点。
方法一:快慢指针
class Solution {
public ListNode deleteMiddle(ListNode head) {
if (head == null || head.next == null) {
return null;
}
ListNode slow = head;
ListNode fast = head.next;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
slow.next = slow.next.next;
return head;
}
}
21. 删除链表的倒数第n个节点(中等)
21.1. 题目描述
21.2. 解题思路
方法一:双指针(推荐使用)
Java实现代码:
import java.util.*;
public class Solution {
public ListNode removeNthFromEnd (ListNode head, int n) {
//添加表头
ListNode res = new ListNode(-1);
res.next = head;
//当前节点
ListNode cur = head;
//前序节点
ListNode pre = res;
ListNode fast = head;
//快指针先行n步
while(n != 0){
fast = fast.next;
n--;
}
//快慢指针同步,快指针到达末尾,慢指针就到了倒数第n个位置
while(fast != null){
fast = fast.next;
pre = cur;
cur = cur.next;
}
//删除该位置的节点
pre.next = cur.next;
//返回去掉头
return res.next;
}
}
方法二:长度统计法(思路扩展)
Java实现代码:
import java.util.*;
public class Solution {
public ListNode removeNthFromEnd (ListNode head, int n) {
//记录链表长度
int length = 0;
//添加表头
ListNode res = new ListNode(-1);
res.next = head;
//当前节点
ListNode cur = head;
//前序节点
ListNode pre = res;
//找到链表长度
while(cur != null){
length++;
cur = cur.next;
}
//回到头部
cur = head;
//从头遍历找到倒数第n个位置
for(int i = 0; i < length - n; i++){
pre = cur;
cur = cur.next;
}
//删去倒数第n个节点
pre.next = cur.next;
//返回去掉头节点
return res.next;
}
}
22. 从尾到头打印链表(简单)
22.1. 题目描述
22.2. 解题思路
class Solution {
public int[] reversePrint(ListNode head) {
int count = 0; //链表结点的个数
ListNode p = head;
while(p != null){ //统计链表中结点的个数
count++;
p = p.next;
}
int[] ans = new int[count]; //创建一个和结点个数等大的数组
p = head;
int index = count - 1; //作为索引填充数组(从后往前填充)
while(p != null){
ans[index] = p.val;
p = p.next;
index--;
}
return ans;
}
}
23. 两两交换链表中的节点(中等)
23.1. 题目描述
23.2. 解题思路
方法一:递归
class Solution {
public ListNode swapPairs(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode newHead = head.next;
head.next = swapPairs(newHead.next);
newHead.next = head;
return newHead;
}
}
方法二:迭代
Java代码:
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode temp = dummyHead;
while (temp.next != null && temp.next.next != null) {
ListNode node1 = temp.next;
ListNode node2 = temp.next.next;
temp.next = node2;
node1.next = node2.next;
node2.next = node1;
temp = node1;
}
return dummyHead.next;
}
}
24. 复制带随机指针的链表(中等)
24.1. 题目描述
24.2. 解题思路
方法一:回溯 + 哈希表
Java代码:
class Solution {
Map<Node, Node> cachedNode = new HashMap<Node, Node>();
public Node copyRandomList(Node head) {
if (head == null) {
return null;
}
if (!cachedNode.containsKey(head)) {
Node headNew = new Node(head.val);
cachedNode.put(head, headNew);
headNew.next = copyRandomList(head.next);
headNew.random = copyRandomList(head.random);
}
return cachedNode.get(head);
}
}
方法二:迭代 + 节点拆分
class Solution {
public Node copyRandomList(Node head) {
if (head == null) {
return null;
}
for (Node node = head; node != null; node = node.next.next) {
Node nodeNew = new Node(node.val);
nodeNew.next = node.next;
node.next = nodeNew;
}
for (Node node = head; node != null; node = node.next.next) {
Node nodeNew = node.next;
nodeNew.random = (node.random != null) ? node.random.next : null;
}
Node headNew = head.next;
for (Node node = head; node != null; node = node.next) {
Node nodeNew = node.next;
node.next = node.next.next;
nodeNew.next = (nodeNew.next != null) ? nodeNew.next.next : null;
}
return headNew;
}
}
25. 链表最大孪生和(中等)
25.1. 题目描述
25.2. 解题思路
快慢指针+ 翻转链表(头部翻转)
传统方法:
- 先使用快慢指针找到中间节点 Using the Fast& Slow pointers to Find the middle point
- 翻转第二部分 Reverse the secondPart
- 通过同时遍历第一部分与反转后的第二部分得出最大结果 Add the values and store the maximum value
具体代码如下:
class Solution {
public int pairSum(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
ListNode prev = reverse(slow);
ListNode head1 = head, head2 = prev;
int res = 0;
while (head2 != null) {
res = Math.max(head1.val + head2.val, res);
head1 = head1.next;
head2 = head2.next;
}
return res;
}
private ListNode reverse(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode curr = head;
ListNode prev = null;
while (curr != null) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
}
对原有思路进行改进:
可以在使用快慢指针时同时对第一部分(前一半)链表进行翻转操作 相较于原先 找到中间节点后翻转第二部分 更加简洁快速
具体代码如下:
class Solution {
public int pairSum(ListNode head) {
ListNode fast = head;
ListNode slow = head;
ListNode prev = null;
while (fast != null && fast.next != null) {
fast = fast.next.next;
ListNode next = slow.next;
slow.next = prev;
prev = slow;
slow = next;
}
int res = 0;
while (slow != null) {
res = Math.max(res, prev.val + slow.val);
prv = prev.next;
slow = slow.next;
}
return res;
}
}