算法基础-数组

0 数组

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

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++;
      }
    }
}

Guess you like

Origin blog.csdn.net/huangshanchun/article/details/121190926