刷题 | 数组移动元素

Posted 就良同学

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了刷题 | 数组移动元素相关的知识,希望对你有一定的参考价值。

题目描述

LeetCode.283. 移动零

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

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例 1:

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

示例 2:

输入: nums = [0]
输出: [0]

提示:

1 <= nums.length <= 104
-231 <= nums[i] <= 231 - 1


解法1

算法思想:

  1. 创建一个数组tmps用于存储nums中所有的非零元素(按原有顺序存储),遍历nums,将非零元素存于tmps中。
  2. 遍历tmps,将tmps中元素覆盖nums元素(从0号下标开始依次覆盖)。
  3. 将nums剩余位置元素置零。

时间复杂度=O(n)

空间复杂度=O(n)

代码实现:

#include <iostream>
#include <vector>

using namespace std;

class Solution 
public:
    void moveZeroes(vector<int>& nums) 
        vector<int> tmps;
        int i;
        for(i = 0; i < nums.size(); i++)
            if(nums[i] != 0)
                tmps.push_back(nums[i]);
            
        
        for(i = 0; i < tmps.size(); i++)
            nums[i] = tmps[i];
        
        for(; i < nums.size(); i++)
            nums[i] = 0;
        
    
;

int main()

   int arr[] = 0, 1, 0, 3, 12;
   vector<int> vec(arr, arr + sizeof(arr) / sizeof(int));
   Solution().moveZeroes(vec);
   for(int i = 0; i < vec.size(); i++)
    cout << vec[i] << " ";
   
   cout << endl;
   system("pause");
   return 0;

运行效果:

1 3 12 0 0

解法2

算法思想:

原地操作,不开辟新的内存空间

  1. 声明两个指针i和k,i:用于遍历数组,k:保证[0...k)中保存所有当前已遍历过的所有非0元素;

    即:遍历完第i个元素后,[0...i]中所有非0元素都按照原有顺序排列再[0...k)中。

  2. 若当前元素nums[i]为0元素,忽略,继续向前遍历;

  3. 若当前元素nums[i]为非0元素,将该元素插入到nums[k]的位置,然后i++,k++;

  4. 重复上述操作,直至指针i遍历结束;

  5. 从位置k开始遍历数组,将元素值置零。

代码实现:

class Solution 
public:
    void moveZeroes(vector<int>& nums) 
        int i, k = 0;
        for(i = 0; i < nums.size(); i++)
            if(nums[i] == 0)
                continue;
            else
                nums[k++] = nums[i];
            
        
        for(i = k; i < nums.size(); i++)
            nums[i] = 0;
        
    
;

相当于把数组nums分成左右两部分:

  • 左部分:已处理元素集合,存储数组中已经处理完毕的非零元素(处理完毕即元素均为非0元素且按原有顺序排列),左右边界为[0...k),初始时k=0代表左部分为空;
  • 右部分:待处理元素集合,左右边界为[k...nums.size()-1],初始时k=0代表整个数组。

解法3:

算法思想:

在解法2的基础上进一步优化,去掉解法2最后一步将数组剩余元素置零的操作。

  1. 声明两个指针i和k,i:用于遍历数组,k:保证[0...k)中保存所有当前已遍历过的所有非0元素;

    即:遍历完第i个元素后,[0...i]中所有非0元素都按照原有顺序排列再[0...k)中。

  2. 若当前元素nums[i]为0元素,忽略,继续向前遍历;

  3. 若当前元素nums[i]为非0元素,将该元素与nums[k]对应元素交换位置,然后i++,k++;

  4. 重复上述操作,直至指针i遍历结束;

代码实现:

class Solution 
public:
    void swap(int &a, int &b)
        int tmp = a;
        a = b;
        b = tmp;
    
    void moveZeroes(vector<int>& nums) 
        int i, k = 0;
        for(i = k; i < nums.size(); i++)
            if(nums[i] == 0)
                continue;
            else
                swap(nums[k++], nums[i]);
            
        
    
;

进一步优化:

若数组nums全是非零元素,按照上述代码,每个元素(均为非0元素)都会和自己执行一次交换,耗时。

class Solution 
public:
    void swap(int &a, int &b)
        int tmp = a;
        a = b;
        b = tmp;
    
    void moveZeroes(vector<int>& nums) 
        int i, k = 0;
        for(i = k; i < nums.size(); i++)
            if(nums[i] == 0)
                continue;
            else
                if(nums[k] == 0)
                    swap(nums[k++], nums[i]);
                else
                    k++;
                
            
        
    
;

相似题目

leetcode.27. 移除元素

class Solution 
public:
    int removeElement(vector<int>& nums, int val) 
        int k = 0;
        for(int i = k; i < nums.size(); i++)
            if(nums[i] != val)
                nums[k++] = nums[i];
            
        
        return k;
    
;

leetcode.26. 删除有序数组中的重复项

/*
算法思想:
    声明指针k,将数组划分成两部分:nums[0...k)为已处理序列,nums[k...]为待处理序列,k初始时为0
    声明指针i遍历数组:
        1. 若已处理序列为空(即k==0),则将当前元素nums[i]插入到k位置,并执行k++;
        2. 若已处理序列非空,将当前元素nums[i]与已处理序列尾元素nums[k-1]比较:
            (1)若二者相等,忽略当前元素,继续遍历;
            (2)若二者不等,将当前元素nums[i]插入到k位置,并执行k++。
*/
class Solution 
public:
    int removeDuplicates(vector<int>& nums) 
        int k = 0;
        for(int i = 0; i < nums.size(); i++)
            if(k == 0)
                nums[k++] = nums[i];
            else
                if(nums[i] != nums[k-1])
                    nums[k++] = nums[i];
                
            
        
        return k;
    
;

leetcode.80. 删除有序数组中的重复项 II

将leetcode.26中的nums[i] != nums[k-1]改为nums[i] != nums[k-2]即可。

class Solution 
public:
    int removeDuplicates(vector<int>& nums) 
        int k = 0;
        for(int i = 0; i < nums.size(); i++)
            if(k == 0)
                nums[k++] = nums[i];
            else
                if(nums[i] != nums[k-2])
                    nums[k++] = nums[i];
                
            
        
        return k;
    
;

LeetCode刷题(127)~最小移动次数使数组元素相等动态规划|数学


题目描述

给定一个长度为 n 的非空整数数组,找到让数组所有元素相等的最小移动次数。每次移动将会使 n - 1 个元素增加 1。

示例:

输入:
[1,2,3]

输出:
3

解释:
只需要3次移动(注意每次移动会增加两个元素的值):

[1,2,3] => [2,3,3] => [3,4,3] => [4,4,4]

解答

Demo【n-1个元素都+1相对于 一个元素-1】

int minMoves(vector<int>& nums) 
sort(nums.begin(),nums.end());
int count=0;
for(int i=nums.size()-1;i>0;--i)
count+=nums[i]-nums[0];
return count;

运行结果

LeetCode刷题(127)~最小移动次数使数组元素相等【动态规划|数学】_数组


Demo

int minMoves(vector<int>& nums) 
int ans=0;
int a=INT_MAX;
for(int num:nums)
a=min(num,a);
for(int num:nums)
ans+=num-a;
return ans;

运行结果

LeetCode刷题(127)~最小移动次数使数组元素相等【动态规划|数学】_leetcode_02


Demo(动态规划)

int minMoves(vector<int>& nums) 
sort(nums.begin(),nums.end());
int moves=0;//总的移动数量
for(int i=1;i<nums.size();++i)

int diff=moves+nums[i]-nums[i-1];//每次的移动数
nums[i]+=moves;
moves+=diff;

return moves;

运行结果

LeetCode刷题(127)~最小移动次数使数组元素相等【动态规划|数学】_数组_03

题目来源

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-moves-to-equal-array-elements


以上是关于刷题 | 数组移动元素的主要内容,如果未能解决你的问题,请参考以下文章

Leetcode刷题笔记-数组

刷题笔记(数组)-01

Leetcode刷题100天—283. 移动零(双指针)—day20

Leetcode刷题100天—283. 移动零(双指针)—day20

leetcode刷题记录——数组与矩阵

Leetcode刷题Python26. 删除有序数组中的重复项