反转链表是链表的一个常用操作,一些算法题中也需要用到此操作,是值得学习探讨的。
1 反转链表
剑指 Offer 24. 反转链表
题目描述:定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
涉及到链表的操作,我们可以先在纸上画出过程,写程序时思路会更清晰
根据以上图解,思路主要为两个步骤循环操作:
- 将当前节点next指向pre
- 前移pre, curr, next
//循环体内:
curr.next = pre;
pre = curr;
curr = next;
next = next.next;
出现问题:执行到 第四行 会出现NullPointerException
,因为最后一步(图中第n步) 时curr
已经为null
,再执行null.next
就会产生空指针异常。
解决方案:将循环体内的 第四行 移到 第一行,相应地,初始化时 next = head.next
也改为 next = head
另外注意:
- 结束条件:
curr == null
- 返回值:
pre
为新的head - 考虑边界情况:如果链表为空 (
head==null
) 或 只有一个节点 (head.next==null
),直接返回head
;正好符合循环操作,结果一致,故可省略
实现代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if (head==null || head.next==null) return head; //可省略
ListNode pre = null, curr = head, next = head;
while (curr != null) {
next = next.next;
curr.next = pre;
pre = curr;
curr = next;
}
return pre;
}
}
2 相关题目
2.1 两个链表整数相加
题目描述:用单向链表表示十进制数,求两个整数的和 (不允许使用其他数据结构)
示例:
1->2->3->4
+ 5->6->7
-------------
1->8->0->1
思路:
- 反转两个链表,计算长度
- 同时遍历相加,得到的数%10 存在长的链表中(如果两个数字加和>=10,则用digit记录进1)
- 最后再反转长的链表(如果digit>0,则还需添加一个节点在新head之前)
4->3->2->1
+ 7->6->5
-------------
1->0->8->1 反转:1->8->0->1
实现代码:
public class Test {
public static void main(String[] args) {
ListNode head1 = new ListNode(1);
ListNode node2 = new ListNode(2);
ListNode node3 = new ListNode(3);
ListNode node4 = new ListNode(4);
ListNode head2 = new ListNode(5);
ListNode node6 = new ListNode(6);
ListNode node7 = new ListNode(7);
head1.next = node2;
node2.next = node3;
node3.next = node4;
head2.next = node6;
node6.next = node7;
printList(head1);
printList(head2);
ListNode newHead = addTwoList(head1, head2);
printList(newHead);
}
// 链表相加
public static ListNode addTwoList(ListNode head1, ListNode head2) {
int n1 = size(head1), n2 = size(head2);
head1 = reverseList(head1);
head2 = reverseList(head2);
ListNode cur1 = (n1 >= n2) ? head1 : head2;
ListNode cur2 = (n1 >= n2) ? head2 : head1;
int digit = 0;
while (cur1 != null) {
cur1.val += (cur2 == null) ? digit : (digit+cur2.val);
digit = (cur1.val >= 10) ? 1 : 0;
cur1.val %= 10;
cur1 = cur1.next;
cur2 = (cur2 != null) ? cur2.next : cur2;
}
ListNode newHead = (n1 >= n2) ? head1 : head2;
newHead = reverseList(newHead);
if (digit > 0) {
ListNode tmp = new ListNode(digit);
tmp.next = newHead;
newHead = tmp;
}
return newHead;
}
// 反转链表
public static ListNode reverseList(ListNode head) {
ListNode pre = null, curr = head, next = head;
while (curr != null) {
next = next.next;
curr.next = pre;
pre = curr;
curr = next;
}
return pre;
}
// 获取链表长度
public static int size(ListNode head) {
int size = 0;
while (head != null) {
size++;
head = head.next;
}
return size;
}
// 打印链表
public static void printList(ListNode head) {
while (head != null) {
System.out.print(head.val);
head = head.next;
}
System.out.println();
}
}
class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
}
}
2.2 K 个一组翻转链表 (LeetCode 25)
题目描述:给定一个链表,每 k 个节点一组进行翻转,返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例:
1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
借用图解中的图:
Ref:
LeetCode题解:k个一组翻转链表