【算法】数组常见算法

【算法】—— 数组常见算法

1. 移除元素

1.1 问题描述

​ 给你一个数组nums和一个值val,你需要原地移除所有数值等于val的元素,并返回移除后数组的新长度。

​ 不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

​ 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]

1.2 实现思路

​ 使用快慢指针,快指针遍历数组,慢指针构成移除指定元素后的数组

  1. 快指针fast与慢指针slow从0下标位置开始遍历

删除元素1

  1. fast遍历数组,遇到不是val值的,fast处的数值赋值给slow处,slow加一
    删除元素2

  2. fast遇到val值,slow不动,fast继续遍历

删除元素3

  1. 重复上述操作,直到遍历完数组

删除元素4

  1. slow指向新数组的最后一个元素,slow+1就是新数组的长度

1.3 代码实现

int removeElement(int* nums, int numsSize, int val){
    int fast = 0;
    int slow = 0;
    while (fast < numsSize)
    {
        if (nums[fast] != val)
        {
            nums[slow] = nums[fast];
            slow++;
        }
        fast++;
    }
    return slow;
}

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

2.1 问题描述

​ 给你一个 升序排列 的数组nums,请你原地删除重复出现的元素,使每个元素只出现一次 ,返回删除后数组的新长度。元素的相对顺序应该保持 一致 。

​ 由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么nums的前 k 个元素应该保存最终结果。

​ 将最终结果插入nums的前 k 个位置后返回 k 。

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

示例:

输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]

2.2 实现思路

依然使用快慢指针的方式实现,快指针遍历数组,慢指针构成新数组

  1. 快指针fast从1号位置开始,慢指针slow从0号位置开始

重复1

  1. fastslow进行对比,若是相等,则fast加一

重复2

  1. 若是不相等,则slow加一,并得到fast的值

重复3

  1. 重复上述操作,直到数组遍历完成

重复4

  1. slow指向新数组的最后一个元素,slow+1就是新数组的长度

2.3 代码实现

int removeDuplicates(int* nums, int numsSize){
    int fast = 1;
    int slow = 0;
    while (fast < numsSize)
    {
       if (nums[fast] != nums[slow])
       {
           nums[slow+1] = nums[fast];
           slow++;
       }
       fast++;
    return slow+1;
}

3. 合并有序数组

3.1 问题描述

​ 给你两个升序排列的整数数组nums1nums2,另有两个整数 m 和 n ,分别表示nums1nums2中的元素数目。

​ 请你合并nums2nums1中,使合并后的数组同样按升序排列。

示例:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]

3.2 实现思路

方法一(开辟空间)

​ 创建m+n大小的数组,依次对nums1nums2中的元素遍历对比,将较小值存入新数组,比较完成后将剩余的数组全部复制到新数组中,并将新数组返回。很好理解,代码如下,这里就不细嗦了。

  • 时间复杂度: O ( n + m ) O(n+m) O(n+m)
  • 空间复杂度: O ( m + n ) O(m+n) O(m+n)

代码实现

int* merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
	int* nums = (int*)malloc((m+n)*sizeof(int));
    int i = 0;
    int j = 0;
    int k = 0;
    while (i<m && j<n)
    {
        if (nums1[i] < nums2[j])
        {
            nums[k] = nums1[i];
            i++;
        }
        else
        {
            nums[k] = nums2[j];
            j++;
        }
        k++;
    }
    
    while (i<m)
    {
        nums[k] = nums1[i];
        i++;
        k++;
    }
    while (j<n)
    {
        nums[k] = nums2[j];
        j++;
        k++;
    }
    
    return nums;
}

方法二(原地合并数组)

​ 若是从前往后遍历,则每一次插入都要移动大量数据,所以我们不走寻常路,从后往前遍历,大的往后放

  1. 定义新数组的指针k,让他指向合并数组的末尾,就是m+n-1下标处(m、n分别是两个数组的长度),指针ij指向两数组末尾

原地1

  1. 比较ij处数据的大小,将大的挪到k处,并指向下一个要比较的数,k也向前挪动一格

原地2

  1. 重复上述操作,直到其中的一个数组遍历完,此时数组num1遍历完毕

原地3

  1. 此时num2还没遍历完,将剩余的数据一定是最小的,全部插入到num1

原地4

原地5

  1. 若是nums1剩余则已经是最小值在数组中了,不用处理,最后将数组长度返回即可

代码实现

int* merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
	int i = m - 1;
    int j = n - 1;
    int k = m + n - 1;
    while (i>=0 && j>=0)
    {
        if (nums1[i] > nums2[j])
        {
            nums1[k] = nums1[i];
            i--;
        }
        else
        {
            nums1[k] = nums2[j];
            j--;
        }
        k--;
    }
    
    while (j>=0)
    {
        nums1[k] = nums2[j];
        k--;
        j--;
    }
    
    return nums1;
}

4. 旋转数组

4.1 问题描述

​ 给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

示例 1:

输入: nums = [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:

输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释: 
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]

4.2 实现思路

方法一(一个一个移)

一次移动一个元素,一共移动k次,代码如下

void rotate(int* nums, int numsSize, int k)
{
    k = k % numsSize;
    
    int i = 0;
    for (i=0; i<k; i++)			//进行k次移动
    {
        //所有元素移动一格
        int temp = nums[numsSize-1];
        int j = 0;
        for (j=0; j<numsSize-1; j++)
        {
            nums[j+1] = nums[j];
        }
        nums[0] = temp;
    }
}

方法二(三步反转法)

0 ~ k-1的数据进行反转,再对k ~ numsSize-1的数据进行反转,最后对整个数组来个反转,就得到了旋转数组

当k=3时,对 [1, 2, 3, 4, 5, 6, 7, 8] 反转:

  1. 将 0~2 的数据进行反转

反转1

  1. 将 3~7 的元素进行反转

反转2

  1. 将 0~7的元素进行反转,得到旋转后的数组

反转3

代码实现

//反转数组
void reverse(int *nums, int left, int right)
{
    while (left < right)
    {
        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
        ++left;
        --right;
    }
}

//旋转数组
void rotate(int* nums, int numsSize, int k){
    if (numsSize < 2)
    {
        return;
    }

    k = k % numsSize;
    reverse(nums, 0, numsSize-1-k);
    reverse(nums, numsSize-k, numsSize-1);
    reverse(nums, 0, numsSize-1);
}
  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值