算法练习Day4| 24. 两两交换链表中的节点 19.删除链表的倒数第N个节点 02.07. 链表相交 142.环形链表II

#算法#链表#环形链表

今天依然练习链表的操作,做了几道题之后,感觉数组和链表各有自己的优势吧。当然,站在面试或者说单纯做题的角度,两个也都有各自需要注意的点,数组就是删除的话需要考虑后面元素的向前移动,链表的话就是遍历以及指针移动需要考虑清楚。

24. 两两交换链表中的节点 

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

这道题乍一看像反转链表,但是实际上跟反转链表有区别,即使是对每两个节点的处理上,也是不一样。解决思路:创建虚拟节点dummyNode,其next指向链表的head结点,主要是做以下三步:

1. dummy指向第二个节点

2. 第二个节点指向第一个节点

3. 第一个节点指向第二个的next节点。

这三步循环执行。

如下图(图片来自代码随想录)

执行完这三步,就可以像1和2节点交换位置了:

具体代码:

 public ListNode swapPairs(ListNode head) {
        if(null == head) {
            return head;
        }
        ListNode dummy = new ListNode();
        dummy.next = head;
        ListNode temp = null;
        ListNode cur = dummy;
        ListNode first = head;
        ListNode second = first.next;
        while(null != first && null != first.next) {
            temp = second.next;
            cur.next = second;
            second.next = first;
            first.next = temp;

            cur = first;
            first = temp;
            if(null != first) {
                second = first.next;
            }
            
        }
        return dummy.next;
    }

这里我可能和其他人的略有不同,但是大体逻辑是一样的。

19.删除链表的倒数第N个节点

题目链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

这道题有两种思路。

第一种就是马上可以想到的,先遍历计算链表的总长度size,然后倒数第N个也就是第size - N +1个,这样的话再进行一次遍历,就可以找到对应的节点然后删除。

第二种就是经典的双指针法。通过双指针找到删除的节点,只进行一次遍历。创建两个指针fast和slow,fast先遍历到第N个节点,然后slow从头结点开始和fast一起遍历,fast遍历完整个链表,则slow对应的下个节点是要删除的节点。

具体代码:

public ListNode removeNthFromEnd(ListNode head, int n) {
        if(null == head) {
            return head;
        }
        //解法一: 遍历两次,先拿到size, 倒数第N个就是第 size - N个
        // int size = 0;
        // ListNode count = head;           //注意,这里不能直接用head,否则原链表head就发生变化了
        // while(null != count) {
        //     size++;
        //     count = count.next;
        // }
        // int target = size - n;
        // ListNode dummy = new ListNode();
        // dummy.next = head;
        // ListNode cur = dummy;
        // int i = 0;
        // while(null != cur.next && i < target) {
        //     cur = cur.next;
        //     i++;
        // }
        // if(null != cur && null != cur.next) {
        //     cur.next = cur.next.next;
        // }

        //解法二 双指针法,通过双指针,一次遍历找到删除的位置
        ListNode dummy = new ListNode();
        dummy.next = head;
        ListNode fast = dummy.next;
        int i = 0;
        while(null != fast && i < n) {
            fast = fast.next;
            i++;
        }
        ListNode slow = dummy;
        while(null != fast) {
            fast = fast.next;
            slow = slow.next;
        }
        if(null != slow && null != slow.next) {
            slow.next = slow.next.next;
        }
        
        return dummy.next;
    }

两种解法写在一起了,我是两次都在leetcode上进行了提交,都可以通过,分开的话怕复制粘贴搞得有问题,容易出错。

02.07. 链表相交

题目链接:面试题 02.07. 链表相交 - 力扣(LeetCode)给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

这个我是想了一会,首先就是思考如何确认有相交,因为我们这种是单链表,所以类似数组的查找公共元素,所以我的想法是两个循环,遍历链表,找到两个链表都有的节点,那就是相交节点,返回对应节点即可,如果没有那就是没相交。

具体代码:

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(null == headA || null == headB) {
            return null;
        }
        //两个循环遍历(但是不符合题目要求:你能否设计一个时间复杂度 O(n) 、仅用 O(1) 内存的解决方案?)
        ListNode curA = headA;
        while(null != curA) {
            ListNode curB = headB;           //这里需要注意,每次A链表更新节点时,B链表都要从第一个节点开始
            while(null != curB) {
                if(curA.equals(curB)) {
                    return curA;
                }
                curB = curB.next;
            }
            curA = curA.next;
        }
        return null;
    }

142.环形链表II

题目链接:142. 环形链表 II - 力扣(LeetCode)给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

这道题首先是判断是否有环,有环再去找对应的节点,没有环则直接返回null。

如何判断是否有环,这里用到了快慢指针,fast每次移动两个节点,slow移动一个,遍历之后,如果有环,那么两个指针一定会相遇,如果没有环,那fast会先遍历为null。这个其实是一个固定的结论,看到有些人说这个是小学算术题,我也是虎躯一震,我小学怎么完全不知道这个。

如何找入环的节点,这里需要经过计算,具体的计算经过可以参考代码随想录,讲的比较详细,最终结论:两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口。

具体代码:

public ListNode detectCycle(ListNode head) {
        if(null == head || null == head.next || null == head.next.next) {
            return null;
        }
        //check if there has a cycle
        ListNode fast = head;
        ListNode slow = head;
        while(null != fast && null != fast.next) {
            fast = fast.next.next;
            slow = slow.next;
            if(slow.equals(fast)) {
                //has a cycle
                ListNode index1 = fast;
                ListNode index2 = head;
                //find the target node
                while(!index1.equals(index2)) {
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
        }
        return null;
    }

这道题就是判断是否有环可以多思考下,找入环的第一个节点,我觉得可以学习后记一下结论,每次都算一遍还是比较耗时。

这次的题就全部完成了,下次继续加油!(有疑问的朋友可以留言,相互交流,思想碰撞才能一起成长,加油!)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值