LeetCode—数组

  • T1 两数之和
  • T15 三数之和
  • T26 删除排序数组中的重复项
  • T39 组合之和
  • T62 不同路径
  • T64 最小路径和
  • T78 子集
  • T105 从前序与中序遍历序列构造二叉树
  • T106 从中序与后序遍历序列构造二叉树
  • T189 旋转数组
  • T628 三个数的最大乘积
  • T485 最大连续1的个数
  • T581 最短无序连续子数组

T1 两数之和

给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。

你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

平生不识TwoSum,刷尽LeetCode也枉然
暴力搜索,复杂度 O(n^2)

想用线性时间复杂度,需要只遍历一个数字,另一个数字事先用HashMap存起来,建立数字和坐标的映射关系。

HashMap是常数级的查找效率。

我们在遍历数组的时候,用target减去遍历到的数字,就是另一个需要的数字了,直接在HashMap中查找其是否存在即可

注意要判断查找到的数字不是第一个数字,比如target是4,遍历到了一个2,那么另外一个2不能是之前那个2。

整个实现步骤为:先遍历一遍数组,建立HashMap映射,然后再遍历一遍,开始查找,找到则记录index。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> m;        // 无序哈希表
        vector<int> res;

        for(int i=0; i// 先将数组的值、索引存成键值对的形式
            m[nums[i]] = i;

        for(int i=0; iint temp = target - nums[i];  //第二个数

            if(m.count(temp) && m[temp]!=i)  // 如果哈希表内存在第二个数,且索引和第一个数的索引不一样
            {
                res.push_back(i);
                res.push_back(m[temp]);
                break;
            }
        }
        return res;
    }
};

T15 三数之和

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[ [-1, 0, 1] [-1, -1, 2] ]

(1)加和为0,先固定一个数,再找另外两个,三个有正有负
(2)排序,升序,用双指针
(3)遍历到 倒数第三个停止,
(4)剪枝处理,第一个是正数的,break
(5)重复就跳过,不想相同的数字固定两次

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int> > res;
        sort(nums.begin(),nums.end());

        for(int k=0; kif(nums[k]>0) break;
            if(k>0 && nums[k]==nums[k-1]) continue;

            int target = 0-nums[k];
            int i=k+1, j=nums.size()-1;

            while(iif(nums[i]+nums[j]==target) 
                {
                    res.push_back( {nums[k],nums[i],nums[j]} );
                    while(i1]) ++i;
                    while(i1]) --j;
                    ++i;
                    --j;
                }
                else if(nums[i]+nums[j]else if(nums[i]+nums[j]>target) --j;
            }
        }
        return res;
    }
};

T26 删除排序数组中的重复项

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

给定 nums = [0,0,1,1,1,2,2,3,3,4],函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if(nums.empty()) return 0;
        int k=0;

        for(int i=0; i//双指针i,k ,只遍历一次
        {
            if(nums[i] != nums[k])        //两个指针数字相同时,快指针i向前走一步
                nums[++k] = nums[i];      //两个指针数字不同时,都走一步
        }
        return k+1;
    }
};

T39 组合之和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。

说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。

示例 :
输入: candidates = [2,3,5], target = 8,
所求解集为:
[ [2,2,2,2], [2,3,3], [3,5] ]

返回所有符合要求解的题 十有八九都是要利用到递归

class Solution {
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int> > res;
        vector<int> vec;

        sort(candidates.begin(), candidates.end());

        combinationSumDFS(candidates, target, 0, vec, res);
        return res;
    }

    // {2,3,5} 8 ; [2,2,2,2],[2,3,3],[3,5]
    void combinationSumDFS(vector<int>& candidates, int target, int start, 
                           vector<int>& vec, vector<vector<int> >& res)
    {
        if(target<0) return;
        else if(target==0) res.push_back(vec);
        else
        {
            for(int i=start; i

T62 不同路径

一个机器人位于一个 m x n 网格的左上角
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(

问总共有多少条不同的路径?
LeetCode—数组_第1张图片
例如,上图是一个7 x 3 的网格。有多少可能的路径?
说明:m 和 n 的值均不超过 100。

示例 1:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右

示例 2:
输入: m = 7, n = 3
输出: 28

跟跳台阶一样,需要动态规划DP来解

我们可以维护一个二维数组dp,其中dp[i][j]表示到当前位置不同的走法的个数,然后可以得到递推式为: dp[i][j] = dp[i - 1][j] + dp[i][j - 1]

这里为了节省空间,我们使用一维数组dp,一行一行的刷新也可以,代码如下:

class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<int> dp(n,1);
        for(int i=1;ifor(int j=1;j1];
        }
        return dp[n-1];
    }
};

T64 最小路径和

给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

示例:
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。

这应该算是DP问题中比较简单的一类,我们维护一个二维的dp数组,其中dp[i][j]表示当前位置的最小路径和,
递推式也容易写出来 dp[i][j] = grid[i][j] + min(dp[i - 1][j],dp[i][j-1])

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        int m=grid.size();
        int n=grid[0].size();
        int dp[m][n];
        dp[0][0] = grid[0][0];

        for(int i=1;i0] = grid[i][0]+dp[i-1][0];
        for(int j=1;j0][j] = grid[0][j]+dp[0][j-1];

        for(int i=1;ifor(int j=1;j1][j],dp[i][j-1]);
        }
        return dp[m-1][n-1];
    }
};

T78 子集

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]

最开始我在想的时候,是想按照子集的长度由少到多全部写出来,比如子集长度为0的就是空集,空集是任何集合的子集,满足条件,直接加入。下面长度为1的子集,直接一个循环加入所有数字,子集长度为2的话可以用两个循环,但是这种想法到后面就行不通了,因为循环的个数不能无限的增长,所以我们必须换一种思路。

我们可以一位一位的网上叠加,比如对于题目中给的例子[1,2,3]来说,最开始是空集,那么我们现在要处理1,就在空集上加1,为[1],现在我们有两个自己[]和[1],下面我们来处理2,我们在之前的子集基础上,每个都加个2,可以分别得到[2],[1, 2],那么现在所有的子集合为[], [1], [2], [1, 2],同理处理3的情况可得[3], [1, 3], [2, 3], [1, 2, 3], 再加上之前的子集就是所有的子集合了,代码如下:

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int> > res(1);
        sort(nums.begin(), nums.end());

        for(int i=0; iint size = res.size();

            for(int j=0; j//将res内已有的加在后面
                res.back().push_back(nums[i]);   //再在后面添加新元素
            }
        }
        return res;
    }
};

整个添加的顺序为:

[]
[1]
[2]
[1 2]
[3]
[1 3]
[2 3]
[1 2 3]

T105 从前序与中序遍历序列构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:

3
/ \
9 20
—- / \
—- 15 7

由于先序的顺序的第一个肯定是根,所以原二叉树的根节点可以知道,题目中给了一个很关键的条件就是树中没有相同元素,有了这个条件我们就可以在中序遍历中也定位出根节点的位置,并以根节点的位置将中序遍历拆分为左右两个部分,分别对其递归调用原函数。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        return build(preorder,0,preorder.size()-1,
                     inorder, 0,inorder.size()-1);
    }

    TreeNode* build(vector<int>& preorder, int preStart, int preEnd,
                    vector<int>& inorder,  int inStart,  int inEnd)
    {
        if(preStart>preEnd || inStart>inEnd) return NULL;

        int i=0;
        for(i=inStart; i<=inEnd; i++)
            if(preorder[preStart] == inorder[i]) break;  // 在中序遍历中,找到根节点位置i,break 
                                                         // 中序遍历,i左侧是根节点的左子树,i右侧是根节点的右子树

        TreeNode* cur = new TreeNode(preorder[preStart]); // 根节点是前序遍历的,第一个元素
        cur->left  = build(preorder, preStart+1, preStart+i-inStart, inorder, inStart, i-1);  // i-inStart是根节点左子树的节点个数
        cur->right = build(preorder, preStart+i-inStart+1, preEnd, inorder, i+1, inEnd);

        return cur;
    }
};

T106 从中序与后序遍历序列构造二叉树

由于后序最后一个肯定是根,所以原二叉树的根节点可以知道

在中序遍历中也定位出根节点的位置,并以根节点的位置将中序遍历拆分为左右两个部分,分别对其递归调用原函数

class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        return build(inorder,0,inorder.size()-1, postorder,0,postorder.size()-1);
    }

    TreeNode* build(vector<int>& inorder,   int inStart, int inEnd,
                    vector<int>& postorder, int postStart, int postEnd)
    {
        if(inStart>inEnd || postStart>postEnd) return NULL;
        int i=0;
        for(i=inStart; iif(inorder[i]==postorder[postEnd]) break;

        TreeNode* cur = new TreeNode(postorder[postEnd]);
        cur->left  = build(inorder,inStart,i-1, postorder,postStart, postStart+i-inStart-1);
        cur->right = build(inorder,i+1,inEnd, postorder,postStart+i-inStart,postEnd-1);

        return cur;
    }
};

T189 旋转数组

给定一个数组,将数组中的元素向右移动 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]

说明:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
要求使用空间复杂度为 O(1) 的原地算法。

方案1:复制一个和nums一样的数组,然后利用映射关系i -> (i+k)%n来交换数字

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        vector<int> t = nums;

        for(int i=0;i

方案二

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        if (nums.empty() || (k %= nums.size()) == 0) return;
        int n = nums.size();

        for (int i = 0; i < n - k; ++i) {
            nums.push_back(nums[0]);
            nums.erase(nums.begin());
        }
    }
};

T628 三个数的最大乘积

给定一个整型数组,在数组中找出由三个数组成的最大乘积,并输出这个乘积。

示例 1:
输入: [1,2,3]
输出: 6

示例 2:
输入: [1,2,3,4]
输出: 24

注意:
给定的整型数组长度范围是[3,10^4],数组中所有的元素范围是[-1000, 1000]。
输入的数组中任意三个数的乘积不会超出32位有符号整数的范围

class Solution {
public:
    int maximumProduct(vector<int>& nums) {
        int n=nums.size();
        sort(nums.begin(),nums.end());
        int p=nums[0]*nums[1]*nums[n-1];
        return max(p, nums[n-1]*nums[n-2]*nums[n-3]);
    }
};

T485 最大连续1的个数

给定一个二进制数组, 计算其中最大连续1的个数。

示例 1:
输入: [1,1,0,1,1,1]
输出: 3
解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3.

class Solution {
public:
    int findMaxConsecutiveOnes(vector<int>& nums) {
        int res=0,cnt=0;
        for(int num:nums)  // 遍历数组nums,值给num
        {
            cnt = (num==0) ? 0 : cnt+1 ;
            res = max(res,cnt);
        }
        return res;
    }
};

T581 最短无序连续子数组

给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。

你找到的子数组应是最短的,请输出它的长度。

示例 1:

输入: [2, 6, 4, 8, 10, 9, 15]
输出: 5
解释: 你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
说明 :

输入的数组长度范围在 [1, 10,000]。
输入的数组可能包含重复元素 ,所以升序的意思是<=。

用了一个辅助数组,我们新建一个跟原数组一摸一样的数组,然后排序。从数组起始位置开始,两个数组相互比较,当对应位置数字不同的时候停止,同理再从末尾开始,对应位置上比较,也是遇到不同的数字时停止,这样中间一段就是最短无序连续子数组了

class Solution {
public:
    int findUnsortedSubarray(vector<int>& nums) {
        int n=nums.size();
        int i=0;
        int j=n-1;
        vector<int> res=nums;

        sort(res.begin(),res.end());

        while(iwhile(j>i && nums[j]==res[j]) --j;

        return j-i+1;
    }
};

你可能感兴趣的:(LeetCode)