算法通关村 | 第二关 | 白银篇(反转拓展)

废话不多说,直接实战!

算法通关村 | 第二关 | 白银篇(反转拓展)_第1张图片


指定区间反转

题目链接:92. 反转链表 II - 力扣(LeetCode)

 题目介绍

算法通关村 | 第二关 | 白银篇(反转拓展)_第2张图片

题目解法 

本题有两种主要的解法,一是头插法,二是穿针引线法, 先说一种之前学过的解法,头插法。

头插法 

思路:

  1. 创建一个虚拟头节点 dummy,将它的 next 指针指向链表的头节点 head

  2. 定义一个指针 pre,通过循环将它移动到反转部分的前一个节点。循环的次数是 left - 1,这样 pre 就指向了反转部分的前一个节点。

  3. 定义一个指针 cur,初始时指向 pre.next,即反转部分的第一个节点。

  4. 使用循环,循环的次数是 right - left,这样 cur 就指向了反转部分的最后一个节点。

  5. 在循环中,我们每次都将 cur.next 指针指向下一个节点,然后将 cur 插入到 pre.next 的位置,从而实现了反转。

  6. 最后,返回虚拟头节点 dummynext 指针,即为反转后的链表头节点。

 具体如图:

算法通关村 | 第二关 | 白银篇(反转拓展)_第3张图片

具体的代码如下:

class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode pre = dummy;
        for(int i = 0;i < left - 1;i++){
            pre = pre.next;
        }

        ListNode cur = pre.next;
        ListNode tmp;
        for(int i = 0;i < right - left;i++){
            tmp = cur.next;
            cur.next = tmp.next;
            tmp.next = pre.next;
            pre.next = tmp;
        }
        return dummy.next;
    }
}

看图慢慢理解就行了(doge) 

 穿针引线法

        这种方式听起来很难,但它的确很难(doge),只不过难的是理解。下面我们就来学习一下这个方法。

        所谓的穿针引线法,其实就是先找到要反转的区域,也就是 left 到 right 的这个区域,这时看原链表,感觉就像被分成了 3 个部分,这时我们把链表如穿线一样穿起来,就把 要反转区域内的中间链表“倒了过来”,之后的步骤就和平常的链表反转一样了(等于是先把中间部分剪掉进行反转,然后把反转后的与之前的串在一起),具体细节如下图所示:

算法通关村 | 第二关 | 白银篇(反转拓展)_第4张图片

 (图中只是我的理解,有错误欢迎大佬们指正,感谢!)

具体代码如下:

public ListNode reverseBetween(ListNode head, int left, int right) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode pre = dummy;

        // 从虚拟头节点走 left - 1 步,来到反转部分的前一个节点
        for (int i = 0; i < left - 1; i++) {
            pre = pre.next;
        }

        ListNode rightNode = pre;

        // 从 pre 再走 right - left + 1 步,来到反转部分的节点
        for (int i = 0; i < right - left + 1; i++) {
            rightNode = rightNode.next;
        }

        // 切出一个子链表
        ListNode leftNode = pre.next;
        ListNode succ = rightNode.next;
        pre.next = null;
        rightNode.next = null;

        // 反转子链表
        reverseLinkedList(leftNode);

        // 将反转后的子链表接回到原链表中
        pre.next = rightNode;
        leftNode.next = succ;

        return dummy.next;
    }
    private void reverseLinkedList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        while (cur != null) {
            ListNode tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }
    }

两两交换链表中的节点(重要)

题目介绍

题目链接:24. 两两交换链表中的节点 - 力扣(LeetCode)

题目详情:

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

算法通关村 | 第二关 | 白银篇(反转拓展)_第5张图片

解法  

        对于本题而言,使用虚拟头结点是一个非常正确的选择,避免了处理头结点的优点在这道题目中体现的格外突出(自我感觉~~)

大致的思路如图所示:

算法通关村 | 第二关 | 白银篇(反转拓展)_第6张图片

 具体代码如下:

public ListNode swapPairs(ListNode head) {
        ListNode dummy= new ListNode(-1);
        dummy.next = head;
        ListNode cur = dummy;
        while(cur.next != null && cur.next.next != null){
           ListNode node1 = cur.next;
           ListNode node2 = cur.next.next;
           cur.next = node2;
           node1.next = node2.next;
           node2.next = node1;
           cur = node1;
        }
        return dummy.next;
    }

单链表+1

题目介绍

给单链表加一”的问题,即在表示非负整数的单链表上进行加一操作。题目描述如下:

给定一个非负整数,用一个非空单链表来表示。将这个整数加一。

你可以假设这个整数除了 0 本身,没有任何前导的 0。

解法1(栈) 

        这道题用栈可以很好的解决问题,因为加 1 操作是从低位开始的,直接操作链表的话需要反转,如果使用栈,第一个pop出的就是低位的,很方便的就可以进行加操作,具体实现如下:

算法通关村 | 第二关 | 白银篇(反转拓展)_第7张图片

 具体代码:

public ListNode plusOne (ListNode head) {
        // write code here
        Stack st = new Stack<>();
        ListNode cur = head;
        // 将链表节点{}逐个压入栈中
        while (cur != null) {
            st.push(cur);
            cur = cur.next;
        }
        // 从栈中依次弹出节点,对链表进行加一操作
        int carry = 1;
        while (!st.isEmpty() && carry > 0) {
            ListNode node = st.pop();
            int sum = node.val + carry;
            node.val = sum % 10;
            carry = sum / 10;
        }
        // 如果最高位产生了进位,添加一个新的节点
        if (carry > 0) {
            ListNode newHead = new ListNode(carry);
            newHead.next = head;
            return newHead;
        }
        return head;
    }

解法2(链表反转)

public ListNode plusOne (ListNode head) {
        // write code here
         // 第一步:反转原始链表
        ListNode reversedHead = reverseLinkedList(head);

        // 第二步:对反转后的链表进行加一操作
        ListNode current = reversedHead;
        int carry = 1;

        while (current != null && carry > 0) {
            int sum = current.val + carry;
            current.val = sum % 10;
            carry = sum / 10;
            current = current.next;
        }

        // 第三步:如果还有进位,添加一个新节点到链表头部
        if (carry > 0) {
             ListNode newNode = new ListNode(carry);
            if (reversedHead == null) {
                reversedHead = newNode;
            } else {
                ListNode tail = reversedHead;
                while (tail.next != null) {
                    tail = tail.next;
                }
                tail.next = newNode;
            }
        }

        // 第四步:再次反转链表得到最终结果
        return reverseLinkedList(reversedHead);
    }

    // 辅助函数:反转链表
    private ListNode reverseLinkedList(ListNode head) {
        ListNode prev = null;
        ListNode current = head;

        while (current != null) {
            ListNode next = current.next;
            current.next = prev;
            prev = current;
            current = next;
        }

        return prev;
    }

链表加法

题目链接:445. 两数相加 II - 力扣(LeetCode)

题目介绍

算法通关村 | 第二关 | 白银篇(反转拓展)_第8张图片

         这道题就是上面链表加1的拓展,使用栈完全是没问题的,当然也可以使用链表反转,思路和上面都是差不多的,参考加一的操作来拓展即可。所以我们直接看代码吧!

补充 一点hxd们,学习算法本就是一件枯燥的事,因为就是那些知识,不会的该不会还是不会,我们只要用我们会的方法解决就行了。如果遇到写过的类似的还不会,不要气馁,毕竟我们都已经在路上了,坚持走下去,不说能达到什么高度吧,起码比昨天的自己更好!

鸡汤啥大家都该听的耳朵起茧子了,不管咋样,开心就好!

 

解法1(栈) 

         大致的思路就是把两个链表分别压入栈中,然后一起出栈,对结果进行计算取模保存到新的链表中,最后将新链表反转即可,如果不想反转我们也可以在新链表插入结点时选择头插法

这里我们就用头插法来实现!

代码如下:

 

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        Stack st1 = new Stack<>();
        Stack st2 = new Stack<>();
        // 入栈
        while(l1 != null){
            st1.push(l1.val);
            l1 = l1.next;
        }
        while(l2 != null){
            st2.push(l2.val);
            l2 = l2.next;
        }
        ListNode dummy = new ListNode(0);
        int carry = 0;
        // 弹出元素进行相加
        // 当两个栈都空了,若carry=0则退出循环
        while(!st1.isEmpty() || !st2.isEmpty() || carry>0){
            // 有进位用sum进行存储,在下个高位添加
            int sum = carry;
            if(!st1.isEmpty()){
                sum += st1.pop();
            }
            if(!st2.isEmpty()){
                sum += st2.pop();
            }
            // 头插
            ListNode newNode = new ListNode(sum % 10);
            newNode.next = dummy.next;
            dummy.next = newNode;
            carry = sum / 10;
        }
        return dummy.next;
    }

解法2(链表反转)

大致就是先将两个链表反转,计算之后再将其发转一次即可!

代码如下:

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        // 先反转两个链表
        ListNode rs1 = reverseLinkedList(l1);
        ListNode rs2 = reverseLinkedList(l2);
        ListNode dummy = new ListNode(0);
        ListNode cur = dummy;
        int carry = 0;

        while(rs1 != null || rs2 != null ||carry>0){
            int sum = carry;
            if(rs1 != null){
                sum += rs1.val;
                rs1 = rs1.next;
            }
            if(rs2 != null){
                sum += rs2.val;
                rs2 = rs2.next;
            }
            cur.next = new ListNode(sum % 10);
            cur = cur.next;
            carry = sum / 10;
        }
        return reverseLinkedList(dummy.next);
    }

    private ListNode reverseLinkedList(ListNode head){
        ListNode pre = null;
        ListNode cur = head;
        while(cur != null){
            ListNode tmp = cur.next;
            cur.next  = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }

优质又高效!good 

算法通关村 | 第二关 | 白银篇(反转拓展)_第9张图片


 有没有收获hxd们,希望能够帮助到大家,也帮助我自己!努力吧!

算法通关村 | 第二关 | 白银篇(反转拓展)_第10张图片 

你可能感兴趣的:(算法通关村,算法,链表,笔记)