反转链表及相关算法题

反转链表是链表的一个常用操作,一些算法题中也需要用到此操作,是值得学习探讨的。

1 反转链表

剑指 Offer 24. 反转链表
题目描述:定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

涉及到链表的操作,我们可以先在纸上画出过程,写程序时思路会更清晰
反转链表图解

根据以上图解,思路主要为两个步骤循环操作:

  1. 将当前节点next指向pre
  2. 前移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个一组翻转链表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ariel小葵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值