部分链表的反转

部分链表的反转

问题重述:

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

示例 1:

img

输入: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

问题分析:

题目要求对链表内部的一部分进行反转,看到这个我们最容易想到的是找到反转链表的前一个结点和反转链表的后一个结点(我们寻找这两个结点需要知道链表的长度),将反转链表反转后,将反转链表和保存好的两个结点进行连接,但是这种方法需要注意的是当反转的结点包括头节点的时候,此时链表的头不再是原来的head结点,而是反转链表的最后一个结点(这个结点一般在循环结束时候指向该节点)

还有一种解法就是添加一个dummy结点(该节点方便对头节点进行操作),然后经过一次遍历

解法:

解题:

代码:
解法一:
	
public static Node reverseSomeLinkedList(Node head,int from,int to) {
		int len = 0;
        Node node1 = head;
        Node fPre = null;
        Node tNext = null;
        // 在获取长度的过程中获取反转部分的前一个结点和后一个结点
        while(node1 != null){
            len++;
            fPre = (len == from - 1 ? node1 : fPre);
            tNext = (len == to + 1 ? node1 : tNext);
            node1 = node1.next;
        }
        // 计算链表的长度,当要反转的部分不属于链表内时,直接返回链表头结点
        if(from < 1 || to > len || from > to){
            return head;
        }
        // 和全部反转不同的是要将反转的部分的头和尾连到不反转的位置
        // 这一步是为了包含反转部分为头节点
        node1 = (fPre == null ? head : fPre.next);
        Node node2 = node1.next;
        node1.next = tNext;
        Node next = null;
        while(node2 != tNext){
            next = node2.next;
            node2.next = node1;
            node1 = node2;
            node2 = next;
        }
        if(fPre != null){
        	fPre.next = node1;
            return head;
        }
        return node1;
	}
解法2:
 public ListNode reverseBetween(ListNode head, int left, int right) {
        // 设置 dummyNode 是这一类问题的一般做法
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;
        ListNode pre = dummyNode;
        for (int i = 0; i < left - 1; i++) {
            pre = pre.next;
        }
        ListNode cur = pre.next;
        ListNode next;
        for (int i = 0; i < right - left; i++) {
            next = cur.next;
            cur.next = next.next;
            next.next = pre.next;
            pre.next = next;
        }
        return dummyNode.next;
    }
解法2的另一种写法:
	public ListNode reverseBetween(ListNode head, int m, int n) {
        // 定义一个dummyHead, 方便处理
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;

        // 初始化指针
        ListNode g = dummyHead;
        ListNode p = dummyHead.next;

        // 将指针移到相应的位置
        for(int step = 0; step < m - 1; step++) {
            g = g.next;
            p = p.next;
        }

        // 头插法插入节点
        for (int i = 0; i < n - m; i++) {
            ListNode removed = p.next;
            p.next = p.next.next;

            removed.next = g.next;
            g.next = removed;
        }

        return dummyHead.next;
    }
代码解析:
解法一:

解法1的思路是先遍历一遍链表,得到链表的总长度,找到要反转的链表部分,然后将链表的反转部分反转,反转完成后将这一部分链表连接到原来的链表中去。

首先定义两个空结点,用于保存要反转的链表的头尾(头节点的前一个结点和尾结点的后一个结点),然后遍历链表,给两个结点赋值,并且获得链表的长度,用链表长度确定输入参数是否正确,然后就开始第二次遍历,我们上面的代码有一个问题,那就是当头节点被转换的时候头节点的前一个结点为空,我们需要进行特殊处理,因此我们创建一个结点用于保存反转链表的头节点,对反转链表的头节点进行处理,当该节点为原链表的头节点的时候,该节点就为头节点,如果不为头节点,就将该节点定义为反转链表的第一个结点。保存该节点的下一个结点,将该头节点直接连接到反转链表的后一个结点上去,然后用保存好的下一个结点对链表进行反转(循环条件是不等于反转链表的下一个链表)。最后反转完成以后,将链表连接到原来的链表上

解法二:

思路是一开始找到要反转的链表位置,将起始位置以及起始位置的下一个位置确定,每一次将后面的结点插入到两个结点中间,最后返回头节点就可以。

先创建一个dummy结点用于简化操作,使用一个for循环定位开始结点,然后开始第二次循环,第二次循环,每一次将开始结点的下一个结点的下一个结点插入到两个结点中间。只需要注意不要造成环状结构就可以。

总结:

在对链表的操作中,使用dummy结点在大多数情况下都可以起到简化操作的作用。

posted @   foldn  阅读(735)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示