0
点赞
收藏
分享

微信扫一扫

【数据结构】LeetCode必刷题之数组

我是小瘦子哟 2022-02-20 阅读 71

一、数组

1、026——删除有序数组中的重复项

给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。将最终结果插入 nums 的前 k 个位置后返回 k 。不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

/**
* 思路:用temp记录当前位置的值,然后依次比较,如果相同数组下标移动,
* 如果不同,将当前数组下标的值,复制到ans位置
*
* @param nums 目标数组
* @return 新数组长度
*/
public int removeDuplicates(int[] nums) {
    if (null == nums || nums.length == 0) {
        return 0;
    }
    int temp = nums[0];
    int ans = 1;
    int index = 1;
    //
    while (index < nums.length) {
        if (nums[index] != temp) {
            nums[ans++] = nums[index];
            temp = nums[index];
        }
        index++;
    }
    return ans;
}

2、027——移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

/**
* 思路:用ans下标记录不同于目标值的元素,返回ans值
*
* @param nums 目标数组
* @param val  目标值
* @return 返回新数组长度
*/
public static int removeElement(int[] nums, int val) {
    if (null == nums || nums.length == 0) {
        return 0;
    }
    int index = 0;
    int ans = 0;
    while (index < nums.length) {
        if (nums[index] != val) {
            nums[ans++] = nums[index];
        }
        index++;
    }
    return ans;
}

3、031——下一个排列

整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。

例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。

必须 原地 修改,只允许使用额外常数空间。

/**
* 思路:从后往前找到第一个arr[k] < arr[k + 1],这个时候从arr[k+1] 往后依次递减
* 然后再从后往前找到第一个大于arr[k]的值,两者交换,然后将从arr[k+1]开始往后这段反转
* 如果没找到arr[k] < arr[k + 1],此时k为-1,即数组为降序数组,直接倒序就行
*
* @param nums 目标数组
*/
public static void nextPermutation(int[] nums) {
    if (null == nums || nums.length < 2) {
        return;
    }
    int k = nums.length - 2;
    while (k >= 0 && nums[k] >= nums[k + 1]) {
        k--;
    }
    if (k >= 0) {
        int i = nums.length - 1;
        while (i >= 0 && nums[k] >= nums[i]) {
            i--;
        }
        swap(nums, k, i);
    }
    reverse(nums, k + 1);

}

private static void swap(int[] arr, int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

private static void reverse(int[] arr, int left) {
    int l = left;
    int r = arr.length - 1;
    while (l < r) {
        swap(arr, l, r);
        l++;
        r--;
    }
}

4、041——缺失的第一个正数

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

/**
* 思路:原地哈希,由题可知返回值为[1,N + 1],
* 把1放到下标为0,2放到下标为1的位置,以此类推,哪个位置的值不等于下标+1,就返回那个位置的下标+1值
*
* @param nums 目标数组
* @return 返回未出现的最小正整数
*/
public static int firstMissingPositive(int[] nums) {
    if (null == nums || nums.length == 0) {
        return 0;
    }
    int len = nums.length;
    for (int i = 0; i < len; i++) {
        while (nums[i] > 0 && nums[i] <= len && nums[nums[i] - 1] != nums[i]) {
            // 满足在指定范围内、并且没有放在正确的位置上,才交换
            swap(nums, nums[i] - 1, i);
        }
    }
    for (int i = 0; i < len; i++) {
        if (nums[i] != i + 1) {
            return i + 1;
        }
    }
    // 都正确则返回数组长度 + 1
    return len + 1;
}

private static void swap(int[] nums, int index1, int index2) {
    int temp = nums[index1];
    nums[index1] = nums[index2];
    nums[index2] = temp;
}

5、054——螺旋矩阵

给你一个 mn 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

示例 1:

img

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

/**
* 定义边界值,不断缩小边界值
*
* @param matrix 目标数组
* @return 顺时针遍历结果
*/
public static List<Integer> spiralOrder(int[][] matrix) {
    List<Integer> list = new ArrayList<>();
    if (null == matrix || matrix.length == 0) {
        return list;
    }
    int u = 0;
    int d = matrix.length - 1;
    int l = 0;
    int r = matrix[0].length - 1;
    while (true) {
        // 向右移动到最右
        for (int i = l; i <= r; i++) {
            list.add(matrix[u][i]);
        }
        // 若上边界大于下边界,则遍历遍历完成
        if (++u > d) {
            break;
        }
        // 向下遍历
        for (int i = u; i <= d; i++) {
            list.add(matrix[i][r]);
        }
        // 若右边界小于左边界,则遍历遍历完成
        if (--r < l) {
            break;
        }
        // 向左遍历
        for (int i = r; i >= l; i--) {
            list.add(matrix[d][i]);
        }
        // 若下边界小于上边界,则遍历遍历完成
        if (--d < u) {
            break;
        }
        for (int i = d; i >= u; i--) {
            list.add(matrix[i][l]);
        }
        if (++l > r) {
            break;
        }
    }
    return list;
}

6、056——合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

/**
* 思路:排序 + 双指针
* 左指针指区间开始,一个临时变量存最大的区间结尾值,右指针也指向区间开始,
* 比较该区间开始值和临时最大值是否重合,重合取较大的值,更新右指针,
* 如果不重合,放入结果集,更新左指针
*
* @param intervals 目标数组
* @return 返回合并后的区间
*/
public int[][] merge(int[][] intervals) {
    if (intervals == null || intervals.length == 0) {
        return new int[0][2];
    }
    // 排序
    Arrays.sort(intervals, (a, b) -> {
        return a[0] - b[0];
    });
    List<int[]> res = new ArrayList<>();
    //下一个数组的起始值必须要小于上一个数组的最大值才能合并
    int leftIndex = 0;
    int rightIndex = 0;
    while (rightIndex < intervals.length) {
        int start = intervals[leftIndex][0];
        int end = intervals[rightIndex][1];
        while (rightIndex < intervals.length && intervals[rightIndex][0] <= end) {
            end = Math.max(end, intervals[rightIndex][1]);
            rightIndex++;
        }
        res.add(new int[]{start, end});
        leftIndex = rightIndex;
    }
    int[][] result = new int[res.size()][2];
    for (int i = 0; i < res.size(); i++) {
        result[i][0] = res.get(i)[0];
        result[i][1] = res.get(i)[1];
    }

    return result;
}

7、057——插入区间

给你一个 无重叠的 *,*按照区间起始端点排序的区间列表。

在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。

/**
* 思路:因为是有序的,所以从左到右,先把左边不重合的区间添加进去,
* 在找到重合的区间,添加进去,再把右边不重合部分添加进去
*
* @param intervals   原数组
* @param newInterval 插入数组
* @return 合并后的区间
*/
public static int[][] insert2(int[][] intervals, int[] newInterval) {
    if (intervals == null || intervals.length == 0) {
        return new int[][]{{newInterval[0], newInterval[1]}};
    }
    if (newInterval == null || newInterval.length == 0) {
        return intervals;
    }
    List<int[]> res = new ArrayList<>();
    int len = intervals.length;
    int index = 0;
    // 判断左边不重合
    while (index < len && intervals[index][1] < newInterval[0]) {
        res.add(intervals[index]);
        index++;
    }
    // 判断重合
    while (index < len && intervals[index][0] <= newInterval[1]) {
        newInterval[0] = Math.min(intervals[index][0], newInterval[0]);
        newInterval[1] = Math.max(intervals[index][1], newInterval[1]);
        index++;
    }
    res.add(newInterval);
    // 判断右边不重合
    while (index < len && intervals[index][0] > newInterval[1]) {
        res.add(intervals[index]);
        index++;
    }
    return res.toArray(new int[0][]);
}

8、059—— 螺旋矩阵 II

给你一个正整数 n ,生成一个包含 1n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix

/**
 * 思路:同54题旋转矩阵,定义边界值,不断更新边界值,遍历
 *
 * @param n 输入的正整数值
 * @return 顺时针螺旋排列的数组
 */
public int[][] generateMatrix(int n) {
    if (n == 1) {
        return new int[][]{{1}};
    }
    int[][] result = new int[n][n];
    int u = 0;
    int d = n - 1;
    int l = 0;
    int r = n - 1;
    int start = 1;
    while (start <= n * n) {
        // 向左遍历
        for (int i = l; i <= r; i++) {
            result[u][i] = start++;
        }
        if (++u > d) {
            break;
        }
        // 向下遍历
        for (int i = u; i <= d; i++) {
            result[i][r] = start++;
        }
        if (--r < l) {
            break;
        }
        // 向左遍历
        for (int i = r; i >= l; i--) {
            result[d][i] = start++;
        }
        if (--d < u) {
            break;
        }
        for (int i = d; i >= u; i--) {
            result[i][l] = start++;
        }
        if (++l > r) {
            break;
        }
    }
    return result;
}

9、073——矩阵置零

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

img

输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]
输出:[[1,0,1],[0,0,0],[1,0,1]]

/**
 * 思路: 先判断第一行和第一列是否需要置0, 然后用第一行和第一列来标记该行或该列是否是要置0
 * 然后处理第一行和第一列
 *
 * @param matrix 目标数组
 */
public void setZeroe1(int[][] matrix) {
    if (null == matrix || matrix.length == 0) {
        return;
    }
    int row = matrix.length;
    int col = matrix[0].length;
    boolean rowFlag = false;
    boolean colFlag = false;
    // 第一行是否有0
    for (int i = 0; i < col; i++) {
        if (matrix[0][i] == 0) {
            rowFlag = true;
            break;
        }
    }

    // 第一列是否有0
    for (int i = 0; i < row; i++) {
        if (matrix[i][0] == 0) {
            colFlag = true;
            break;
        }
    }

    // 把第一行和第一列作为标记位
    for (int i = 1; i < row; i++) {
        for (int j = 1; j < col; j++) {
            if (matrix[i][j] == 0) {
                matrix[i][0] = matrix[0][j] = 0;
            }
        }
    }

    // 置0
    for (int i = 1; i < row; i++) {
        for (int j = 1; j < col; j++) {
            if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                matrix[i][j] = 0;
            }
        }
    }

    if (rowFlag) {
        for (int j = 0; j < col; j++) {
            matrix[0][j] = 0;
        }
    }
    if (colFlag) {
        for (int i = 0; i < row; i++) {
            matrix[i][0] = 0;
        }
    }


}

10、118——杨辉三角

给定一个非负整数 *numRows,*生成「杨辉三角」的前 numRows 行。

在「杨辉三角」中,每个数是它左上方和右上方的数的和。

输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]

/**
 * 思路:根据杨辉三角的特性可知,每一层第一个和最后一个都为1,其他位置等于上一层,左边和右边之和
 *
 * @param numRows 非负整数
 * @return 杨辉三角集合
 */
public static List<List<Integer>> generate(int numRows) {
    List<List<Integer>> ans = new ArrayList<List<Integer>>();
    for (int i = 1; i <= numRows; i++) {
        List<Integer> level = new ArrayList<>();
        for (int j = 1; j <= i; j++) {
            if (j == 1 || j == i) {
                level.add(1);
            } else {
                level.add(ans.get(i - 2).get(j - 2) + ans.get(i - 2).get(j - 1));
            }
        }
        ans.add(level);
    }
    return ans;
}

11、119——杨辉三角 II

给定一个非负索引 rowIndex,返回「杨辉三角」的第 rowIndex 行。

在「杨辉三角」中,每个数是它左上方和右上方的数的和。

/**
 * 思路:本题仅需最后一行的数据,当处理到最后一行时,row正好是上一行的数据,直接使用
 *
 * @param rowIndex 要返回的行数
 * @return 返回输入行的集合
 */
public List<Integer> getRow(int rowIndex) {
    List<Integer> row = new ArrayList<>();
    row.add(1);
    for (int i = 1; i < rowIndex; i++) {
        row.add(0);
        for (int j = i; j > 0; j--) {
            row.set(j, row.get(j) + row.get(j - 1));
        }
    }
    return row;
}
举报

相关推荐

0 条评论