4.<tag-链表的反转>lt.206-反转链表 + lt.92-反转链表 II 2

lt.206-反转链表

[案例需求]
在这里插入图片描述

1, 头插法;
2. 栈;
3. 原地修改
4. 迭代法

[迭代法第一种, 头插法]

  • 绝对的面试高频题型, 需要同时掌握迭代法和递归法
  1. 迭代法, 遍历原链表, 把遍历到的每个节点使用头插法插入到新的链表头结点的后面.

[代码实现]

class Solution {
    public ListNode reverseList(ListNode head) {
        //迭代法, 遍历链表, 把遍历到的结点以头插法的方式插入到一个新链表的头结点中
        //遍历原有链表为空时, 返回新链表的头结点即可
        ListNode dummyNode = new ListNode(-1);
        ListNode temp = head;
        ListNode temp_cur = temp;

        while(true){
            if(temp == null)break;

            temp_cur = temp.next; // temp指代的某个节点要被插入到新链表中了, 我们需要使用一个变量存储此时temp的下一个结点
            temp.next = dummyNode.next;
            dummyNode.next = temp;

            temp = temp_cur; //temp指针回到原链表中, 并指向原来指向结点的下一个结点
        }

        return dummyNode.next;
    }
}

[迭代法第二种, 原地反转]

在这里插入图片描述
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        //链表原地反转
        ListNode curr = head; //当前结点
        ListNode prev = null; //当前节点的前一个结点

        //目的: 将  1->2->3->4->5 变为
        // 1 <- 2 <- 3 <- 4 <- 5 <-prev , 返回prev就够了
        while(curr != null){
            ListNode next = curr.next; //保存当前节点的下一个节点
            curr.next = prev; // 当前节点链表反转
        
            prev = curr; //后移, 继续指示着下一个curr的前一个结点
            curr = next; //当前节点后移

        }

        return prev; 
    }
}

[迭代法第三种, 栈]

//2. 栈
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        //1. 遍历, 头插法
        //2. 节点放入栈中
        //3. 翻转两个节点之间的next域
        //4. 递归
        Deque<ListNode> stack = new LinkedList<>();
        ListNode temp = head;

        while(true){
            if(temp == null)break;

            stack.push(temp); // 还没断开

            temp = temp.next;
        }

        //取出, 连接;
        ListNode dummy = new ListNode(-1);
        ListNode cur = dummy;

        while(!stack.isEmpty()){
            cur.next = stack.pop();
            cur = cur.next;
        }
        cur.next = null; //断开head的next域
        return dummy.next;
    }
}

[递归法第一种, 递归法]

  1. 递归法,
  1. 确定函数的参数列表和功能, 参数是原有链表中的每一个节点, 返回值是
  2. 确定递归出口, 遇到结点为空时, 退出递归
  3. 确定单层递归逻辑,

待补充递归写法
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

class Solution {
   
    public ListNode reverseList(ListNode head) {
        //反转单链表, 递归法, 头插法

        if(head == null || head.next == null)return head; //递归出口
    	
    	//他上面是递进区
        ListNode newHead = reverseList(head.next);
       	//他下面是递归返回区
       	
       //单层递归逻辑
        head.next.next = head;//反转
        head.next = null; //断开原有链接
        
        return newHead;
    }
}

lt.92-反转链表 II

[案例需求]
在这里插入图片描述

[思路分析一, 模拟]

思路:切断left到right的子链,然后反转,最后再反向连接

复杂度:时间复杂度O(n),空间复杂度O(1)
pre ---->rightNodeleftNode—>post
转为: pre---->leftNode
rightNode---->post

[代码实现]

class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        //把链表要反转的部分断开,反转后再接回来

        //1. 遍历找到位置
        // pre 最终位置为 leftNode的前一个节点
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;
        ListNode pre = dummyNode; /
        //  pre --> leftNode(head1) --> rightNode --> rear
        for(int i = 0; i < left - 1; i++){
            pre = pre.next;
        }
        
        //2. 赋值
        ListNode rightNode = pre;
   

        //3. 遍历查找
        // rightNode 最终位置为待反转序列的最后一个节点
        for(int i = 0; i < right - left + 1; i++){
            rightNode = rightNode.next;
        }

        //4.保留左右位置
        ListNode leftNode = pre.next;
        ListNode rear = rightNode.next;

        //5. 断开链表
        pre.next = null;
        rightNode.next = null;

        //6. 反转链表
        reverseList(leftNode);

        //7. 接回去
        pre.next = rightNode;
        leftNode.next = rear;


        return dummyNode.next;
    }


    //利用头插法反转链表
    public void reverseList(ListNode head){
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;

        ListNode temp = head;

        while(temp != null){
            ListNode cur = temp.next;
            temp.next = dummyNode.next;
            dummyNode.next = temp;
            temp = cur;
        }
    }

    改进做法, 找到left的前一个位置后, 用 pre指针标记
    //然后把left-->right的元素使用头插法插入到pre后面, 并继续往后遍历, 
    //这样只需要一次遍历, 就可以反转链表   
}

补充注释

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        //遍历到left前一个节点, 断开与left的链接,
        //单独对left->right进行翻转

        // pre-->rightNode ---> leftNode ---> post
        // pre--->leftNOde ---> rightNode--->post
        ListNode dummy = new ListNode(-1);
        dummy.next = head;

        //用cur遍历链表,记录反转链表的前一个节点pre, 
        //pre后面的第一个节点是rightNode, 作为被翻转链表的末尾节点
        //用cur继续遍历right-left次, 来到被反转链表的最后一个节点leftNode;
        //leftNode后面的第一个节点是post, 代表着反转链表后面的节点
        ListNode cur = dummy; 

        for(int i = 0; i < left - 1; i++){
            cur = cur.next; 
        }

        //pre->rightNode
        ListNode pre = cur;
        ListNode rightNode = cur.next;
        pre.next = null; //断开pre

        //重新建立temp, 遍历到反转链表段的最后一个节点
        ListNode temp = rightNode;

        //temp遍历了right-left次, 来到被反转链表段的最后一个节点, 我们叫leftNode
        for(int i = 0; i < right - left; i++){
            temp = temp.next;
        }

        //post是反转链表段后面的一个节点
        ListNode post = temp.next;
        temp.next = null;//断开反转链表段的右侧连接

        //rightNode----leftNode  链表段的反转
        ListNode leftNode = reverse(rightNode);

        pre.next = leftNode; // pre--->leftNode=====rightNode--->post
        rightNode.next = post;

        return dummy.next;
    }

    public ListNode reverse(ListNode head){
        ListNode pre = null;
        ListNode temp = head;

        while(true){
            if(temp == null)return pre;

            ListNode temp_next = temp.next;
            temp.next = pre;

            pre = temp;
            temp = temp_next;
        }
    }
}

[思路分析二, 结合头插法]

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

[代码实现]

class Solution {
    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;
    }
}
反转链表中从位置 m 到 n 的部分,可以按以下步骤进行操作: 首先,找到第 m 个节点和第 n 个节点的位置。 然后,我们将从第 m 个节点到第 n 个节点的部分链表进行反转操作。 最后,将反转后的部分链表重新连接到原链表中。 下面是具体的算法实现: 1. 声明一个 dummy 节点,并将其 next 指针指向头节点。同时声明 pre 指针指向 dummy 节点。 2. 将 pre 指针移动到第 m - 1 个节点的位置。 3. 声明一个 cur 指针指向 pre 指针的下一个节点。 4. 声明一个 tail 指针指向 cur 指针的下一个节点。 5. 从 m 到 n 的位置进行反转操作,需要进行 n - m 次迭代。 1. 将 cur 指针的 next 指针指向 tail 指针。 2. 将 pre 指针的 next 指针指向 cur 指针。 3. 将 cur 指针移动到 tail 指针的位置。 4. 将 tail 指针指向 cur 指针的下一个节点。 6.反转后的部分链表重新连接到原链表中,即将第 m - 1 个节点的 next 指针指向第 n 个节点。 7. 返回 dummy 节点的 next 指针。 以下是具体的实现代码: ```python def reverseBetween(head, m, n): dummy = ListNode(0) dummy.next = head pre = dummy # 得到第 m - 1 个节点 for _ in range(m - 1): pre = pre.next cur = pre.next tail = cur.next # 反转从第 m 个节点到第 n 个节点的部分链表 for _ in range(n - m): cur.next = tail.next tail.next = pre.next pre.next = tail tail = cur.next return dummy.next ``` 该算法的时间复杂度为 O(n - m),其中 n 是链表的长度。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值