代码随想录算法训练营第4天|LeetCode24,19,02,07,142

24.交换链表结点

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

文章链接:代码随想录 (programmercarl.com)

视频链接:代码随想录算法公开课 | 最强算法公开课 | 代码随想录

第一想法

正常模拟,先画图再说,有点像链表反转?这个模拟完全是错误的,交换第二对时会把第一对的末指针丢掉。

代码随想录想法

有三个比较重要关注的点:

cur的定义一定是要预备交换的一对结点的前一个结点,也就是已经交换完结的一对结点之中的第二个结点。这是理解我们为什么要引入虚拟节点,和循环条件的关键。

交换过程中,每一句代码执行时,哪里指针断了,哪里指针脸上得画图看清楚。不要弄丢前后指针。

while (cur.next!=null&&cur.next.next!=null)这个循环条件要注意前后关系,否则会空指针异常

代码

class Solution2{
    public ListNode swapPairs(ListNode head) {
        ListNode dummyNode = new ListNode(-1, head);
        ListNode cur = dummyNode;
        while (cur.next!=null&&cur.next.next!=null){
            ListNode temp1 = cur.next;
            ListNode temp2 = cur.next.next.next;

            cur.next = cur.next.next;
            cur.next.next = temp1;
            temp1.next = temp2;

            cur = cur.next.next;
        }
        return dummyNode.next;
    }
}

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

题目链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

文档链接:代码随想录 (programmercarl.com)

视频链接:链表遍历学清楚! | LeetCode:19.删除链表倒数第N个节点_哔哩哔哩_bilibili

第一想法

暴力解题法,第一遍遍历链表
第二遍指针初始时,链表到达尾部,往回遍历,当i = n+1时,指针恰好等于要删除结点的前一个结点,删除结点。
为方便删除应当设置链表的虚拟头结点。

暴力写不出来,方法二:

首先设置虚拟头结点,然后设置距离为n+1的双指针。

代码随想录想法

双指针想法一致,算是靠自己写的吧。

问题

相隔n个单位,所以快指针要跳n+1个单位,循环是(for int i =0;i<=n;i++),左闭右闭循环n+1次,左闭右开,循环n次。

end指针移动n+1个单位,此处代码可以优化。参考这里:

while(n--&&fast!=nullptr){
      fast=fast->next;
}    

2.在第一个while循环移动快制作的循环条件中,我认为删除倒数第n个节点,这个n肯定是小于等于链表长度,所以fast移动n次肯定不会指向空指针,这个条件可以去掉,此时群里大佬指出一个新的专业术语“健壮行保证”

代码随想录算法训练营第四天| leetcode 24、19、160、142-CSDN博客

代码

class Solution1 {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummyNode = new ListNode(-1,head);
        ListNode begin = dummyNode;
        ListNode end =dummyNode;
        for (int i = 0; i <= n; i++) {
            end = end.next;
        }
        while(end!=null){
            begin =begin.next;
            end = end.next;
        }
        begin.next = begin.next.next;
        return dummyNode.next;
    }
}

面试题02.07题链表相交

题目链接:面试题 02.07. 链表相交 - 力扣(LeetCode)

文档链接:代码随想录 (programmercarl.com)

第一想法

和19题很类似,同样是长度差。

先统计链表A和链表B的长度,设置相距链表长度差的双指针,二者同时往后走。一旦数值相等,那么就一定是相同的第一个结点。

代码

class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode longList = headA;
        ListNode shortList = headB;
        int longLength = 0;
        int shortLength = 0;
        while (longList!=null){
            longLength++;
            longList = longList.next;
        }
        while (shortList!=null){
            shortLength++;
            shortList = shortList.next;
        }
        longList = headA;
        shortList = headB;
        if(longLength<shortLength){
            ListNode tempNode = longList;
            longList = shortList;
            shortList = tempNode;
            int temp = longLength;
            longLength = shortLength;
            shortLength = temp;
        }
        int gap = longLength-shortLength;
        while(gap-->0)
            longList = longList.next;
        while (longList!=null){
            if(longList ==shortList )
                return longList;
            longList = longList.next;
            shortList = shortList.next;
        }
        return null;
    }
}

142.环形链表2

第一想法:

第一想法就是我做不出来

代码随想录思路:

慢指针一次走一个结点,快指针一次走两个结点,快指针相对慢指针一个结点一个结点的追赶,所以总会有一次相遇。

假设快指针和慢指针相遇,其中慢指针一定会在尚未走完的第一圈,而快指针可能已经转了好几圈。

当相遇时,慢指针走的距离:x+y

快指针走的距离:x+n(y+z)+y

有等式:2(x+y) = x+n(y+z)+y

即:x = (n-1)(y+z)+z;

n至少是>=1的

当n = 1 时,只需要设置起始点指针和相遇点指针同时往前走,相等时即是程序入口,因为x = z

当n>1时,只不过相遇处的指针在圆里多转几圈,将圆展开来还是一样的。

   while (fast !=null && fast.next != null){
            fast = fast.next.next;
            slow =slow.next;
        }

 这个判断条件要会想起24题的判断条件,走两步的指针如何在判断终点的同时避免空指针异常。

代码:

class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast !=null && fast.next != null){
            fast = fast.next.next;
            slow =slow.next;
            if(slow==fast){//如果有环
                ListNode index1 = head;
                ListNode index2 = slow;
                while (index1!=index2){
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
        }
        return null;
    }
}

第二十二算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值