LeetCode初级 数组部分

LeetCode初级 数组部分

  • 从排序数组中删除重复项
    • 说明
    • 解答
  • 买卖股票的最佳时机 II
  • 旋转数组
    • 说明
    • 解答
      • 方法一 旋转法
  • 只出现一次的数字
    • 方法 4:位操作
  • 两个数组的交集 II
    • 说明
    • 解答
  • 加一
    • 思路
  • 移动零
    • 说明
    • 解答
  • 有效的数独
    • 说明
    • 解答
  • 旋转图像
    • 说明
    • 解答

从排序数组中删除重复项

  • 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
  • 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

说明

  • 示例 1:
    • 给定数组 nums = [1,1,2],
    • 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
    • 你不需要考虑数组中超出新长度后面的元素。
  • 示例 2:
    • 给定 nums = [0,0,1,1,1,2,2,3,3,4],
    • 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
    • 你不需要考虑数组中超出新长度后面的元素。
  • 说明:
    • 为什么返回数值是整数,但输出的答案是数组呢?
    • 请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
    • 你可以想象内部操作如下:
      // nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
      int len = removeDuplicates(nums);
      
      // 在函数里修改输入数组对于调用者是可见的。
      // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
      for (int i = 0; i < len; i++) {
          print(nums[i]);
      }
      

解答

  • 解析:
    • 使用unique进行去重unique(begin(), end()), 返回去重后的end
    • (非必须)使用erase删除后面的元素
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
         nums.erase(unique(nums.begin(), nums.end()), nums.end());
         // int t = unique(nums.begin(), nums.end()) - nums.begin();
         // return t;
        return nums.size();
    }
};

买卖股票的最佳时机 II

旋转数组

说明

  • 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
  • 示例 1:
    • 输入与输出:
      • 输入: [1,2,3,4,5,6,7] 和 k = 3
      • 输出: [5,6,7,1,2,3,4]
    • 解释:
      • 向右旋转 1 步: [7,1,2,3,4,5,6]
      • 向右旋转 2 步: [6,7,1,2,3,4,5]
      • 向右旋转 3 步: [5,6,7,1,2,3,4]
  • 示例 2:
    • 输入与输出:
      • 输入: [-1,-100,3,99] 和 k = 2
      • 输出: [3,99,-1,-100]
    • 解释:
      • 向右旋转 1 步: [99,-1,-100,3]
      • 向右旋转 2 步: [3,99,-1,-100]
  • 说明:
    • 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
    • 要求使用空间复杂度为 O(1) 的 原地 算法。

解答

方法一 旋转法

  • 逻辑如下:
    • 输入: [1,2,3,4,5,6,7] 和 k = 3
    • 输出: [5,6,7,1,2,3,4]
    • 将输出反转一下: [4, 3, 2, 1, 7, 6, 5]
    • 把它两个部分: [4, 3, 2, 1] 以及 [7, 6, 5]
    • 然后和输入进行对比
      • 第一段为 [1234]的反转
      • 第二段为 567的反转
    • 中提供中的reverse() 反转函数 reverse(arrayRever.begin(),arrayRever.end());
  • 代码:
    void rotate(vector<int>& nums, int k) {
    	k = k % nums.size();
    	int iLen = nums.size();
    	reverse(nums.begin(),nums.end()-k);
    	reverse(nums.end()-k,nums.end());
    	reverse(nums.begin(),nums.end());
    }
    

只出现一次的数字

  • 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
  • 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
  • 示例
    • 示例 1:
      • 输入: [2,2,1]
      • 输出: 1
    • 示例 2:
      • 输入: [4,1,2,1,2]
      • 输出: 4

方法 4:位操作

  • 概念:
    • 如果我们对 0 和二进制位做 XOR 运算,得到的仍然是这个二进制位
      • 0 = a⊕0=a
    • 如果我们对相同的二进制位做 XOR 运算,返回的结果是 0
      • a = a⊕a=0
    • XOR 满足交换律和结合律
      • a =a⊕b⊕a=(a⊕a)⊕b=0⊕b=b
    • 所以我们只需要将所有的数进行 XOR 操作,得到那个唯一的数字。
      class Solution(object):
          def singleNumber(self, nums):
              """
              :type nums: List[int]
              :rtype: int
              """
              a = 0
              for i in nums:
                  a ^= i
              return a
      
    • 复杂度分析
    • 时间复杂度: O(n)O(n) 。我们只需要将 \text{nums}nums 中的元素遍历一遍,所以时间复杂度就是 \text{nums}nums 中的元素个数。
    • 空间复杂度:O(1)O(1) 。

两个数组的交集 II

说明

  • 给定两个数组,编写一个函数来计算它们的交集。
  • 示例
    • 示例 1:
      • 输入: nums1 = [1,2,2,1], nums2 = [2,2]
      • 输出: [2,2]
    • 示例 2:
      • 输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
      • 输出: [4,9]
  • 说明:
    • 输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
    • 我们可以不考虑输出结果的顺序。
  • 进阶:
    • 如果给定的数组已经排好序呢?你将如何优化你的算法?
    • 如果 nums1 的大小比 nums2 小很多,哪种方法更优?
    • 如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?

解答

  • 思路:
    • 将第一个列表的数加入到map中进行统计
    • 第二个列表对比map进行统计相同(记得删除);
  • Code
    class Solution {
    public:
        vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
            map<int,int> m;
            for(int i=0;i<nums1.size();i++)        //遍历数组1,将数组1中对应元素的的映射加1
            {
                m[nums1[i]]++;
            }                                      //最后得到的映射就是数组1中每个元素的数量
            vector<int> rev;
            for(int i=0;i<nums2.size();i++)        //遍历数组2
            {
                if(m[nums2[i]]>=1)                //映射大于等于1的情况
                {
                    rev.push_back(nums2[i]);
                    m[nums2[i]]--;
                }
            }
            return rev;
    
        }
    };
    

加一

  • 给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
  • 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
  • 你可以假设除了整数 0 之外,这个整数不会以零开头。
  • 示例
    • 示例 1:
      • 输入: [1,2,3]
      • 输出: [1,2,4]
    • 解释: 输入数组表示数字 123。
    • 示例 2:
      • 输入: [4,3,2,1]
      • 输出: [4,3,2,2]
    • 解释: 输入数组表示数字 4321。

思路

  • 从后向前,找到第一个不为9的数,该位+1,该位后的数均变为0;
  • 未找到的话,说明该数全为9,则返回 1 加 digits.size()个0
class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        size_t len = digits.size();
        vector<int> ans(len+1, 0);
        ans[0] = 1;
        // 注意如果这里使用size_t 的话 当为负数的时,就报错
        for(int i = len - 1; i >= 0; --i){
            if(digits[i] < 9){
                digits[i]++;
                for(int j = i + 1; j < len ; ++j){
                    digits[j] = 0;
                }
                return digits;
            }
        }
        return ans;
    }
};

移动零

说明

  • 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
  • 示例:
    • 输入: [0,1,0,3,12]
    • 输出: [1,3,12,0,0]
  • 说明:
    • 必须在原数组上操作,不能拷贝额外的数组。
    • 尽量减少操作次数。

解答

  • 方法1 快慢指针
    • 思路
      • 快指针 往后便利
      • 慢指针 满足条件的IDX
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int lastNoZeroIdx, i;
        for(lastNoZeroIdx = 0,  i=0; i<nums.size(); i++){
            if(nums[i] != 0){
                nums[lastNoZeroIdx++]=nums[i];
            }
        }
        for(;lastNoZeroIdx < nums.size(); lastNoZeroIdx++){
        	nums[lastNoZeroIdx] = 0;
        }
    }
};
  • 方法2 快慢指针升级版本
  • 基本思路, 慢指针的下一个必定是0, 一致往后挪动即可
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        for(int lastNoZeroIdx = 0, i=0; i<nums.size(); i++){
            if(nums[i] != 0){
                swap(nums[lastNoZeroIdx++], nums[i]);
            }
        }
    }
};

有效的数独

说明

  • 判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
    • 数字 1-9 在每一行只能出现一次。
    • 数字 1-9 在每一列只能出现一次。
    • 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

LeetCode初级 数组部分_第1张图片

  • 上图是一个部分填充的有效的数独。
  • 数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
  • 示例
    • 示例 1:
      • 输入:
        [
        [“5”,“3”,".",".",“7”,".",".",".","."],
        [“6”,".",".",“1”,“9”,“5”,".",".","."],
        [".",“9”,“8”,".",".",".",".",“6”,"."],
        [“8”,".",".",".",“6”,".",".",".",“3”],
        [“4”,".",".",“8”,".",“3”,".",".",“1”],
        [“7”,".",".",".",“2”,".",".",".",“6”],
        [".",“6”,".",".",".",".",“2”,“8”,"."],
        [".",".",".",“4”,“1”,“9”,".",".",“5”],
        [".",".",".",".",“8”,".",".",“7”,“9”]
        ]
      • 输出: true
    • 示例 2:
      • 输入:
        [
        [“8”,“3”,".",".",“7”,".",".",".","."],
        [“6”,".",".",“1”,“9”,“5”,".",".","."],
        [".",“9”,“8”,".",".",".",".",“6”,"."],
        [“8”,".",".",".",“6”,".",".",".",“3”],
        [“4”,".",".",“8”,".",“3”,".",".",“1”],
        [“7”,".",".",".",“2”,".",".",".",“6”],
        [".",“6”,".",".",".",".",“2”,“8”,"."],
        [".",".",".",“4”,“1”,“9”,".",".",“5”],
        [".",".",".",".",“8”,".",".",“7”,“9”]
        ]
      • 输出: false
  • 解释:
    • 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
    • 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
  • 说明:
    • 一个有效的数独(部分已被填充)不一定是可解的。
    • 只需要根据以上规则,验证已经填入的数字是否有效即可。
    • 给定数独序列只包含数字 1-9 和字符 ‘.’ 。
    • 给定数独永远是 9x9 形式的。

解答

  • 思路:
    • 基本思想以空间为代价,遍历的时候每一个都判断是否满足条件
    • 弄一个row, col,cube的队列来分别判断首付重复
  • Code
    class Solution {
    public:
        int getcubeid(int x, int y){
            return 3 * (x / 3) + y / 3;
        }
        bool isValidSudoku(vector<vector<char>>& board) {
            vector<vector<bool>> Row(9, vector<bool>(9, false));
            vector<vector<bool>> Col(9, vector<bool>(9, false));
            vector<vector<bool>> Cubes(9, vector<bool>(9, false));
            for(int i=0; i<9; ++i){
                for(int j =0; j<9; ++j){
                    char &t = board[i][j];
                    
                    if(t=='.') {continue;}
                    if(Row[i][t-'1']) {return false;}
                    if(Col[j][t-'1']) {return false;}
                    if(Cubes[getcubeid(i, j)][t-'1']) {return false;}
                    
                    Row[i][t-'1'] = 1;
                    Col[j][t-'1'] = 1;
                    Cubes[getcubeid(i, j)][t-'1'] = 1;
                }
            }
            return true;
        }
    };
    

旋转图像

说明

  • 给定一个 n × n 的二维矩阵表示一个图像。将图像顺时针旋转 90 度。
  • 说明:
    • 你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。
  • 示例
    • 示例 1:
    • 给定 matrix =
      [
      [1,2,3],
      [4,5,6],
      [7,8,9]
      ],
    • 原地旋转输入矩阵,使其变为:
      [
      [7,4,1],
      [8,5,2],
      [9,6,3]
      ]
    • 示例 2:
    • 给定 matrix =
      [
      [ 5, 1, 9,11],
      [ 2, 4, 8,10],
      [13, 3, 6, 7],
      [15,14,12,16]
      ],
    • 原地旋转输入矩阵,使其变为:
      [
      [15,13, 2, 5],
      [14, 3, 4, 1],
      [12, 6, 8, 9],
      [16, 7,10,11]
      ]

解答

  • 思路:
    • 将原矩阵转置
    • 然后对每一行逆序即可
    class Solution {
    public:
        void rotate(vector<vector<int>>& matrix) {
            // 转置矩阵
            int rows = matrix.size();
            int cols = matrix[0].size();
            for(int i = 0; i<rows; ++i) {
                for(int j = i; j<cols; ++j){
                    int tmp = matrix[i][j];
                    matrix[i][j] = matrix[j][i];
                    matrix[j][i] = tmp;
                }
            }
            // 调整列
            for(auto &t:matrix){
                reverse(t.begin(), t.end());
            }
        }
    };
    

你可能感兴趣的:(LeetCode)