代码随想录算法训练营第二天| 977. 有序数组的平方、209. 长度最小的子数组、59. 螺旋矩阵 II。

[LeetCode] 977. 有序数组的平方

[LeetCode] 977. 有序数组的平方讲解文章

[LeetCode] 977. 有序数组的平方讲解视频

自己看到题目的第一想法

    从头到尾遍历所有元素, 遍历过程中对每个元素进行平方后赋值. 最后对数组进行一次排序.

    要求时间复杂度为 O(n), 可是遍历计算平方数都需要 O(n) 了, 排序怎么办呢?

    如果都是整数, 是不是就不需要排序了对吧?

    于是就很聪明想, 可以通过遍历找到了第一个非负数, 找到第一个非负数就等于找到了第一个最大负数的索引, 也就是负数里平方后最小的那个数的索引, 所以负数的平方排序也就可以线性的计算出来了. 这时候使用双指针, 一个从大负数往小负数方向走, 一个从小非负数往大非负数走, 再将两个指针对应的平方数中, 小的那一个放到结果数组里. 这样当两个指针都走到末尾的时候, 结果数组的赋值就完成了...

看完代码随想录之后的想法

    是的... 对有序负数列表中的值进行平方后得到的列表, 从大到小的排序天然的就存在于原始列表中, 只需要索引从小到大遍历就行了. 而非负数的原始排列也存在于原是列表中, 只需要索引从大到小遍历就行. 而索引小的值想要大于索引大的值, 就一定是存在负数的情况. 因此如果使用双指针, 一个从 negativeIndex = 0 开始, 一个从 positiveIndex = numbers.length - 1 开始, 则 negativeIndex 在停留在第一个非负数的时候, 就不能再移动了. 或者说从 0 开始就是无法移动的. 因此最后一个数, 一定是收敛在 negativeIndex 等于 positiveIndex 这个位置的(这里写出来是因为昨天看到别人写的二分法搜索时候, 开闭区间收敛逻辑时得到的启发. 知道最终收敛的索引以及对应的取值, 才能真正的理解对应的算法).

// 自己第一反应写的答案, LeetCode 可过, 然而并不完全符合题意.
class Solution {
    public static int[] sortedSquares(int[] nums) {
        int negativeNumberIndex = -1;
        int positiveNumberIndex = -1;
        int[] newNumbers = new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] >= 0) {
                break;
            }
            negativeNumberIndex++;
        }
        positiveNumberIndex = negativeNumberIndex + 1;

        int insertIndex = 0;
        while (negativeNumberIndex >= 0 || positiveNumberIndex < nums.length) {
            if (negativeNumberIndex >= 0 && positiveNumberIndex < nums.length) {
                if (Math.abs(nums[negativeNumberIndex]) < nums[positiveNumberIndex]) {
                    newNumbers[insertIndex++] = (int) Math.pow(nums[negativeNumberIndex--], 2);
                } else {
                    newNumbers[insertIndex++] = (int) Math.pow(nums[positiveNumberIndex++], 2);
                }
            } else if (negativeNumberIndex >= 0) {
                newNumbers[insertIndex++] = (int) Math.pow(nums[negativeNumberIndex--], 2);
            } else {
                newNumbers[insertIndex++] = (int) Math.pow(nums[positiveNumberIndex++], 2);
            }
        }
        return newNumbers;
    }
}
// 看完代码随想录之后的实现
// 无论从代码行数还是逻辑清晰度, 都有了极大的提高
// 编程是一门艺术, 是一种构建的工程
// 所以大部分的我们, 都算不上工程师
// 甚至在大部分的我们的天赋里, 都没有成为工程师的资格
// 唯一的办法就是比别人努力, 执着, 坚定, 专注
// 以赢取两边力量悬殊的缩小.
class Solution {
    public int[] sortedSquares(int[] nums) {
        int leftIndex = 0;
        int rightIndex = nums.length - 1;
        
        int currentResultIndex = nums.length - 1;
        int[] newNumbers = new int[nums.length];

        while (leftIndex <= rightIndex) {
            if (nums[leftIndex] * nums[leftIndex] > nums[rightIndex] * nums[rightIndex]) {
                newNumbers[currentResultIndex--] = nums[leftIndex] * nums[leftIndex++];
            } else {
                newNumbers[currentResultIndex--] = nums[rightIndex] * nums[rightIndex--];
            }
        }

        return newNumbers;
    }
}

自己实现过程中遇到哪些困难

    努力想办法靠近 O(n) 时间复杂度的时候, 花费了不少的时间. 但是勉强还是在 LeetCode 中 AC 了. 从这个结果上看, 没有遇到太大的困难.

[LeetCode] 209. 长度最小的子数组

[LeetCode] 209. 长度最小的子数组文章讲解

[LeetCode] 209. 长度最小的子数组视频讲解

自己看到题目的第一想法

    看了文章讲解里提到了滑动窗口, 这和寻找最大字串是一样的逻辑, 很简单, 直接来吧.

看完代码随想录之后的想法

    滑动窗口的双指针, 分为向前移动寻找符合要求的终点的索引和标识窗口起始位置的索引. 窗口最开始的起始索引是 0, 直到找到第一个符合要求的终点索引时, 记录下当前窗口的长度, 然后开始搜索窗口, 即将窗口起始位置的索引向终点的索引移动. 每收缩一次判断一次窗口是否依旧满足要求, 满足要求则更新索引相应的数据. 直到当前窗口不满足要求时, 将寻找满足要求的索引区间的终点索引继续前进, 找到下一个索引区间段. 这里表达的有些糊涂是因为我脑子里不停的思考最大字串和最小字串的区别, 甚至我错误的认为最大字串和最小字串的终点索引在代码中的位置是不同的, 其实不是的, 他们都是在遍历数组的 for 循环里. 只是窗口收缩的逻辑可能略微有些区别.

// 自己第一反应做出的答案
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int leftStartIndex = 0;
        int totalSum = nums[0];
        if (totalSum >= target) {
            return 1;
        }
        int minLength = 0;
        for (int i = 1; i < nums.length; i++) {
            if (totalSum + nums[i] >= target) {
                if (minLength == 0) {
                    minLength = i - leftStartIndex + 1;
                } else {
                    minLength = Math.min(minLength, i - leftStartIndex + 1);
                }
                System.out.println("start index " + leftStartIndex + " right " + i + " total " + totalSum + " minLength " + minLength);
                totalSum -= nums[leftStartIndex++];
                i--;
            } else {
                totalSum += nums[i];
            }
        }
        return minLength;
    }
}
// 看完视频后做出的答案
// 依旧是简单清晰了很多
// 优雅存在于理性智慧中
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int subArrayStartIndex = 0;
        int subArraySum = 0;
        int minSubLength = 0;
        for (int i = 0; i < nums.length; i++) {
            subArraySum += nums[i];
            while (subArraySum >= target) {
                if (minSubLength == 0) {
                    minSubLength = i - subArrayStartIndex + 1;
                } else {
                    minSubLength = Math.min(minSubLength, i - subArrayStartIndex + 1);
                }
                subArraySum -= nums[subArrayStartIndex++];
            }
        }
        return minSubLength;
    }
}

自己实现过程中遇到哪些困难

    对于窗口收缩的逻辑, 依旧是没有想清楚想明白, 我想两个月后当我重新回来看这些内容的时候, 依旧能感觉到自己对算法的把握并不够火候. 然而就像阅读、学习, 第一遍总是无知的, 第二遍是迷茫的, 第三遍第四遍的时候, 也许就会因为自己的足够专注和努力, 而得到了悟. 而后才能豁然开朗. 对于没有天赋的我们来说, 坚持和专注是唯一的成长的机会.

[LeetCode] 59. 螺旋矩阵 II

[LeetCode] 59. 螺旋矩阵 II 文章讲解

[LeetCode] 59. 螺旋矩阵 II 视频讲解

自己看到题目的第一想法

    还记得多年前去面试, 面试官拿出纸让我写下这个算法... 当时我写的很乱, 后来看了解答觉得不过如此, 我已经会了.

    于是拿出写字板开始表达自己的思考.

    ...

    ......

    两个小时过去了, 为什么脑子里还是那么混乱呢?

    我只要每次固定一个索引, 只更新另一个索引, 然后给数组赋值, 就那么简单不是吗?

    于是有了代表方向的 director(还是错误的英文, 应该是 direction)

    幸运的是, 我终于解出来了... LeetCode 也承认了我的努力, 没有继续敲打我.

看完代码随想录之后的想法

    竟然如此简单... 四点定位心法, 先确定每一层四个点的位置, 然后就可以知道四个点之间四条线的所有点在哪里了... 为什么明明脑子里也曾经想过的四点定位, 最终却不知道如何实现呢. 文章解说里也说了, 开闭区间心法在此可映射一定的逻辑. 好的, 现在我知道 1 个 while + 4 个 for 循环可以实现螺旋数组的赋值了.

// 第一反应做出的答案
class Solution {
    public int[][] generateMatrix(int n) {

        int[][] resultMatrix = new int[n][n];

        int row = 0;
        int column = 0;

        int currentNumber = 1;

        int director = 0;

        while (true) {
            // 从左向右
            if (director == 0) {
                if (column == n || resultMatrix[row][column] != 0) {
                    column--;
                    row++;
                    director = 1;
                    if (!checkRowAndColumn(row, column, resultMatrix)) {
                        break;
                    }
                } else {
                    resultMatrix[row][column++] = currentNumber++;
                }
            } else if (director == 1) {// 从上往下
                if (row == n || resultMatrix[row][column] != 0) {
                    row--;
                    column--;
                    director = 2;
                    if (!checkRowAndColumn(row, column, resultMatrix)) {
                        break;
                    }
                } else {
                    resultMatrix[row++][column] = currentNumber++;
                }
            } else if (director == 2) {// 从右往左
                if (column == -1 || resultMatrix[row][column] != 0) {
                    column++;
                    row--;
                    director = 3;
                    if (!checkRowAndColumn(row, column, resultMatrix)) {
                        break;
                    }
                } else {
                    resultMatrix[row][column--] = currentNumber++;
                }
            } else {// 从下往上
                if (row == 0 || resultMatrix[row][column] != 0) {
                    row++;
                    column++;
                    director = 0;
                    if (!checkRowAndColumn(row, column, resultMatrix)) {
                        break;
                    }
                } else {
                    resultMatrix[row--][column] = currentNumber++;
                }
            }
        }

        return resultMatrix;
    }

    private boolean checkRowAndColumn(int row, int column, int[][] result) {
        return row >= 0 && row < result.length && column >= 0 && column < result.length && result[row][column] == 0;
    }
}
// 看完视频后做的解答
class Solution {
    public int[][] generateMatrix(int n) {
        int currentCircle = 0;
        int totalCircle = n/2;

        int rowIndex = 0;
        int columnIndex = 0;

        int[][] resultMatrix = new int[n][n];
        int number = 1;

        while (currentCircle < totalCircle ) {
            rowIndex = currentCircle;
            for (columnIndex = rowIndex; columnIndex < n - currentCircle - 1; columnIndex++) {
                resultMatrix[rowIndex][columnIndex] = number++;
            }
            for (; rowIndex < n - currentCircle - 1; rowIndex++) {
                resultMatrix[rowIndex][columnIndex] = number++;
            }
            for (; columnIndex > currentCircle; columnIndex--) {
                resultMatrix[rowIndex][columnIndex] = number++;
            }
            for (; rowIndex > currentCircle; rowIndex--) {
                resultMatrix[rowIndex][columnIndex] = number++;
            }
            currentCircle++;
            rowIndex++;
            columnIndex++;
        }
        if (n%2 == 1) {
            resultMatrix[rowIndex][columnIndex] = number;
        }
        
        return resultMatrix;
    }
}

今日收获,记录一下自己的学习时长

    因为今天的要求是自己先解题, 再看视频, 之后重新编码解题. 因此耗费的时间有些长, 甚至过程中有些沮丧. 然而当放下包袱只求答案时候, 自己还是相出了通过 LeetCode 判题的答案. 然而依旧完全没有实现最优雅的解法, 而且没有在规定的 3 - 4 小时内完成所有任务. 这对于自己是无法接受的. 人生需要有规划, 需要精准, 需要自律与克制.

    同时, 因为到今天的话, 数组篇已经完结了. 数组篇的总结还没看, 自己对数组篇的总结也还没写.

    今天是比较沮丧的. 晚安.

  • 13
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二天的算法训练营主要涵盖了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、付费专栏及课程。

余额充值