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

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;
    }
}
posted @   青松城  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示