关于数组的几道算法面试题

打乱数组

打乱一个没有重复元素的数组。

示例:

// 以数字集合 1, 2 和 3 初始化数组。 int[] nums = {1,2,3}; Solution solution = new Solution(nums); // 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。 solution.shuffle(); // 重设数组到它的初始状态[1,2,3]。 solution.reset(); // 随机返回数组[1,2,3]打乱后的结果。 solution.shuffle();

class Solution
{
    vector<int> source;

  public:
    Solution(vector<int> nums)
    {
        source = nums;
    }

    vector<int> reset()
    {
        return source;
    }

    vector<int> shuffle()
    {
        vector<int> shuffleArr = source;
        for (int i = 0; i < shuffleArr.size(); i++)
        {
            int x = rand() % shuffleArr.size();
            swap(shuffleArr[i], shuffleArr[x]);
        }
        return shuffleArr;
    }
};

递增的三元子序列

给定一个未排序的数组,判断这个数组中是否存在长度为 3 的递增子序列。

数学表达式如下:

如果存在这样的 i, j, k,  且满足 0 ≤ i < j < k ≤ n-1,

使得 arr[i] < arr[j] < arr[k] ,返回 true ; 否则返回 false 。

说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1) 。

示例 1:

输入: [1,2,3,4,5] 输出: true

示例 2:

输入: [5,4,3,2,1] 输出: false

class Solution
{
  public:
    bool increasingTriplet(vector<int> &nums)
    {
        int min1 = __INT_MAX__;
        int min2 = __INT_MAX__;
        for (int i = 0; i < nums.size(); i++)
        {
            if(nums[i] <= min1)
            {
                min1 = nums[i];
            }
            else if(nums[i] <= min2)
            {
                min2 = nums[i];
            }
            else
            {
                return true;
            }
            
        }
        return false;
    }
};

除自身以外数组的乘积

给定长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。

示例:

输入: [1,2,3,4] 输出: [24,12,8,6]

说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。

进阶:

你可以在常数空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)

 

方法一:

class Solution
{
  public:
    vector<int> productExceptSelf(vector<int> &nums)
    {
        int len = nums.size();
        vector<int> pre(len);
        vector<int> post(len);
        vector<int> ans(len);
        int cnt = nums[0];
        for (int i = 1; i < nums.size(); i++)
        {
            pre[i] = cnt;
            cnt *= nums[i];
        }
        cnt = nums[len - 1];
        for (int i = len - 2; i >= 0; i--)
        {
            post[i] = cnt;
            cnt *= nums[i];
        }
        for (int i = 0; i < len; i++)
        {
            if(i == 0)
            {
                ans[i] = post[i];
            }
            else if(i == len - 1)
            {
                ans[i] = pre[i];
            }
            else
            {
                ans[i] = pre[i] * post[i];
            }
            
        }
        return ans;
    }
};

方法二(常数空间):

走两遍,第一次完成前面的乘积。

第二次完成后面的乘积

class Solution
{
  public:
    vector<int> productExceptSelf(vector<int> &nums)
    {
        int left = 1;
        int right = 1;
        vector<int> ans(nums.size());
        for (int i = 0; i < nums.size(); i++)
        {
            ans[i] = left;
            left *= nums[i];
        }
        for (int i = nums.size() - 1; i >= 0; i--)
        {
            ans[i] *= right;
            right *= nums[i];
        }
        return ans;
    }
};

两个数组的交集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 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?

方法一:

class Solution
{
  public:
    vector<int> intersect(vector<int> &nums1, vector<int> &nums2)
    {
        map<int, int> n1;
        map<int, int> n2;
        vector<int> ans;
        for(int i : nums1)
        {
            n1[i]++;
        }
        for(int i : nums2)
        {
            n2[i]++;
        }
        for(int i : nums1)
        {
            while(n1[i] > 0 && n2[i] > 0)
            {
                ans.push_back(i);
                n1[i]--;
                n2[i]--;
            }
        }
        return ans;
    }
};

方法二(假如排好序):

class Solution
{
  public:
    vector<int> intersect(vector<int> &nums1, vector<int> &nums2)
    {
        //排序好的情况
        sort(nums1.begin(), nums1.end());
        sort(nums2.begin(), nums2.end());
        int i = 0, j = 0;
        vector<int> ans;
        while(i < nums1.size() && j < nums2.size())
        {
            if(nums1[i] == nums2[j])
            {
                ans.push_back(nums1[i]);
                i++;
                j++;
            }
            else if(nums1[i] > nums2[j])
            {
                j++;
            }
            else
            {
                i++;
            } 
        }
        return ans;
    }
};

移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12] 输出: [1,3,12,0,0]

说明:

  1. 必须在原数组上操作,不能拷贝额外的数组。
  2. 尽量减少操作次数。

 

冒泡算法改进+优化

class Solution
{
  public:
    void moveZeroes(vector<int> &nums)
    {
        for (int i = 0; i < nums.size() - 1; i++)
        {
            bool flag = false;
            for (int j = 1; j < nums.size() - i; j++)
            {
                if(nums[j - 1] == 0)
                {
                    swap(nums[j], nums[j - 1]);
                    flag = true;
                }
            }
            if(!flag)
                return;
        }
    }
};

 

存在重复元素

给定一个整数数组,判断是否存在重复元素。

如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。

示例 1:

输入: [1,2,3,1] 输出: true

示例 2:

输入: [1,2,3,4] 输出: false

示例 3:

输入: [1,1,1,3,3,4,3,2,4,2] 输出: true

class Solution
{
  public:
    bool containsDuplicate(vector<int> &nums)
    {
        if(nums.size() <= 1)
            return false;
        sort(nums.begin(), nums.end());
        for (int i = 1; i < nums.size(); i++)
        {
            if(nums[i] == nums[i - 1])
                return true;
        }
        return false;
    }
};

旋转数组

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

 

加入T代表倒置

[A(T)B(T)] = [BA]

注意k大于数组长度的情况

class Solution
{
  public:
    void rotate(vector<int> &nums, int k)
    {
        int len = nums.size();
        k = k - (k / len) * len;
        Inversion(nums, 0, len - k - 1);
        Inversion(nums, len - k, len - 1);
        Inversion(nums, 0, len - 1);
    }

    void Inversion(vector<int> &arr, int s, int e)
    {
        for (; s < e; s++, e--)
        {
            swap(arr[s], arr[e]);
        }
    }
};

 

乘积最大子序列

 

给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。

示例 1:

输入: [2,3,-2,4] 输出: 6 解释: 子数组 [2,3] 有最大乘积 6。

示例 2:

输入: [-2,0,-1] 输出: 0 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

 

最大值可能来源于:1.最大值乘正数 2.最小值乘负数

所以用两个变量来维护到第i个位置时,靠近i的前面的乘积子序列。

class Solution
{
  public:
    int maxProduct(vector<int> &nums)
    {
        if(nums.size() == 0)
            return 0;
        int _max = nums[0];
        int _min = nums[0];
        int ans = _max;
        for (int i = 1; i < nums.size(); i++)
        {
            int temp = _min;
            _min = min(min(_min * nums[i], nums[i]), _max * nums[i]);
            _max = max(max(_max * nums[i], nums[i]), temp * nums[i]);
            ans = max(_max, ans);
        }
        return ans;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值