首页 > 其他分享 >数组

数组

时间:2025-01-22 15:54:54浏览次数:1  
标签:arr int 元素 数组 public size

数组

  1. 静态数组:是一块连续的内存空间,可以通过索引来访问这块内存空间的元素,是数组的原始形态

  2. 动态数组:是编程语言为了我们方便使用,在静态数组的基础上添加了常用的api,如push、insert、remove等方法,可以让我们不用写代码去实现操作

定义一个静态数组

package com.wang.base.array;

public class Demo01 {
    //少了psvm
    //定义一个大小为10的静态数组和名字为arr的数组指针,指向这段内存空间的首地址
    int[] arr=new int[10];//在内存中开辟了一段连续的内存空间,大小是10*4个字节
    
    //使用索引赋值
    arr[0]=1;
    arr[1]=2;
    
    //使用索引取值
    int a=arr[0];
    
}

int arr[10]语句中我们可以知道:

  1. 内存空间的首地址:arr指向首地址
  2. 每个元素类型:int,每个元素占用内存空间大小为4个字节
  3. 这块空间是连续的,大小为10*4=40个字节

所以我们获得了数组的超能力:随机访问,只要给定任何一个数组索引,我们可以在O(1)时间内直接获取到对应元素的值

O(1)是因为计算机的内存寻址时间可以认为是O(1)

增:给静态数组增加元素

情况一:数组末尾追加(append)元素

package com.wang.base.array;

public class Demo01 {
    public static void main(String[] args) {
        //大小为10的数组已经装了4个元素
        int[]arr=new int[10];
        for (int i = 0; i < 4; i++) {
            arr[i]=i;
        }
        //现在在数组末尾追加元素4和5
        arr[4]=4;
        arr[5]=5;
        //以此类推。。。
    } 
}

由于只是对索引赋值,所以在数组末尾追加元素的时间复杂度是O(1)

情况二:数组中间插入(insert)元素

package com.wang.base.array;

public class Demo01 {
    public static void main(String[] args) {
        //大小为10的数组已经装了4个元素
        int[]arr=new int[10];
        for (int i = 0; i < 4; i++) {
            arr[i]=i;
        }
        //想在第三个位置插入元素666,即arr【2】=666
        //需要把第三个位置及后面的元素都向后移动一位
        //注意要倒着遍历数组中已有元素,避免覆盖
        for (int i = 4; i > 2; i--) {
            arr[i]=arr[i-1];
        }
        arr[2]=666;       
    }
}

综上,在数组中间插入元素的时间复杂度是O(N),因为涉及到数据搬迁,给新元素腾地方

情况三:数组空间已满

连续空间必须一次性分配,分配好之后不能随意删减(因为后面可能已经被其他程序占用了,不能说你想要就给你)

所以只能重新申请更大一块的空间,把原来的数据复制过去,再加入新元素,这就是数组的扩容操作

package com.wang.base.array;

public class Demo01 {
    public static void main(String[] args) {
        //大小为10的数组已经装满
        int[]arr=new int[10];
        for (int i = 0; i < 10; i++) {
            arr[i]=i;
        }
        //现在想在末尾追加元素10
        //先创建一个空间更大的数组
       int[]newArr=new int[20];
        //把原来的数据移进去
        for (int i = 0; i < 10; i++) {
            newArr[i]=i;}
        //再在末尾加入元素
        newArr[10]=10;
    }
}

数组的扩容操作会涉及到新数组的开辟和数据的复制,时间复杂度是O(N)

删:删除静态数组的元素

情况一:删除末尾元素

把末尾元素标记成一个特殊值代表已删除

package com.wang.base.array;

public class Demo01 {
    public static void main(String[] args) {
        //大小为10的数组已经装满5个元素
        int[]arr=new int[10];
        for (int i = 0; i < 5; i++) {
            arr[i]=i;
        }
        //删除末尾元素,暂时用-1逮捕元素已删除,在后面动态数组中会有更好解决方法
        arr[4]=-1; 
    }
    
}

时间复杂度是O(1)

情况二:删除中间元素

涉及到数据搬移,把被删元素的后面元素都往前移动一位,保持连贯性

package com.wang.base.array;

public class Demo01 {
    public static void main(String[] args) {
        //大小为10的数组已经装满5个元素
        int[]arr=new int[10];
        for (int i = 0; i < 5; i++) {
            arr[i]=i;
        }
        //想要删除arr【1】,需要把它后面的都向前移动一位
        //注意要正着遍历数组中已有元素
        for (int i = 1; i < 4; i++) {
            arr[i]=arr[i+1];}
        //最后一个元素为-1代表已经删除
        arr[4]=-1;
        }
    }

时间复杂度是O(N)

查:给定索引,查询索引对应元素的值

时间复杂度O(1)

改:给定索引,修改索引对应元素的值

时间复杂度O(1)

动态数组的增删查改

动态数组底层还是静态数组,只是自动帮我们进行数组空间的扩容缩容,并把增删改查操作进行了封装,让我们用起来更方便

package com.wang.base.array;

import java.util.ArrayList;

public class Demo02 {
    public static void main(String[] args) {
        //创建动态数组,不用显示指定大小,会根据实际存储的元素数量自动缩扩容
        ArrayList<Integer>arr=new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            //在末尾追加元素,时间复杂度O(1)
            arr.add(i);
        }
        //在中间插入元素,时间复杂度O(N)
        arr.add(2,666);//在索引为2的地方插入元素666
        
        //在头部插入元素,时间复杂度O(1)
        arr.add(0,-1);
        
        //删除末尾元素,时间复杂度O(1)
        arr.remove(arr.size()-1);
        
        //删除中间元素,时间复杂度O(N)
        arr.remove(2);//删除索引为2的元素
        
        //根据索引查询元素,时间复杂度O(1)
        int a= arr.get(0);//查询索引为0的元素
        
        //根据索引修改元素,时间复杂度O(1)
        arr.set(0,100);//把索引为0的元素修改成100
        
        //根据元素值查找索引,时间复杂度O(N)
        int index= arr.indexOf(666);//已知元素值是666,查找索引是多少
    }
}

环形数组

可以在逻辑上把数组变成环形的

package com.wang.base.array;

public class Demo03 {
    public static void main(String[] args) {
        //长度为5的数组
        int[]arr=new int[]{1,2,3,4,5};
        int i=0;
        //模拟环形数组,这个循环永远不会结束
        while (i< arr.length){
            System.out.println(arr[i]);
            i=(i+1)% arr.length;
            //当i到数组末尾元素时,i+1和arr.length取余数又会变成0,
            // 即会回到数组头部,这样就在逻辑上形成了一个环形数组  
        }
    }
}

start和end指针

环形数组的关键在于,维护了两个指针start和end,start指向第一个有效元素的索引,end指向最后一个有效元素的下一个位置索引。

这样在数组头部删除或添加元素,只需要移动start索引,而在数组尾部添加或删除元素,只需要移动end索引

当start,end移动超出数组边界(<0或>=arr.length)时,可以通过%让他们转一圈到数组头部或尾部继续工作,这样就实现了环形数组的效果

开闭区间

左开右闭:【start,end)区间包含数组元素

package com.wang.base.array;

public class Demo04 {
    public class CycleArray<T>{
        private T[]arr;
        private int start;
        private int end;
        private int count;
        private int size;
        public CycleArray(){
            this(1);
        }
        @SuppressWarnings("unchecked")
        public CycleArray(int size){
            this.size=size;
            //因为Java不支持直接创建泛型数组,所以使用了类型转换
            this.arr=(T[])new Object[size];
            //start指向第一个有效元素的索引,是闭区间
            this.start=0;
            //end指向最后一个有效元素的下一个位置索引,是开区间
            this.end=0;
            this.count=0;
        }
        //自动扩缩容辅助函数
        @SuppressWarnings("unchecked")
        public void resize(int newSize){
            //创建新的数组
            T[] newArr=(T[])new Object[newSize];
            //把旧的数组元素复制到新数组中
            for (int i = 0; i < count; i++) {
                newArr[i]=arr[(start+i)%size];
            }
            arr=newArr;
            //重置start和end指针
            start=0;
            end=count;
            size=newSize;
        }
        //在数组头部添加元素,时间复杂度O(1)
        public void addFirst(T val){
            //当数组满时,扩容为原来的两倍
            if (isFull()){
                resize(size*2);
            }
            //因为start是闭区间,所以先左移再赋值
            start=(start-1+size)%size;
            arr[start]=val;
            count++;
        }
        //删除头部元素,时间复杂度O(1)
        public  void removeFirst(){
            //防止数组是空的,没有元素,无法删减出现错误
            if (isEmpty()){
                throw new IllegalStateException("Array is empty");
                
            }
            //因为start是闭区间,所以先赋值再右移
            arr[start]=null;
            start=(start+1)%size;
            count--;
            //如果元素数量减少到原大小的四分之一,则减小数组大小为一半
            if (count>0&&count==size/4){
                resize(size/2);
            }
        }
        //在数组尾部增加元素,时间复杂度O(1)
        public void addLast(T val){
            //防止数组是空的,没有元素,无法删减出现错误
            if (isFull()){
                resize(size*2);
            }
            //因为end是开区间,所以先赋值后右移
            arr[end]=val;
            end=(end+1)%size;
            count++;
        }
        //删除数组尾部元素,时间复杂度O(1)
        public void removeLast(){
            //防止数组是空的,没有元素,无法删减出现错误
            if (isEmpty()){
                throw new IllegalStateException("Arry is empty");
            }
            //因为end是开区间,所以先左移,再赋值
            end=(end-1+size)%size;
            arr[end]=null;
            count--;
            //缩容
            if (count>0&&count==size/4){
                resize(size/2);
            }
        }
        //获取数组头部元素,时间复杂度O(1)
        public T getFirst(){
            //防止数组是空的,没有元素,无法删减出现错误
            if (isEmpty()){
                throw new IllegalStateException("Arry is empty");
            }
            return arr[start];
        }
        //获取数组尾部元素,时间复杂度O(1)
        public T getLast(){
            //防止数组是空的,没有元素,无法删减出现错误
            if (isEmpty()){
                throw new IllegalStateException("Arry is empty");
            }
            //end是开区间,指向的是下一个元素的位置,所以要减一 
            return arr[(end-1+size)%size];
        }
        public boolean isFull(){
            return count==size;
        }
        public int size(){
            return count;
        }
        public boolean isEmpty(){
            return count==0;
        }
    }
}

标签:arr,int,元素,数组,public,size
From: https://www.cnblogs.com/redstar-190601/p/18686219

相关文章

  • 《零基础Go语言算法实战》【题目 7-4】删除数组重复项,使每个元素只出现一次并返回新的
    《零基础Go语言算法实战》【题目7-4】删除数组重复项,使每个元素只出现一次并返回新的长度给定一个排序数组array,就地删除重复项,使每个元素只出现一次并返回新的长度。不要为另一个数组分配额外的空间,开发者必须通过使用空间复杂度为O(1)的额外内存就地修改输入数组来做到......
  • 从一个无序的整数数组中,找出最小和最大数之间缺失的数字,要求最小的时间复杂度
    为了找出无序整数数组中最小和最大数之间缺失的数字,我们首先需要确定最小和最大的数字。这可以通过遍历数组一次来实现,时间复杂度为O(n),其中n是数组的长度。一旦我们有了最小和最大的数字,我们可以检查它们之间的所有数字是否都存在于数组中。但是,如果直接遍历检查每个数字,时间复......
  • 零数组变换II
    思路对一段区间加减并且进行多次操作可以联想到差分算法,并且queries数组比较大,可以使用二分查找加快效率。这里的代码个人觉得lambda表达式更加简洁代码intminZeroArray(vector<int>&nums,vector<vector<int>>&queries){intn=nums.size(),q=queries.size();......
  • 2090. 半径为 k 的子数组平均值
    【题目】:2090.半径为k的子数组平均值classSolution{public:vector<int>getAverages(vector<int>&nums,intk){vector<int>res(nums.size(),-1);longcurSum=0;//记录当前滑窗内的数值和for(intl=0,r=0;r<nums.s......
  • 33. 搜索旋转排序数组
    整数数组nums按升序排列,数组中的值互不相同。在传递给函数之前,nums在预先未知的某个下标k(0<=k<nums.length)上进行了旋转,使数组变为[nums[k],nums[k+1],...,nums[n-1],nums[0],nums[1],...,nums[k-1]](下标从0开始计数)。例如,[0,1,2,4,5,6,7]在下标3处经......
  • C语言中的二维数组
    1.二维数组的定义类型说明符数组名 [常量表达式][常量表达式];(1).类型说明符      表示二维数组中数据元素的类型 (2).数组名          标识符 (3).[常量表达式][常量表达式]      第1维       第2维   ......
  • 树状数组
    Question01[P3374树状数组一]模板题Code#include<bits/stdc++.h>usingnamespacestd;constintN=5e5+7;classTree{ public: inlinevoidscan(longlong*_data,int_size){ size=_size; for(inti=1;i<=size;i++)_data[i]+=_data[i-1]; for(inti......
  • 5、原来可以这样理解C语言_数组
    目录​编辑1.数组的概念2.⼀维数组的创建和初始化2.1数组创建⼀维数组创建的基本语法如下:2.2数组的初始化2.3数组的类型3.⼀维数组的使⽤ 3.1数组下标3.2数组元素的打印3.3数组的输⼊4.⼀维数组在内存中的存储5.sizeof计算数组元素个数6.⼆维数组......
  • 树状数组
    l(x)=x-lowbit(x)+1。即,l(x)是c[x]管辖范围的左端点。对于任意正整数x,总能将x表示成s*2^{k+1}+2^k的形式,其中lowbit(x)=2^k。下面「c[x]和c[y]不交」指c[x]的管辖范围和c[y]的管辖范围不相交,即[l(x),x]和[l(y),y]不相交。「c[x]包含于c[y]」......
  • 剑指offer面试题3:数组中重复的数字(Python实现)
    """面试题3:数组中重复的数字在一个长度为n的数组里所有数字都在0~n-1的范围内,某些数字是重复的,找出任意一个重复的数字"""defduplicate1(numbers:list,length:int)->int:"""修改原数组"""ifnumbers==[]orlength<=0:......