2024-03-25

题目114、题目430、题目206、题目92、题目25


题目一描述

114. 二叉树展开为链表

给你二叉树的根结点 root ,请你将它展开为一个单链表:

展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。

示例 1:


示例1

输入:root = [1,2,5,3,4,null,6]
输出:[1,null,2,null,3,null,4,null,5,null,6]

示例 2:
输入:root = []
输出:[]

示例 3:
输入:root = [0]
输出:[0]

提示:
树中结点数在范围 [0, 2000] 内
-100 <= Node.val <= 100

进阶:你可以使用原地算法(O(1) 额外空间)展开这棵树吗?

解题思路

后序遍历比较好,先处理子树,再对父节点有同样的操作。

代码实现

方法一:

class Solution {
    public void flatten(TreeNode root) {
        if (root == null) {
            return;
        }
        // 后序遍历比较好
        flatten(root.left);
        flatten(root.right);
        TreeNode temp = root.right;

        // 左子树变为右子树
        root.right = root.left;
        root.left = null;

        // 右子树接到左子树最右下角的右孩子
        TreeNode right = root;
        while (right.right != null) {
            right = right.right;
        }
        right.right = temp;
    }
}

题目二描述

430. 扁平化多级双向链表
你会得到一个双链表,其中包含的节点有一个下一个指针、一个前一个指针和一个额外的 子指针 。这个子指针可能指向一个单独的双向链表,也包含这些特殊的节点。这些子列表可以有一个或多个自己的子列表,以此类推,以生成如下面的示例所示的 多层数据结构 。

给定链表的头节点 head ,将链表 扁平化 ,以便所有节点都出现在单层双链表中。让 curr 是一个带有子列表的节点。子列表中的节点应该出现在扁平化列表中的 curr 之后 和 curr.next 之前 。

返回 扁平列表的 head 。列表中的节点必须将其 所有 子指针设置为 null 。

示例 1:


示例1

输入:head = [1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12]
输出:[1,2,3,7,8,11,12,9,10,4,5,6]
解释:输入的多级列表如上图所示。
扁平化后的链表如下图:


结果1

示例 2:


示例2

输入:head = [1,2,null,3]
输出:[1,3,2]
解释:输入的多级列表如上图所示。
扁平化后的链表如下图:


结果2

示例 3:
输入:head = []
输出:[]
说明:输入中可能存在空列表。

提示:
节点数目不超过 1000
1 <= Node.val <= 10^5

解题思路

类似的思路,可以看作一种二叉树,从子树开始展开,最后到全部。

代码实现

方法一:

class Solution {
    public Node flatten(Node head) {
        return dfs(head);
    }

    public Node dfs(Node head) {
        if (head == null)
            return null;

        // 后序遍历
        dfs(head.child);
        dfs(head.next);

        // 如果当前结点有child,进行操作
        if (head.child != null) {

            // 取出当前的next,后面要接到新的next最后
            Node tempNext = head.next;

            // 本结点的child连接到原来next的位置
            head.child.prev = head;
            head.next = head.child;
            head.child = null;

            Node temp = head;
            while (temp.next != null) {
                temp = temp.next;
            }
            // 原来的next连接到现在next的末尾
            temp.next = tempNext;
            tempNext.prev = temp;
        }
        return head;
    }
}

题目三描述

206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

示例一

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

示例 2:

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

示例 3:

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

提示:

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

进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

解题思路

这是较为通用的一个模板,可以实现反转某个区间里的链表。
一定要背下来循环里的操作,每次先找next再反转,再移动。

代码实现

方法一:

class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null)
            return null;
        ListNode dummyHead = new ListNode(0, head); // 虚拟头结点
        ListNode ppre = dummyHead;  // 找到要反转的结点的前一个,记录,最后要连接尾部结点
        ListNode pre = dummyHead;   // 找到要反转的结点的前一个,这个是要遍历用的
        ListNode cur = head;        // 这是要反转的结点

        while (cur != null) {
            ListNode nex = cur.next;    // 1,要反转的结点的下一个
            cur.next = pre;             // 2,反转结点
            pre = cur;                  // 3,pre移动到cur
            cur = nex;                  // 4,cur移动到nex
        }
        ppre.next.next = cur;           // 反转后的链表的尾结点连接到原来的后序结点上
        ppre.next = pre;                // ppre连接到反转之后链表的头结点

        return dummyHead.next;
    }
}

题目四描述

92. 反转链表 II

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

示例 1:


示例1

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

示例 2:
输入:head = [5], left = 1, right = 1
输出:[5]

提示:

链表中节点数目为 n
1 <= n <= 500
-500 <= Node.val <= 500
1 <= left <= right <= n

进阶: 你可以使用一趟扫描完成反转吗?

解题思路

先找到要反转的链表的前一个,再对这一段反转即可,也是模版题,注意反转次数。

代码实现

方法一:

class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        if (head == null)
            return head;
        ListNode dummyHead = new ListNode(-1, head);
        ListNode pre = dummyHead;
        for (int i = 0; i < left - 1; i++) {
            pre = pre.next;
        }
        ListNode ppre = pre;
        ListNode cur = pre.next;
        // 注意反转次数,这里是从第一个结点反转的,所以操作次数要加一
        for (int i = 0; i < right - left + 1; i++) {
            ListNode nex = cur.next;
            cur.next = pre;
            pre = cur;
            cur = nex;
        }
        ppre.next.next = cur;
        ppre.next = pre;
        return dummyHead.next;
    }
}

题目五描述

234. 回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为 回文链表 如果是,返回 true ;否则,返回 false 。

示例 1:


示例1

输入:head = [1,2,2,1]
输出:true

示例 2:


示例2

输入:head = [1,2]
输出:false

提示:

链表中节点数目在范围[1, 105] 内
0 <= Node.val <= 9

进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

解题思路

可以转成数组然后双指针判断
也可以先找到链表中点,再反转链表后半部分,再两两对比判断。

代码实现

方法一:

class Solution {
    public boolean isPalindrome(ListNode head) {
        // 这里是排除只有一个结点的情况,以免以后有空指针的错误
        if (head.next == null)
            return true;
        ListNode dummyHead = new ListNode(-1, head);

        // 找到链表中点
        ListNode slow = dummyHead;
        ListNode fast = dummyHead;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        // 反转后部分
        ListNode ppre = slow;
        ListNode pre = ppre;
        ListNode cur = pre.next;
        while (cur != null) {
            ListNode nex = cur.next;
            cur.next = pre;
            pre = cur;
            cur = nex;
        }
        if (ppre.next != null) {
            ppre.next.next = cur;
            ppre.next = pre;
        }
        // 从两侧向内比对
        ListNode left = dummyHead.next;
        ListNode right = pre;
        while (right != null) {
            if (left.val != right.val) {
                return false;
            }
            left = left.next;
            right = right.next;
        }
        return true;
    }
}

题目六描述

25. K 个一组翻转链表

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

示例 1:


示例1

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

示例 2:


示例2

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

提示:
链表中的节点数目为 n
1 <= k <= n <= 5000
0 <= Node.val <= 1000

进阶:你可以设计一个只用 O(1) 额外内存空间的算法解决此问题吗?

解题思路

反转链表的重点是找到要反转的结点的前一个结点,所以如果这个结点有变动,就需要暂存并更新了。

代码实现

方法一:

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode dummyHead = new ListNode(-1, head);

        // 计算总结点数,进而计算反转几组
        int n = 0;
        ListNode temp = dummyHead.next;
        while (temp != null) {
            temp = temp.next;
            n++;
        }

        ListNode ppre = dummyHead;
        ListNode pre = dummyHead;
        ListNode cur = pre.next;

        for (int group = 0; group < n / k; group++) {
            for (int i = 0; i < k; i++) {
                ListNode nex = cur.next;
                cur.next = pre;
                pre = cur;
                cur = nex;
            }
            ListNode nextPPRE = ppre.next; // 每次要暂存下一组的上一个结点
            ppre.next.next = cur;
            ppre.next = pre;
            ppre = nextPPRE;
        }
        return dummyHead.next;
    }
}

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容