算法基础-数组

本文深入探讨了数组和动态数组的概念,分析了Java中数组的访问、插入和删除的时间复杂度。讨论了动态数组如ArrayList和vector的扩容与缩容策略,并通过实例解释了插入元素时的拷贝次数和均摊复杂度。此外,针对LeetCode上的三个问题——合并有序数组、有序数组去重及移动零,提供了解决方案,并分析了空间复杂度和优化方法。通过这些案例,阐述了如何在有限的空间内高效地处理数组操作。
摘要由CSDN通过智能技术生成

0 数组

数组(array)是一种线性表数据结构,它用一组连续的内存空间来存储一组具有相同类型的数据。基本特点:支持随机访问,索引与寻址。java 中 int[] arr=new int[100]; 时间复杂度:
Access:O(1)Insert和Delete:平均O(n)。

1 动态数组

所谓动态数组就是其空间会随着数据量变化而变化(扩容或者缩容),比如java ArrayList C++ vector等都是动态数组。如果要实现一个动态数组需要怎么做?1)支持随机访问+寻址 2)空间怎么分配,怎么动态扩容,怎么缩容等
扩容做法:如果空间 不够,重新申请2倍空间大小,将数据拷贝新空间,然后释放旧空间
缩容做法:若空间利用率不到25%,则释放空间

在空数组中连续插入n个元素,来分析一下拷贝次数,数组最终长度2n,其由空间n->2n,拷贝n次数据,n是由n/2扩容到n,需要拷贝n/2 次,依次类推可以得出:总拷贝次数=n+n/2+n/4+…<2n的,可以看插入n个元素的均摊复杂度为o(1)
一次扩容到下一次释放,至少需要再删除0.5n次,比如刚扩容2n,那么至少删除0.5n数据才回触发缩容2n->n。思考一下扩容是2倍扩容,缩容为啥不是1/2空间阈值缩容。如果这么做话可能在临界情况插入和删除交替的话,就会触发扩容、缩容。

2 LeetCode 实战

合并有序数组 https://leetcode-cn.com/problems/merge-sorted-array/submissions/

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

主题思路:开辟一个新的数组,两个索引指针i,j 依次扫描数组数据,谁小先放谁
细节:需要考虑边界问题
具体代码如下。

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        if(nums2==null || n<=0) {
            return;
        }
        int [] tmp=new int[m+n];
        int i=0,j=0,k=0;
        while(k<m+n){
           //处理边界问题
           if(j>=n||(i<m && nums1[i]<nums2[j])) {
               tmp[k++]=nums1[i++];
           }else{
              tmp[k++]=nums2[j++];
           }
        }
        k=0;
        while(k<m+n){
            nums1[k]=tmp[k];
            k++;
        }  
    }
}

上面解法整体思路是没有问题,但是从空间复杂度耗费了o(m+n),有没有可以优化空间呢?由于最终数据是合并num1中,考虑能不能倒着放,先处理大的。

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        if(nums2==null || n<=0) {
            return;
        }
        int i=m-1,j=n-1,k=m+n-1;
        while(k>=0) {
           if(j<0||(i>=0 && nums1[i]>nums2[j])) {
               nums1[k--]=nums1[i--];
           }else{
              nums1[k--]=nums2[j--];
           }
        }
    }
}


有序数组去重 https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。

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

主题思路:从头到尾扫描整个数组,如果这个元素和前一元素是否一样,不一样才保留
细节:边界和数组覆盖

```java
class Solution {
    public int removeDuplicates(int[] nums) {
        int i=0,k=0;
        for(i=0;i<nums.length;i++){
            //边界
            if(i==0||nums[i]!=nums[i-1]) {
                nums[k++]=nums[i];
            }
        }
        return k;
    }
}

移动零 https://leetcode-cn.com/problems/move-zeroes/

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

示例:

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

主题思路:从头到尾扫描数组,保留要的元素,放到数组前面,然后把尾部数据处理成0
细节:边界

class Solution {
    public void moveZeroes(int[] nums) {
          int i=0,k=0;
          for(;i<nums.length;i++){
            if(nums[i]!=0){
                nums[k++]=nums[i];
            }
        }
        while(k<nums.length){
            nums[k++]=0;
        }
    }
}

或者使用双指针法

class Solution {
    public void moveZeroes(int[] nums) {
      int i=0,j=0,len=nums.length;
      while(j<len){
          if(nums[j]!=0) {
              if(i!=j){
                nums[i]=nums[j];
                nums[j]=0;
              } 
              i++;
          }
          j++;
      }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值