[leetcode] 24. 两两交换链表中的节点

题目描述

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例 1:
交换链表

输入:head = [1,2,3,4]
输出:[2,1,4,3]

示例 2:

输入:head = []
输出:[]

示例 3:

输入:head = [1]
输出:[1]

提示:

  • 链表中节点的数目在范围 [0, 100]
  • 0 <= Node.val <= 100

解题方法

方法一:数组存储

已知链表结点两两交换,那我们只需要提供两个 l i s t list list集合,分别按先后顺序存储交换后的结点。遍历链表时以 n u m num num计数( n u m num num初始为0),每遍历到一个结点,记录当前 n u m num num的奇偶性,当 n u m num num是偶数时,将当前结点添加到 l i s t 2 list2 list2中;若 n u m num num是奇数时,则将当前结点添加到 l i s t 1 list1 list1中。之后, n u m + + num++ num++。数组存储完成之后,我们按照先 l i s t 1 list1 list1的一个结点,再 l i s t 2 list2 list2的一个结点添加到最终链表的尾部。直到数组遍历完成,此时 l i s t 1 list1 list1的第一个结点即为交换后链表的头结点。

java代码

ListNode结构

public class ListNode {
    public int val;
    public ListNode next;

    public ListNode() {
    }

    public ListNode(int val) {
        this.val = val;
    }

    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

结点交换方法

public ListNode swapPairs(ListNode head) {
    if (head == null || head.next == null) {
        return head;
    }
    List<ListNode> list1 = new ArrayList<>();
    List<ListNode> list2 = new ArrayList<>();
    int num = 0;
    ListNode curNode = head;
    while (curNode != null) {
        if (num % 2 == 0) {
            list2.add(curNode);
        } else {
            list1.add(curNode);
        }
        num++;
        curNode = curNode.next;
    }
    for (int i = 0; i < list1.size(); i++) {
        ListNode node = list1.get(i);
        node.next = list2.get(i);
        //list1没遍历到尾部
        if (i != list1.size() - 1) {
            //list1后面还有结点
            node.next.next = list1.get(i + 1);
        } else {
            //list2比list1多一个元素
            if (list2.size() > list1.size()) {
                //list2的最后一个元素是最后一个结点
                node.next.next = list2.get(i + 1);
            } else {
                //list1和list2元素一样多,list2最后一个结点的next指针指向null
                node.next.next = null;
            }
        }
    }
    return list1.get(0);
}

复杂度分析

时间复杂度:需要遍历一次链表,再遍历一次 l i s t list list数组,所以渐进时间复杂度为 O ( N ) O(N) O(N) N N N为链表长度。
空间复杂度: O ( N ) O(N) O(N)。需要用数组存储链表结点。

方法二:递归

我们还可以采用递归的方式构造交换链表。思路是在每次递归中,记 s w a p P a i r s swapPairs swapPairs方法中的参数 h e a d head head为第一个结点,第二个结点是 h e a d . n e x t head.next head.next,记为 n e w H e a d newHead newHead,第三个结点是 n e w H e a d . n e x t newHead.next newHead.next结点,记 n e x t H e a d nextHead nextHead结点。我们需要将 n e w H e a d newHead newHead变为第一个结点,并将 n e w H e a d . n e x t newHead.next newHead.next指向 h e a d head head,然后将 h e a d . n e x t head.next head.next结点指向 s w a p P a i r s ( n e x t H e a d ) swapPairs(nextHead) swapPairs(nextHead)返回的结点,最终返回参数为 n e w H e a d newHead newHead。终止条件是当 h e a d head head或者 h e a d . n e x t head.next head.next为空时,直接返回 h e a d head head

java代码

public ListNode swapPairs(ListNode head) {
    if (head == null || head.next == null) {
        return head;
    }
    ListNode newHead = head.next;
    ListNode nextHead = newHead.next;
    newHead.next = head;
    head.next = swapPairs(nextHead);
    return newHead;
}

复杂度分析

时间复杂度:需要遍历一次链表,所以渐进时间复杂度为 O ( N ) O(N) O(N) N N N为链表长度。递归深度为 N / 2 N/2 N/2 N N N最大为100,深度最大为50,不会栈溢出。
空间复杂度: O ( N ) O(N) O(N)。空间复杂度主要取决于递归栈占用的空间。

方法三:迭代

为了减少递归栈占用的空间,我们也可以将递归改为迭代的方式。我们记遍历中原链表的第一个结点为 p r e N o d e preNode preNode p r e N o d e preNode preNode n e x t next next结点为第二个结点,记为 n o d e node node,第三个结点为 n o d e node node n e x t next next结点,记为 n e x t P r e N o d e nextPreNode nextPreNode。每次迭代中,如果 n e x t P r e N o d e nextPreNode nextPreNode n e x t P r e N o d e . n e x t nextPreNode.next nextPreNode.next存在,我们需要将 n o d e . n e x t node.next node.next指向 p r e N o d e preNode preNode p r e N o d e . n e x t preNode.next preNode.next指向 n e x t P r e N o d e . n e x t nextPreNode.next nextPreNode.next。再将 p r e N o d e preNode preNode指向 n e x t P r e N o d e nextPreNode nextPreNode n o d e node node指向 n e x t P r e N o d e . n e x t nextPreNode.next nextPreNode.next,进入下一次迭代。
若迭代中 n e x t P r e N o d e nextPreNode nextPreNode或者 n e x t P r e N o d e . n e x t nextPreNode.next nextPreNode.next不存在,则 n o d e . n e x t node.next node.next指向 p r e N o d e preNode preNode p r e N o d e . n e x t preNode.next preNode.next指向 n e x t P r e N o d e nextPreNode nextPreNode,然后跳出迭代。

java代码

public ListNode swapPairs(ListNode head) {
    if (head == null || head.next == null) {
        return head;
    }
    ListNode newHead = head.next;
    ListNode node = newHead;
    ListNode preNode = head;
    while (true) {
        ListNode nextPreNode = node.next;
        node.next = preNode;
        //nextPreNode后面不再有结点
        if (nextPreNode == null || nextPreNode.next == null) {
            preNode.next = nextPreNode;
            break;
        }
        preNode.next = nextPreNode.next;
        preNode = nextPreNode;
        node = nextPreNode.next;
    }
    return newHead;
}

复杂度分析

时间复杂度:需要遍历一次链表,所以渐进时间复杂度为 O ( N ) O(N) O(N)
空间复杂度: O ( 1 ) O(1) O(1)。只有常数级别的指针存储。

相似题目

[leetcode] 25. K 个一组翻转链表


  • 个人公众号
    个人公众号
  • 个人小游戏
    个人小游戏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

会飞的大鱼人

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

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

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

打赏作者

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

抵扣说明:

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

余额充值