leetcode刷题篇 第一篇

82 阅读2分钟
燕过留痕,写这个长篇的原因是我不希望我的学习以及思考是没有痕迹的,所以乐于对于我的思考过程进行记录,选择一个较优质的平台进行长久的储存,供复盘。

分篇是因为我这是按照题型来进行专项训练 第一篇:本篇主要内容为 数组 链表章节

数组是不能删的,只能进行覆盖

第一篇

二分查找:注意区间的区别

  1. 左闭右开

  2. 左闭右闭

对于这两种条件的写法不同 需要考虑的是left = right是否存在意义,以及middle值的变化

数组移除

暴力解法:使用双层循环即可,高位覆盖

双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

这种方法在数组、链表、字符串等操作中很常见,why?

因为涉及到一个点,就是遍历,简化循环的次数,对于嵌套for可以考虑进行解法切换

具体应用:对于解决数组移除问题

将快指针定义在循环中,当不相等时,快指针继续移动,慢指针停滞

之后进行高位覆盖


class Solution {

    public int removeElement(int[] nums, int val) {

        int slow = 0;

        for (int fast = 0; fast < nums.length; fast++) {

            if(val != nums[fast]){

                nums[slow] = nums[fast];

                slow++;

            }

        }

        return slow;

    }

}

解法二:基于题目中出现元素位置可以移动,那么此时可以使用相向双指针法


class Solution {

    public int removeElement(int[] nums, int val) {

        int left = 0;

        int right = nums.length - 1;

        while(right >= 0 && nums[right]==val)right--;

        while(left <= right){

            if (nums[left] == val){

                nums[left] = nums[right];

                right--;

            }else{

                left++;

                while(right >= 0 && nums[right] == val) right--;

            }

        }

        return left;

    }

}

有序数组的平方

这题的核心在于如何处理负数平方后放置的位置:

这里是无法丢失数据的,所以使用创建新数组存放的方式


class Solution {

    public int[] sortedSquares(int[] nums) {

        // 本题涉及到的点还是双指针法 从两头到中间的思想 加快遍历速度

        int solw = 0;

        int fast = nums.length - 1;

        int index = nums.length - 1;

        int[] result = new int[nums.length];

        while(solw <= fast){

            if(nums[solw]*nums[solw] > nums[fast]*nums[fast]){

                result[index--] = nums[solw]*nums[solw];

                solw++;

            }else{

                result[index--] = nums[fast]*nums[fast];

                fast--;

            }

        }

        return result;

    }

}

长度最小的子数组

首先考虑暴力解法:双重for遍历 当需要多次确认或从某已经遍历过的地方重新开始时就需要使用双重循环,i不用说,j从i处开始


    int minSubArrayLen(int s, vector<int>& nums) {

        int result = INT32_MAX; // 最终的结果

        int sum = 0; // 子序列的数值之和

        int subLength = 0; // 子序列的长度

        for (int i = 0; i < nums.size(); i++) { // 设置子序列起点为i

            sum = 0;

            for (int j = i; j < nums.size(); j++) { // 设置子序列终止位置为j

                sum += nums[j];

                if (sum >= s) { // 一旦发现子序列和超过了s,更新result

                    subLength = j - i + 1; // 取子序列的长度

                    result = result < subLength ? result : subLength;

                    break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break

                }

            }

        }

        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列

        return result == INT32_MAX ? 0 : result;

    }

技巧解法:滑动窗口法

不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果

涉及到不断重置位置的子序列问题,就需要使用到滑动窗口

好的,这里我们已经明确了为什么要使用滑动窗口的方式去解决这个问题,那么就出现了下面一个问题,我们如何使用一个for解决这个问题呢?我们for循环中的索引是什么呢?

这里直接给出答案:for循环中的索引应该是末位置

为什么?

如果使用首位,那么当跳出循环之后我们如何遍历接下来的元素呢,这里必然会出现一个死循环

所以这里使用末端位置作为索引

那么末位置的移动我们这里已经解决了,首位的移动呢? 这就是滑动窗口的核心步骤了

这里引用代码随想录中的图

其实滑动窗口也是双指针的思路:

双指针就是使用一个指针去指代单个位置,而处理多个元素的时候就不可以使用指针的方式去指代这么多个元素,但是因为元素的数量你并不可控,所以这个时候需要使用一种方式去绑定这多个元素,还绑定这多个元素的方法就是使用sum


class Solution {

    public int minSubArrayLen(int s, int[] nums) {

        int left = 0;

        int sum = 0;

        int result = Integer.MAX_VALUE;

        for (int fin = 0; fin < nums.length; fin++) {

            sum+=nums[fin];

            while(sum>=s){

                int subLength = fin - left + 1;

                result = (result < subLength) ? result : subLength;

                sum-=nums[left++]; // 核心步骤

            }

        }

        return result == Integer.MAX_VALUE ? 0 : result;

    }

}

还有一个问题:这里的时间复杂度会是多少?

答案是O(n)

为什么?因为时间复杂度计算是看大部分元素一共被执行了多少次

for双重嵌套无疑是n次,那么时间复杂度为O(n^2),但是这里每个在for这里一次,在while中一次,只有两次,所以是O(2n),但是常数省略,所以是O(n)。

螺旋矩阵

这里的重点是边界条件的设定,主要关于每一个边界处理方式都需要达到一致

叫做:循环不变量,在循环中对于边界处理规则不能发生改变


class Solution {

    public int[][] generateMatrix(int n) {

        int startX = 0;

        int startY = 0;

        int time = 0;

        int offset = 1;

        int num = 1;

        int i,j;

        int[][] result = new int[n][n];

        while(time < n/2){

            // 循环不变量的设定 即边界条件的处理方式

            for (i = startX; i < n - offset; i++) {

                result[startY][i] = num++;

            }

            for (j = startY; j < n - offset; j++) {

                result[j][i] = num++;

            }

            for (;i > startX; i--) {

                result[j][i] = num++;

            }

            for (;j > startY ; j--) {

                result[j][i] = num++;

            }

            startY++;

            startX++;

            offset++;

            time++;

        }

        if(n % 2 == 1){

            result[n/2][n/2] = n * n;

        }

        return result;

    }

}

总结

主要解法:

  1. 双指针应用 减少循环,提升遍历速度 应用很广

  2. 滑动窗口 绑定多数据进行移动,查找子序列