《LeetCode面试刷题》第一篇

1. 第73题 矩阵置零

题目地址

给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。

这题如果不适用原地算法非常简单,可以用做初学者的数组练习题,直接开一个n * m的矩阵,然后让它的元素和原数组完全对应,之后遍历原数组的每一个元素,如果是0在新数组改变对应行列的元素即可。(时空复杂度较高)

如果空间复杂度压缩到O(n + m)的话,我们只需要开两个一维数组,记录行列是否存在0,最后在遍历修改值即可。

但是要求原地算法,那就不能多开数组,我们可以借助原数组的第一行和第一列作为上面的两个数组,但是导致的问题就是第一行和第一列无法被计算,所以我们多开两个遍历记录第一行和第一列是否应该变为0

class Solution {
    
    
    public void setZeroes(int[][] matrix) {
    
    
        int r0 = 1, c0 = 1; // r0记录第一行是否存在0, c0记录第一列是否存在0
        int n = matrix.length, m = matrix[0].length; // n:行数 m:列数

        for (int i = 0; i < m; i ++ ) if (matrix[0][i] == 0) r0 = 0; // 如果第一列存在0,就把r0标志改变
        for (int i = 0; i < n; i ++ ) if (matrix[i][0] == 0) c0 = 0;

        for (int i = 1; i < m; i ++ ) // 判断除了第一列的每一列是否有0
            for (int j = 0; j < n; j ++ )
                if (matrix[j][i] == 0) // 如果存在0就把第一列当成额外的一维数组,标记成0
                    matrix[0][i] = 0;
        
        for (int i = 1; i < n; i ++ ) // 和列的操作一样
            for (int j = 0; j < m; j ++ )
                if (matrix[i][j] == 0)
                    matrix[i][0] = 0;
        
        for (int i = 1; i < m; i ++ )
            if (matrix[0][i] == 0) // 如果某一列存在0
                for (int j = 0; j < n; j ++ ) // 这一列全部赋值为0
                    matrix[j][j] = 0;
        
        for (int i = 1; i < n; i ++ )
            if (matrix[i][0] == 0) // 如果某一行存在0
                for (int j = 0; j < m; j ++ ) // 这一行全部赋值为0
                    matrix[i][j] = 0;
        
        // 此时就剩第一行第一列没有解决了,单独判断一下是否应该全部赋值0
        if (r0 == 0) for (int i = 0; i < m; i ++ ) matrix[0][i] = 0;
        if (c0 == 0) for (int i = 0; i < n; i ++ ) matrix[i][0] = 0;

    }
}

时间复杂度从O(n*n*m*m)变成了O(4n*n),空间复杂度变成了O(1)


2. 第75题 颜色分类

题目地址

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

这就是一个普通的排序,如果遇到了那我肯定选择直接一个快排搞定它,像这样:

class Solution {
    
    

    void quick_sort(int l, int r, int[] nums) {
    
    
        if (l >= r) return;
        int x = nums[l + r >> 1], i = l - 1, j = r + 1;
        while (i < j) {
    
    
            do i ++ ; while (nums[i] < x);
            do j -- ; while (nums[j] > x);
            if (i < j) {
    
    
                int t = nums[i]; // java的swap
                nums[i] = nums[j];
                nums[j] = t;
            }
        }
        quick_sort(l, j, nums);
        quick_sort(j + 1, r, nums);
    }


    public void sortColors(int[] nums) {
    
    
        quick_sort(0, nums.length - 1, nums);
    }
}

当然快排一遍还是较为复杂,可以直接扫描一遍记录下三个数分别出现多少次,在写回去就行了。

上面是两种非常容易想到的写法,还有一种较为巧妙地方法,我们姑且叫它三指针
首先维护i, j, k三个变量,在数组上的指向让它符合j < i < k,且下标0 ~ j - 1这段区间全部是0,j ~ i - 1这段区间全部是1k + 1 ~ n - 1这段全部是2。初始的时候i = j = 0, k = n - 1,这样可以发现满足上述的定义,让i尝试者向右移动,期间不断维护上述的性质,当ik相交时整个区间就排完了。

class Solution {
    
    
    public void sortColors(int[] nums) {
    
    
        int t;
        for (int i = 0, j = 0, k = nums.length - 1; i <= k; ) {
    
    
            if (nums[i] == 2) {
    
    
                t = nums[i];
                nums[i] = nums[k];
                nums[k] = t;
                k -- ;
            } else if (nums[i] == 1) i ++ ; // 如果i的位置是1只需要i继续移动就能维护该性质
            else {
    
     
            // i的位置等于0时候,这个0应该给下标0~j-1的区间,所以做交换
            // 又因为j所在的位置根据性质肯定为1,所以交换完i的位置已经是1了,根据性质j~i-1是1,所以i和j都要移动
                t = nums[i];
                nums[i] = nums[j];
                nums[j] = t;
                i ++ ;
                j ++ ;
            }
        }
    }
}

3. 第78题 子集

题目地址

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

元素互不相同,找所有子集,我直接上来就是一个爆搜,但是爆栈了,很奇怪,找了半天发现了自己的憨批操作

class Solution {
    
    
    
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    void dfs(int[] nums, int step) {
    
    
        if (step == nums.length) {
    
    
            res.add(new ArrayList(path));
            return; // 我忘了return!!!
        }
        
        path.add(nums[step]);
        dfs(nums, step + 1);
        path.remove(path.size() - 1);

        dfs(nums, step + 1);
    }

    public List<List<Integer>> subsets(int[] nums) {
    
    
        dfs(nums, 0);
        return res;
    }
}

查看题解发现还有二进制的巧妙做法:
假设有n个数,那么这些数的子集就有2^n个,其中每一种方案都对应着一些数要被选择,一些数不被选择,那么选择的话设置1,不选的设置0,每一种方案都对应了一个01串,把他们看成二进制的位的话,组成的就一定是大于等于0且小于等于2^n的数字(最大就是全选)
那么自己和二进制的数字就一一对应,就可以枚举这些数字,看看他们的每一位是不是1,如果是的话就证明被选择了,加到答案里即可。

class Solution {
    
    
   
    public List<List<Integer>> subsets(int[] nums) {
    
    
        List<List<Integer>> res = new ArrayList<>();

        for (int i = 0; i < (1 << nums.length); i ++ ) {
    
    
            List<Integer> path = new ArrayList();
            for (int j = 0; j < nums.length; j ++ )
                if ((i >> j & 1) == 1)
                    path.add(nums[j]);
            res.add(path);
        }

        return res;
    }
}

第一天,且有事,所以only三题

猜你喜欢

转载自blog.csdn.net/weixin_43795939/article/details/113865162