day4任务

1.两两交换链表中的节点:代码随想录

力扣链接:. - 力扣(LeetCode)

这个题按照链表节点个数分为两种情况:

偶数个:两两交换没有剩余

奇数个:最后一个不交换

模拟一下交换过程就行了,具体看代码随想录链接中的思路部分。

贴一下代码,自己写了不少注释方便理解和复习。

package LuStudy;
/*
 *  两两交换链表中的节点
 * 
 *  https://leetcode.cn/problems/swap-nodes-in-pairs/
 * 
 *  问题理解:链表中节点为偶数则都需要两两交换,奇数个则不交换最后一个
 *  情况分类:链表空、链表一个节点、链表奇数个节点(节点数>=1)链表偶数个节点
 * 
 *  此题代码随想录有三个解法:递归解法、模拟交换、模拟交换简洁版
 */

/*
 * 自己定义下Listnode
 */
class ListNode {
    int val;
    ListNode next;

    // 无参构造方法
    public ListNode() {
    }

    public ListNode(int val) {
        this.val = val;
    }

    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

public class _4_1TwoTwoExchageLinkListNode { // 模拟交换法
    // 建议先在本子上自己模拟下过程
    private static ListNode swapPairs(ListNode head) { // 传参为头节点
        // 循环外定义2个变量:虚拟头节点、控制循环的cur
        // 用一个链表的例子: 1 -> 2 -> 3 -> 4
        ListNode dumyHead = new ListNode(-1, head);
        ListNode cur = dumyHead;
        while (cur.next != null && cur.next.next != null) {
            ListNode node1 = cur.next; // 节点1
            ListNode node2 = cur.next.next.next; // 节点3这个也能在下面定义,但是像这样好理解
            // 先让虚拟头节点连上第二个节点
            // 目前虚拟头节点指向2节点,1也指向2,下一步要改变1指向2的链接,改为2指向1
            // 目前cur.nex指向2了已经,即 cur.next -> 2;cur -> dumyHead
            cur.next = cur.next.next;
            // 将2的线指向1,再将1的线指向3
            cur.next.next = node1;
            node1.next = node2;
            // 这两个节点交换结束,关注cur的指向,敢这么移动是因为在while里有条件可以移动
            cur = cur.next.next;
        }

        return dumyHead.next; // 返回的是头节点
        /*
         * cur.next = cur.next.next;
         * cur.next.next = node1;
         * node1.next = node2;
         * 
         * 实际上这三条语句可以翻译一下,以第一次交换为例,cur第一次指向dumyHead
         * cur的next是2,2=cur.next.next
         * 2的next是1(原来第一个节点),1=node1,node1存储过了
         * 1的next是3(原来第三个节点),3=node2,node2存储过了,其实这边叫node3比较形象
         */
    }

    public static void main(String[] args) {
        // 初始化链表
        ListNode head = new ListNode(1);
        // 添加元素
        head.next = new ListNode(2);
        head.next.next = new ListNode(3);
        head.next.next.next = new ListNode(4);
        head.next.next.next.next = new ListNode(5);
        head.next.next.next.next.next = new ListNode(6);
        head.next.next.next.next.next.next = new ListNode(7);

        // 测试两两交换操作
        head = swapPairs(head);
        ListNode cur = head;
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
    }
}

虚拟头节点出现场景:有增删的地方,定义这个头节点就可以统一操作,不用按情况分类,即考虑头节点的情况。

头节点定义这边,个人这样理解:

ListNode dumyHead = new ListNode(-1, head);

里面的-1不重要,注意后面的head,是上面的函数  ListNode swapPairs(ListNode head)  传过来的,传过来了链表的头节点。

看ListNode的构造函数:

public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }

这里就是  this.next = head;即这个dumyHead.next = head;、

完成虚拟头节点的定义。


定义的cur指针指向虚拟头节点dumyHead,定义好虚拟头节点后,整个流程其实就三步(链表例子:1->2->3->...)

1.让cur.next指向2节点,所以要写cur.next = cur.next.next;

2.让2节点指向1节点,所以要写cur.next.next = node1;这个node1就是为了备份1不让1走丢的

3.让1节点指向3节点,所以要写node1.next = node2;node2就是为了指明1的方向,让1知道下一个是谁。

最后再移动下cur,cur永远指向要调换的两个节点的第一个节点的前一个。

return下面的注释:加强理解

可以结合代码随想录网站的思路、代码中的注释、return下面的注释一起理解,应该很快就知道什么意思了。main中是为了本地测试随便写的例子。


2.删除链表的倒数第N个节点:代码随想录 (programmercarl.com)

力扣链接:. - 力扣(LeetCode)

一定要注意是倒数第N个,注意看题,别傻呵呵写一堆发现是解决第N个节点,这跟设计链表那道题就没啥区别了。

这个题有点绕,双指针写法确实比较巧妙,建议多看几遍代码随想录的视频和文章。双指针的细节写在注释。快指针先行的原因来自代码随想录B站视频教程评论区。

package LuStudy;
/*
 *  删除链表的倒数第N个节点
 * 
 *  https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
 */

//先定义一个链表结构
class ListNode {
    int val;
    ListNode next;

    public ListNode() {
    };

    public ListNode(int val) {
        this.val = val;
    }

    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

public class _4_2DeleteLastNLinkListNodeByDoublePoint { // 双指针
    // 定义删除链表的倒数第N个节点作用的函数
    /*
     * 为啥要让快指针先行?(摘抄评论区:https://www.bilibili.com/video/BV1vW4y1U7Gf/?vd_source=
     * ab65c9e0700638016fc5e2fafb93d68e)
     * 我认为更好懂的一种解释:快指针先行n步,这样快慢指针之间形成了一段长度为n的窗口,之后快慢指针同步向前相当于保持窗口长度不变。
     * 这样当快指针到达了末尾指向NULL,另一端的慢指针距离末尾的长度是n,自然就是指向倒数第n个位置了。
     */
    private static ListNode deleteLastNNode(ListNode head, int target) {
        ListNode dummyHead = new ListNode(0, head);
        ListNode fastNode = dummyHead;
        ListNode slowNode = dummyHead;
        for (int i = 0; i <= target; i++) {
            fastNode = fastNode.next;
        }
        while (fastNode != null) {
            fastNode = fastNode.next;
            slowNode = slowNode.next;
        }
        if (slowNode.next != null) {
            slowNode.next = slowNode.next.next;
        }
        return dummyHead.next;
    }

    public static void main(String[] args) {
        // 初始化链表
        ListNode head = new ListNode(1);
        // 添加元素
        head.next = new ListNode(2);
        head.next.next = new ListNode(3);
        head.next.next.next = new ListNode(4);
        head.next.next.next.next = new ListNode(5);
        head.next.next.next.next.next = new ListNode(6);
        head.next.next.next.next.next.next = new ListNode(7);
        head = deleteLastNNode(head, 1);
        ListNode cur = head;
        if (cur == null) {
            System.out.print("[]");

        }
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
    }
}

3.链表相交:代码随想录 (programmercarl.com)

力扣链接:. - 力扣(LeetCode)

一定注意:值相同的不一定是同一个节点,其实这个题就是求地址相同的节点。

这块困扰好久,发现是被例子误导了,一定注意是地址相同,所以最好别管值是啥。

建议多看文章、多看这个题的力扣评论区,你会发现这题写的真是太优雅了。反正我想不出来。

两个版本,一个代码多点,来自随想录的思路;另一个是优雅解法,多看力扣讨论区。思路有点巧妙,今天没空记录了,先粘一下代码。有空再review。

版本1:

package LuStudy;

/*
 *  链表相交
 * 
 *  https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/
 */

class ListNode {
    int val;
    ListNode next;

    ListNode(int val) {
        this.val = val;
        next = null;
    }
}

public class _4_3IntersectionLinkedList {
    private ListNode getIntersectionNode(ListNode headA, ListNode headB) {// (版本一)先行移动长链表实现同步移动
        ListNode curA = headA;
        ListNode curB = headB;
        int lenA = 0, lenB = 0;
        while (curA != null) {
            curA = curA.next;
            lenA++;
        }
        while (curB != null) {
            curB = curB.next;
            lenB++;
        }
        curA = headA;
        curB = headB;
        // 让curA为最长链表的头,lenA为其长度
        if (lenA < lenB) {
            int tempLen = lenA;
            lenA = lenB;
            lenB = tempLen;

            ListNode tempNode = curB;
            curA = curB;
            curB = tempNode;
        }
        // 求长度差
        int gap = lenA - lenB;
        while (gap-- > 0) {
            curA = curA.next;
        }
        while (curA != null) {
            if (curA == curB) {
                return curA;
            }
            curA = curA.next;
            curB = curB.next;
        }
        return null;
    }

    public static void main(String[] args) {
        // 创建链表A: 1 -> 2 -> 3 -> 4 -> 5
        ListNode headA = new ListNode(1);
        // 添加元素
        headA.next = new ListNode(2);
        headA.next.next = new ListNode(3);
        headA.next.next.next = new ListNode(4);
        headA.next.next.next.next = new ListNode(5);

        // 创建链表B: 3 -> 4 -> 5
        ListNode headB = new ListNode(3);
        // 添加元素
        headB.next = new ListNode(4);
        headB.next.next = new ListNode(5);
        // 假设链表 B 从1节点val=3开始与链表 A 相交,为了测试函数能不能找到,这样才可打印出结果
        ListNode bIntersection = headB;
        bIntersection.next = headA.next.next;

        _4_3IntersectionLinkedList listNode = new _4_3IntersectionLinkedList();
        ListNode res = listNode.getIntersectionNode(headA, headB);
        if (res != null) {
            System.out.println(res.val);
        } else {
            System.out.println("No intersection node found.");
        }
    }
}

版本2(优雅写法):

package LuStudy;
/*
 *  链表相交
 * 
 *  https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/
 */

class ListNode {
    int val;
    ListNode next;

    ListNode(int val) {
        this.val = val;
        next = null;
    }
}

public class _4_3IntersectionLinkedListByDoublePoint {
    private ListNode getIntersectionNodeByDP(ListNode headA, ListNode headB) {
        ListNode curA = headA;
        ListNode curB = headB;
        while (curA != curB) {
            if (curA != null) {
                curA = curA.next;
            } else {
                curA = headB;
            }

            if (curB != null) {
                curB = curB.next;
            } else {
                curB = headA;
            }
        }
        return curA;
    }

    public static void main(String[] args) {
        // 创建链表A: 1 -> 2 -> 3 -> 4 -> 5
        ListNode headA = new ListNode(1);
        // 添加元素
        headA.next = new ListNode(2);
        headA.next.next = new ListNode(3);
        headA.next.next.next = new ListNode(4);
        headA.next.next.next.next = new ListNode(5);

        // 创建链表B: 3 -> 4 -> 5
        ListNode headB = new ListNode(3);
        // 添加元素
        headB.next = new ListNode(4);
        headB.next.next = new ListNode(5);
        // 假设链表 B 从1节点val=3开始与链表 A 相交,为了测试函数能不能找到
        ListNode bIntersection = headB;
        bIntersection.next = headA.next.next;

        _4_3IntersectionLinkedListByDoublePoint listNode = new _4_3IntersectionLinkedListByDoublePoint();
        ListNode res = listNode.getIntersectionNodeByDP(headA, headB);
        if (res != null) {
            System.out.println(res.val);
        } else {
            System.out.println("No intersection node found.");
        }
    }
}

版本3(超优雅写法):力扣评论区排名前几个,都是大神

4.环形链表:代码随想录 (programmercarl.com)

力扣链接:. - 力扣(LeetCode)

没看懂,mark一下。贴个自己的代码(哈哈哈笑话下自己)

package LuStudy;

/*
 *  环形链表II
 * 
 *  https://leetcode.cn/problems/linked-list-cycle-ii/
 */
public class _4_4CircularLinkedList {
    public static void main(String[] args) {

    }
}

  • 22
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值