Java数据结构----数组

目录

一、Java中的数组回顾

1、数组概括介绍

1.1.1、数组的创建 

 1.1.2、数组的操作

  1.1.3、数组的排序

 1.1.4、数组的查找

二、数组的数据结构剖析

1、 数组的基本数据结构

优点

缺点

三. leetcode实战 

1. leetcode66 加一

2. leetcode485. 最大连续 1 的个数

  3. leetcode1. 两数之和 梦开始的地方

4.  leetcode 27. 移除元素

5、 Leetcode26删除有序数组中的重复项

6.  leetcode 88. 合并两个有序数组


一、Java中的数组回顾

1、数组概括介绍

1、数组可以存放多个同一类型的数据。数组也是一种数据类型,是引用类型。

即:数 ( 数据 ) ( 一组 ) 就是一组数据。相当于容器,容纳多个同类型的元素。
2、可以通过 数组名 .length 得到数组的大小 / 长度
3、数组中存储的Java对象,实际上存放的是对象引用(内存地址),数组一旦创建,在Java中规定,长度就不能变。所有数组都有length属性,是Java自带的,用来获取元素个数
4、数组的元素的内存地址是连续的,用数组元素第一个元素的地址代表堆内存中数组对象的内存地址,数组中元素是同类型的,因此每个元素所占用内存空间是一样的,又知第一个元素的内存地址以及数组中元素内存地址是连续的一段空间,进而可以通过下标,快速得到目标元素。线性表
5、数组这种数据结构优缺点:
        优点:查/索引某个下标的元素效率较高,可以说是查找效率最高的。
                1)、每个元素的内存地址在存储空间是连续的
                2)、每一个元素的类型相同,即占用的内存空间大小一致
                3)知道第一个元素内存地址,知道每个元素占用的空间大小,又知道下标。
               故而通过一个数学表达式就可以计算出某个下标上的元素的内存地址,直接通过内存地址定位元素,检索效率最高。
        缺点:由于为了保证数组中每个元素的内存地址连续,子啊数组上随机增删数组效率低,因为随机增删数组会涉及到后面元素统一向前或者向后位移。
                数组不能存储大量数据,因为很难在内存空间上找到特别大的连续内存空间供数组使用。
        注意:数组最后一个元素的增删效率没什么影响  arr[arr.length-1]

1.1.1、数组的创建 

1、数组声明定义:        

        数据类型  数组名[]  =  new  数据类型[大小];

        int a[] = new int[5];//创建一个数组,名字是a,存放5个int类型数据

        Person[]  peaple  =  new  Person[5];

        这只是定义数组的一种方法,也可以先声明后用new方式分配内存空间

2、-静态初始化数组

        int[]  a = {2,5,7,98};

3、数组使用注意事项和细节   

  • 1) 数组是多个相同类型数据的组合,实现对这些数据的统一管理
  • 2) 数组中的元素可以是任何数据类型,包括基本类型和引用类型,但是不能混用。
  • 3) 数组创建后,如果没有赋值,有默认值
    • int 0short 0, byte 0, long 0, float 0.0,double 0.0char \u0000boolean false,String null        
  • 4) 使用数组的步骤
    • 1. 声明数组并开辟空间(new)
    • 2 .给数组各个元素赋值
    • 3. 使用数组
  • 5) 数组的下标是从 0 开始的
  • 6) 数组下标必须在指定范围内使用,否则报:下标越界异常,比如int [] arr=new int[5]; 则有效下标为 0-4。arr[5]会报运行时异常ArrayIndexOutOfBoundsException
  • 7) 数组属引用类型,数组型数据是对象(object)
4、 数组赋值机制
        1、基本数据类型赋值,这个值就是具体的数据,而且相互不影响
        2、 数组在默认情况下是引用传递,赋的值是地址。
                int[] arr1 = {1,2,3};
                int[] arr2 = arr1;
                
char[] arr1 = {'a','b','c'};
char[] arr2 = arr1;
arr1[1] = '李';
//[a, 李, c],arr1和arr2指向同一个地址空间,因此修改arr1的元素,arr2元素也变了
System.out.println(Arrays.toString(arr2));

 1.1.2、数组的操作

1、数组拷贝

        new 的方式开辟不同的数据空间,修改 arr2, 不会对 arr1 有影响.循环遍历进行拷贝。

        System.arraycopy(Object src,  int  srcPos,Object dest, int destPos,int length)        

                Object src-----原数组

                srcPos---原数组从哪个位置开始复制

                Object dest-----目标数组,要拷贝到哪个数组

                destPos----目标数组拷贝的初始位置

                int length -----拷贝多长

                

int[] arr1 = {1,2,3,4,5,6};
int[] arr2 = new int[10];
//将arr1从数组下标为1的元素 1开始拷贝5个长度到arr2的下标为0的5个元素长度
System.arraycopy(arr1,1,arr2,0,5);
//arr2:[2, 3, 4, 5, 6, 0, 0, 0, 0, 0]

2、数组反转

        

//每次交换时,对应的下标 是 arr[i] 和 arr[arr.length - 1 -i]。对称位置下标
int temp = 0;
int len = arr.length; //计算数组的长度
for( int i = 0; i < len / 2; i++) {
temp = arr[len - 1 - i];//保存
arr[len - 1 - i] = arr[i];
arr[i] = temp;
}

//逆序遍历,顺序拷贝

for(int i = arr.length - 1, j = 0; i >= 0; i--, j++) {
    arr2[j] = arr[i];
}

       逆序遍历,顺序拷贝也能完成数组反转

3、数组添加/扩容        

        思路分析:
             1. 定义初始数组 int[] arr = {1,2,3}// 下标 0-2
             2. 定义一个新的数组 int[] arrNew = new int[arr.length+1];
             3. 遍历 arr 数组,依次将 arr 的元素拷贝到 arrNew 数组
             4. 将 4 赋给 arrNew[arrNew.length - 1] = 4; 4 赋给 arrNew 最后一个元素
             5. 让 arr 指向 arrNew ; arr = arrNew; 那么 原来 arr 数组就被销毁
             6. 创建一个 Scanner 可以接受用户输入
        
Scanner myScanner = new Scanner(System.in);
//初始化数组
int[] arr = {1,2,3};
do {
    int[] arrNew = new int[arr.length + 1];
    //遍历 arr 数组,依次将 arr 的元素拷贝到 arrNew 数组
    for(int i = 0; i < arr.length; i++) {
        arrNew[i] = arr[i];
    }
    System.out.println("请输入你要添加的元素");
    int addNum = myScanner.nextInt();
    //把 addNum 赋给 arrNew 最后一个元素
    arrNew[arrNew.length - 1] = addNum;
    //让 arr 指向 arrNew, arr = arrNew;
    //输出 arr 看看效果
    System.out.println("====arr 扩容后元素情况====");
    for(int i = 0; i < arr.length; i++) {
         System.out.print(arr[i] + "\t");
    }
    //问用户是否继续
    System.out.println("是否继续添加 y/n");
    char key = myScanner.next().charAt(0);
    if( key == 'n') { //如果输入 n ,就结束
        break;
    }
}while(true);
System.out.println("你退出了添加...");

  1.1.3、数组的排序

1、内部排序:指将需要处理的所有数据都加载到内部存储器中进行排序。包括(交换式排序法、选择式排序法和插入式排序法)。

2、外部排序法:数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。包括(合并排序法和直接合并排序法)。

3、冒泡排序法      

       冒泡排序(Bubble Sorting)的基本思想是: 依次  比较  相邻的两个数,正序则不动,倒序则交换位置,如此循环,直到整个数组为有序为止。 使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。
for( int i = 0; i < arr.length - 1; i++) {//外层循环是 arr.length-1 次
    for( int j = 0; j < arr.length - 1 - i; j++) {//4 次比较-3 次-2 次-1 次
    //如果前面的数>后面的数,就交换
        if(arr[j] > arr[j + 1]) {
        temp = arr[j];
        arr[j] = arr[j+1];
        arr[j+1] = temp;
    }
}

 1.1.4、数组的查找

java 中,我们常用的查找有两种:

        1) 顺序查找

        2) 二分查找---算法中

二、数组的数据结构剖析

1、 数组的基本数据结构

1、System.arraycopy使用的基本定义

public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

  • src:源数组;
  • dest:目的数组;
  • destPos:目的数组放置的起始位置;
  • length:复制的长度.

2、动态数组

// 容量检查

private void checkAndGrow() {
        // 容量检查
        if (size == 0) {
            array = new int[capacity];
        } else if (size == capacity) {
            // 进行扩容, 1.5 1.618 2
            capacity += capacity >> 1;
            int[] newArray = new int[capacity];
            System.arraycopy(array, 0,
                    newArray, 0, size);
            array = newArray;
        }

//添加数据

/**
     * 向 [0 .. size] 位置添加元素
     *
     * @param index   索引位置
     * @param element 待添加元素
     */
    public void add(int index, int element) {
        checkAndGrow();

        // 添加逻辑
        if (index >= 0 && index < size) {
            // 向后挪动, 空出待插入位置
            System.arraycopy(array, index,
                    array, index + 1, size - index);
        }
        array[index] = element;
        size++;
    }

3、

优点

        1. 按照索引查询元素速度快
        2. 按照索引遍历数组方便

缺点

        1. 数组的大小固定后就无法扩容了
        2. 数组只能存储一种类型的数据
        3. 添加,删除的操作慢,因为要移动其他的元素。向尾部添加不影响
数组适用频繁查询,对存储空间要求不大,很少增加和删除的情况。

三. leetcode实战 

1. leetcode66 加一

题目描述:给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。

分析:

 

class Solution {
    public int[] plusOne(int[] digits) {
        for (int i = digits.length - 1; i >= 0; i--) {
            digits[i]++;
            digits[i] = digits[i] % 10;
            if (digits[i] != 0) return digits;
        }
        digits = new int[digits.length + 1];
        digits[0] = 1;
        return digits;
    }
}

2. leetcode485. 最大连续 1 的个数

leetcode485. 最大连续 1 的个数 

题目描述:给定一个二进制数组 nums , 计算其中最大连续 1 的个数。

class Solution {
    public int findMaxConsecutiveOnes(int[] nums) {
        int ans = Integer.MIN_VALUE;
        int cur = 0;
        for(int num : nums){
            if(num == 0){
                ans = Math.max(cur, ans);
                cur = 0;
            }else{
                cur++;
            }
        }
        return Math.max(cur, ans);
    }
}

  3. leetcode1. 两数之和 梦开始的地方

leetcode1. 两数之和

题目描述:  给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

分析:使用HashMap集合存储,key为数组元素,value为数组元素对应的下标。遍历数组判断map的key是否包含。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        HashMap<Integer, Integer> map = new HashMap<>();
        int[] result = new int[2];
        for(int i=0;i<nums.length;i++){
            if (map.containsKey(target - nums[i])){
                result[0] = map.get(target - nums[i]);
                result[1] = i;
                return result; 
            }
            map.put(nums[i],i);
        }
        return result;
    }
}

4.  leetcode 27. 移除元素

题目描述:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
分析:辅助指针p,i是遍历指针,不等value的时候赋值给p位置元素。

class Solution {
    public int removeElement(int[] nums, int val) {
        int p =0;
        for(int i = 0;i<nums.length;i++){
            if (nums[i] !=val){
                nums[p] = nums[i];
                p++;
            }
  
        return p;
    }
}

5、 Leetcode26删除有序数组中的重复项

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

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
返回 k 。

示例 1:

输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

class Solution {
    public int removeDuplicates(int[] nums) {
        int p1 = 0;
        int p2 =1;
        while(p2 < nums.length){
            if (nums[p1] != nums[p2]){
                nums[++p1] = nums[p2++];
            }else{
                p2++;
            }
        }
        return p1+1;
    }
}

6.  leetcode 88. 合并两个有序数组

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

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

        注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

 输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
分析:双指针,每次比较较小的存入辅助数组,注意索引位置和双指针的关系

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int[] nums = new int[m+n];
        int cur = 0;
        int p1 = 0;
        int p2 = 0;

        while(p1<m || p2<n){
            if(p1 == m){
                cur = nums2[p2++];
            }else if(p2 == n){
                cur = nums1[p1++];
            }else if(nums1[p1] <= nums2[p2]){
                cur = nums1[p1++];
            }else {
                cur = nums2[p2++];
            }
            nums[p1+p2-1] = cur;
        }
        for(int i = 0;i<nums1.length;i++){
            nums1[i] = nums[i];
        }
    }
}

倒序:

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int right = n-1;
        int left = m-1;
        int cur = nums1.length-1;
        while(right >= 0 && left >= 0){
            if(nums1[left] > nums2[right]){
                nums1[cur--] =  nums1[left--];
            }
            else{
                nums1[cur--] =  nums2[right--];
            }
        }
        while(right >= 0){
            nums1[cur--] =  nums2[right--];
        }
        while(left >= 0){
            nums1[cur--] =  nums1[left--];
        }
    }
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值