Java 数组及相关习题

文章目录

数组

什么是数组?(数组其实是一种数据结构)

一个定义N个相同数据类型的变量,我们就把这种结构称之为数组,数组作为第一个引用数据类型

什么是数据结构?

把一堆数字保存起来的结构就是数据结构,数据结构关心的是如何高效的读写数据

数组的创建与初始化

在这里插入图片描述

int[] arr = new int[]{1,2,3,4};//初始化数组时,每个元素同时赋值
int[] arr1 = new int[5];//创建数组时,若没有给元素赋值,那么每个元素都是该数据类型的默认值

数据的静态初始化

数据类型[ ]数组名称 = {初始化数据};

Int[] arr = {1,3,4};
Int[] arr = new int[]{1,3,4}; 

数据的静态初始化就是一个java的语法糖,javac编译以后,就是动态初始化

数组的使用

1.获取一个数组的长度(最多保存的元素个数),使用数组名称.length

int[] arr = new int[5];
System.out.println(arr.length);//打印数组长度

2.如何访问数组元素
使用数组名称[要访问的元素相较于第一个元素的偏移量]
使用数组名称[元素的索引]

int[] arr = new int[]{1,2,3};
System.out.println(arr[0]);//取得arr中的第一个元素

数组的索引从0开始,最后一个索引arr.length-1

如果打印一个不存在的元素,就会出现
在这里插入图片描述

索引之所以从0开始是因为索引其实就是”偏移量”,相较于第一个元素的单位长度,数组在内存中存储时,每个元素都是顺序存储的,保存的就是数组的首要地址,想要找到其他元素,只需要知道其他元素相较于第一个元素的距离就能找到

遍历每一个元素

for 循环
public static void main(String[] args) {
    int[] arr = new int[]{1,2,3,4};
    for (int i = 0; i < arr.length; i++) {//此处的i表示元素的下标
        System.out.print(arr[i]+" ");
    }
}

for each 循环,增强型for循环

public static void main(String[] args) {
    int[] arr = new int[]{1,2,3,4};
    for (int i:arr) {
        System.out.print(i+" ");//此处i指的是从数组中第一个元素的拷贝,第二次循环时将第二个元素复制给i,以此类推直到遍历结束
    }
}

for each循环只能读取数组的元素,无法修改
i是原数组中每个值的拷贝,并不是实实在在的数组元素

数组和方法之间的关系

1.数组作为方法的参数
在这里插入图片描述

关于应用数据类型的理解问题

JVM把内存划分为6个区域,有栈区,堆区…
方法的调用就是在栈区进行的每个方法的调用过程,就是一个栈帧的入栈以及出栈的过程

“栈”-先进后出的结构,LIFO,方法中的局部变量和形参都在栈中存储,当方法调用结束出栈时,临时变量会被销毁
栈:程序每次调用的过程就对应栈中一个栈帧的入栈和出栈。
当方法开始调用时,入栈,方法中的局部变量都在栈中保存
当方法结束调用时,出栈,所有的局部变量就会销毁

public static void swap(int x,int y) {
    int tmp = x;
    x = y;
    y = tmp;
    System.out.println("x ="+x+" "+"y ="+y);
}
public static void main(String[] args) {
    int x =1;
    int y =2;
    swap(x,y);
    System.out.println("x ="+x+" "+"y ="+y);
}

运行结果如下
在这里插入图片描述
可以看出虽然在swap中交换了x和y的值,但并不影响main中的x和y的值
在栈中是这样的
先把main中x,y 的值赋给swap中的x,y的值
在这里插入图片描述
交换swap中x,y的值
在这里插入图片描述
方法swap调用结束销毁类型变量swap中的x,y
在这里插入图片描述

JVM的另一块内存区域称为”堆区”,所有的对象都在堆中存储
数组对象,类的实例化对象,接口的对象
引用就是起个新的名称,保存的数值就是该对象的地址。
对于数组对象来说,数组引用实际上就是保存了数组首元素的地址

在数组中交换元素

public static void swap(int[] arr) {
    int tmp = arr[0];
    arr[0] = arr[1];
    arr[1] = tmp;
    System.out.println("arr[0] ="+arr[0]+" "+"arr[1] ="+arr[1]);
}
public static void main(String[] args) {
    int[] arr = new int[]{1,2};
    swap(arr);
    System.out.println("arr[0] ="+arr[0]+" "+"arr[1] ="+arr[1]);
}

运行结果如下
在这里插入图片描述

在数组中交换元素是真正意义上的交换
在栈和堆中是这样的:
先将main中的arr赋给swap中的arr
在这里插入图片描述
再将swap中的arr进行元素的交换
在这里插入图片描述
方法结束调用后销毁临时变量swap中的arr
在这里插入图片描述
拿着swapArr方法中的数组引用swap-arr修改了堆中的数组对象的值,这个修改对于主方法中的arr是可见的
本质: 这两个引用指向了堆中的相同的内存区域
new后面的都是对象,数组的对象是堆中实实在在存在的实体

在swap中重新定义arr后交换的对象就不是main中的arr的地址所指向的对象

public static void swap(int[] arr) {
    arr = new int[]{1,2};
    int tmp = arr[0];
    arr[0] = arr[1];
    arr[1] = tmp;
    System.out.println("arr[0] ="+arr[0]+" "+"arr[1] ="+arr[1]);
}
public static void main(String[] args) {
    int[] arr = new int[]{1,2};
    swap(arr);
    System.out.println("arr[0] ="+arr[0]+" "+"arr[1] ="+arr[1]);
}

在栈和堆中如下图所示:
一开始仍然是把main中的arr传递给swap中的arr
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
看见new关键字,就一定在堆中开辟了新的空间

所有对象的内存释放,由JVM来进行
啥时候释放,当一个对象没任何强引用指向,且当前的JVM内存不够用时,才会释放这个对象的内存

Java中数组定义长度时可以是变量

int n=100;
int[] arr = new int[n];//此时我们使用变量n作为数组长度时,使用的这一时刻确定它的值就是100与new int[100]完全相同

数组对象转为字符串

int[] arr={1,2,3,4};
String str = Arrays.toString(arr);
System.out.println(str);

Arrays.toString(数组名称)-> 将数组转为字符串
Arrays.sort(数组名称) ->将数组排序

看到JDK中的某些类后加了s,这些类都是工具类,提供了大量的有用方法,直接调用Arrays-数组的工具类,包含各种各样的方法

以字符串形式式打印数组

public static String fun(int[] arr) {
    String str ="[";
    for (int i = 0; i < arr.length; i++) {
        str = str+arr[i];
        if(i!= arr.length-1) {
            str=str+","+" ";
        }
    }
    str=str+"]";
    return str;
}

二维数组(了解即可)

所谓二维数组就是多了个列的概念
数组类型[ ][ ]数组名称=new数据类型[行数][列数]{可选的初始化数据}

复制数组

1.定义一个方法复制数组

public static int[] copyOf(int[] arr) {
    int[] arr1=new int[arr.length];//定义一个新的数组复制原数组,长度和原数组相同
    for (int i = 0; i < arr.length; i++) {//从第一个下标开始复制数组的元素
        arr1[i]=arr[i];
    }
    return arr1;
}

2.使用Arrays.copyOf复制数组
Arrays.copyOf(数组名称,数组长度)

int[] arr = new int[]{1,2,3};
int[] arr2=Arrays.copyOf(arr,arr.length);//复制数组arr

3.使用Arrays.copyOfRange(数组名称,初始下标,结束下标)
取的范围是[初始下标,结束下标),所以是取不到结束下标的元素的

int[] arr = new int[]{1,2,3};
int[] arr2=Arrays.copyOfRange(arr,0,2);//复制数组arr前两个元素
System.out.println(Arrays.toString(arr2));

运行结果:
在这里插入图片描述

拷贝后的新数组和原数组长度的关系
1.若新数组的长度小于原数组的长度->部分拷贝
2.若新数组的长度等于原数组的长度->全拷贝
3.若新数组的长度大于原数组的长度->全拷贝,剩余的元素由该数据类型的默认值补

数组相关例题

实现一个方法transform,以数组为参数,循环将数组中的每个元素乘以2

public static void transform(int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        arr[i]=arr[i]*2;
    }
}
public static void main(String[] args) {
    int[] arr = new int[]{1,2};
    transform(arr);
    System.out.println(Arrays.toString(arr));
}

创建一个 int 类型的数组, 元素个数为 100, 并把每个元素依次设置为 1 - 100

public static int[] buildArr() {
    int[] arr = new int[100];//数组的动态初始化,在堆中开辟了100个int
    for (int i = 0; i < arr.length; i++) {
        arr[i]=i+1;
    }
    return arr;
}
public static void main(String[] args) {
    System.out.println(Arrays.toString(buildArr()));
}

给出一个整数的数组,找出这个数组的最大值

public static int max(int[] arr) {
    int max = arr[0];//假定数组的首元素为最大值
    for (int i = 1; i <arr.length ; i++) {//从第二个元素开始
        if(arr[i]>max) {//从第二个元素开始判断是否比max大
            max = arr[i];//当第二个元素比max大时,就将第二个元素的值赋给max,以此类推
        }
    }
    return max;
}
public static void main(String[] args) {
    int[] arr = new int[]{1,2,3};
    System.out.println(max(arr));

求一个数组的平均值

public static double avg(int[] arr) {
    double sum=0;
    for (int i = 0; i < arr.length; i++) {
        sum+=arr[i];//用sum去取得所有元素之和
    }
    return sum/arr.length;
}
public static void main(String[] args) {
    int[] arr = new int[]{1,2,3,1};
    System.out.println(avg(arr));
}

查找一个数组中是否包含指定元素,若存在,返回该元素的索引,不存在返回-1

方法1;遍历数组
public static int find(int[] arr,int toFind) {
    for (int i = 0; i < arr.length; i++) {
        if(toFind==arr[i]) {
            return i;//当要找的元素找到时,返回下标i
        }
    }
    return -1;
}
public static void main(String[] args) {
    int[] arr = new int[]{1,2,3,1};
    System.out.println(find(arr,3));
}
方法二:二分查找

二分查找:在有序的集合上(升序或者降序)
在有序的区间中查找元素toFind,不断比较待查找元素和中间位置元素的大小关系,若我们要查找4这个元素对应的索引,除了简单的遍历外,我们可以从区间的中间位置比较
寻找一个元素toFind,left=0,right=arr.length-1,mid=(left+right)/2
若toFind<arr[mid],说明这个元素位于左区间,那么一定小于右区间所有的元素,所以right从mid-1开始判断
若toFind>arr[mid],说明这个元素位右区间,那么一定大于左区间所有的元素,所以left从mid+1开始判断
在这里插入图片描述

public static int binarySearch(int[] arr,int toFind) {
    int left = 0;
    int right = arr.length-1;
    while(left<=right){
        int mid = (left+right)/2;
        if (toFind<arr[mid]) {
            right=mid-1;
        } else if(toFind>arr[mid]) {
            left=mid+1;
        } else {
            return mid;
        }
    }
    return -1;
}
public static void main(String[] args) {
    int[] arr = new int[]{1,2,3,1,7};
    System.out.println(binarySearch(arr,7));

判断一个数组是否是有序数组,默认升序

所谓升序数组:前一个元素大于等于后一个元素,若在遍历数组时发现有一个元素大于后面的元素,则一定不是升序数组
方法一:

public static boolean isSortedArray(int[] arr) {
    for (int i = 0; i < arr.length-1; i++) {
        if (arr[i]>arr[i+1]) {//若前面的元素大于后面的元素则一定不是有序数组,返回false
            return false;
        }
    }
    return true;//遍历完数组还未发现有前面的元素大于后面的元素,返回true
}
public static void main(String[] args) {
    int[] arr = new int[]{1,2,3,1,7};
    System.out.println(isSortedArray(arr));
}

注意边界问题,当循环中出现arr[i+1],就需要判断i+1<arr.length
所以循环继续的条件应该是i<arr.length-1

方法二:

冒泡排序

核心思想:假设现在数组有n个元素,每进行一次遍历过程,就将当前数组的最大值放在数组的末尾,每进行一次遍历,就有一个元素走到了最终位置
将整个数组看做两个子数组
待排序的数组[0…i-1]
排好序的数组[ ]
每当一次遍历之后,待排序的数组元素-1,排好序的数组元素+1

public static void bubbleSort(int[] arr) {
    for (int i = 0; i < arr.length-1; i++) {//外层循环表示一共要要遍历的次数,每经过一次遍历就有一个元素到达了最终位置
        for (int j = 0; j < arr.length-1-i; j++) {
            if (arr[j]>arr[j+1]) {//内层循环表示每当前面的元素大于后面的元素,交换两个元素的位置
                int tmp =arr[j];
                arr[j]=arr[j+1];
                arr[j+1]=tmp;
            }

        }
    }
}

任何一个算法都有优化空间
当待排序数组只剩下一个元素就不需要排序了,当这个数组已经是一个有序数组也就不需要排序了

public static void bubbleSort(int[] arr) {
    for (int i = 0; i < arr.length-1; i++) {//外层循环表示一共要要遍历的次数,每经过一次遍历就有一个元素到达了最终位置
        boolean isSwaped = false;//用isSwaped判断内层循环是否有元素发生了交换
        for (int j = 0; j < arr.length-1-i; j++) {
            if (arr[j]>arr[j+1]) {//内层循环表示每当前面的元素大于后面的元素,交换两个元素的位置
                int tmp =arr[j];
                arr[j]=arr[j+1];
                arr[j+1]=tmp;
                isSwaped = true;
            }

        }    
         if (!isSwaped){//当一个数组的内层循环没有一个元素发生交换时,说明此时已经是一个有序数组了
            System.out.println("此时已经是一个有序数组了");
            break;
        }
    }
}

使一个数组逆序,逆序就是将前面的元素与后面的元素交换

在这里插入图片描述

public static void reverse(int[] arr) {
    int i = 0;
    int j = arr.length-1;
    while(i<j) {
        int tmp = arr[i];
        arr[i]=arr[j];
        arr[j]=tmp;
        i++;
        j--;
    }
}

while循环更多用于只知道循环终止条件,循环走多少次都不清楚
For循环明确知道循环执行的次数

给定一个整形数组,将所有的偶数都放在前半部分,所有奇数都放在后部分

只需要从前向后找奇数,从后向前找偶数,每找到一次两者进行交换

public static void transform(int[] arr) {
    int i = 0;
    int j = arr.length-1;
    while(i<j) {
        if (arr[i]%2==0&&i<j) {//从前往后找奇数,如果不是奇数接着找
            i++;
        }
        if (arr[j]%2!=0&&i<j) {//从后往前找偶数,如果不是偶数接着找
            j--;
        }
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}
public static void main(String args[]){
    int[] arr= new int[]{1,2,3,4,5,6};
    transform(arr);
    System.out.println(Arrays.toString(arr));

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

public static int[] twoSum(int []arr,int target) {
    int[] arr1 = new int[2];//定义一个新的数组用于输出
    for (int i = 0; i <arr.length ; i++) {//外层循环表示从头开始找元素
        for (int j = arr.length-1; i<j ; j--) {//内层循环表示从最后开始找元素
            if (arr[i]+arr[j]==target) {
                arr1[0]=i;//当找到目标元素时,将下标赋值给新的数组的元素
                arr1[1]=j;
                return arr1;
            }
        }
    }
    return arr1;
}
public static void main(String args[]){
    int[] arr= new int[]{1,2,3,4,5,6};
    System.out.println(Arrays.toString(twoSum(arr, 3)));
}

给定了一个非空整数数组,除了某个元素只出现了一次以外,其他元素都出现过两次,找出那个只出现了一次的元素

方法一:

public static int singleNum(int[] arr) {
    for (int i = 0; i < arr.length; i++) {//外层循环表示遍历的次数
        int count=0;
        for (int j = 0; j < arr.length; j++) {//内存循环找相同的元素
            if (arr[i]==arr[j]) {
                count++;
            }
        }
        if (count==1) {//当找了一遍后只出现了一次的元素
            return arr[i];
        }
    }
    return -1;
}
public static void main(String args[]){
    int[] arr= new int[]{1,2,3,4,5,6,2,3,4,5,6,1,0};
    System.out.println(singleNum(arr));
}

方法二:异或运算
异或运算符的本质就是相同返回0,不同返回1,所以只需要将所有元素都异或一遍就能找到只出现一次的元素

public static int singleNum(int[] arr) {
    int sum = 0;//定义一个sum为0,因为0异或任意数都为任意数
    for (int i = 0; i < arr.length; i++) {
        sum=sum^arr[i];
    }
    return sum;
}
public static void main(String args[]){
    int[] arr= new int[]{1,2,3,4,5,6,2,3,4,5,6,1,0};
    System.out.println(singleNum(arr));
}

给定一个大小为n的数组,找出其中的多数元素,多数元素是指在数组中出现次数大于 n/2的元素,假设数组是非空,且给定的数组总是存在多数元素

方法一:

public static int majorityElement(int[] arr) {
    for (int i = 0; i <arr.length; i++) {
        int count = 0;
        for (int j = 0; j <arr.length ; j++) {
            if (arr[i]==arr[j]) {//当两个元素相同,记录该元素的出现次数count++
                count++;
            }
        }
        if (count>arr.length/2) {//当出现的次数count大于数组长度的一半说明找到多数元素
            return arr[i];
        }
    }
    return -1;
}
public static void main(String args[]){
    int[] arr= new int[]{1,2,3,3,4,3,3};
    System.out.println(majorityElement(arr));

方法二:
一个排序的数组,某个元素至少出现了n/2次,那么该元素一定位于排序后的数组的中间位置
在这里插入图片描述

public static int majorityElement(int[] arr) {
    Arrays.sort(arr);
    return arr[arr.length/2];
}
public static void main(String args[]){
    int[] arr= new int[]{1,2,3,3,4,3,3};
    System.out.println(majorityElement(arr));
}

方法三:摩尔投票法
假设第一个元素为候选人,后面的数字给它投票,相同的数字票数+1,不同的票数-1,投的过程中如果票数为0了,更换当前数字为候选人继续投票,当所有人都投过票后,最后剩下的一定就是候选人

public static int majorityElement(int[] arr) {
    int candidate = arr[0];//假设第一个元素为候选人
    int count =1;//候选人先给自己投一票
    for (int i = 1; i < arr.length ; i++) {//其他人从第二个开始投票
        if (candidate==arr[i]) {//相同票数+1
            count++;
        } else {//不同票数-1
            count--;
            if (count==0) {
                candidate=arr[i];
                count++;//更换候选人后当前候选人给自己投一票
            }
        }
    }
    return candidate;//最后剩下的就是候选人也就是多数元素
}
public static void main(String args[]){
    int[] arr= new int[]{1,2,3,3,4,3,3};
    System.out.println(majorityElement(arr));

摩尔投票法的应用-众数问题
1.在一堆元素中,如果至多选择一个最多的元素,则它的票数>n/2
2.在一堆元素中,如果至多选择两个最多的元素,则它的票数>n/3
3.在一堆元素中,如果至多选择m个最多的元素,则它的票数>n/m+1

给定一个整数数组arr,请你判断数组是否存在连续三个奇数,若存在返回true,不存在返回false

public static boolean isConsectiveOdd(int[] arr){
    int count =0;//用count计算连续出现奇数的次数
    for (int i = 0; i < arr.length; i++) {
        if (arr[i]%2!=0) {//出现奇数,count++
            count++;
        } else {
            count=0;//出现偶数,count直接归0
        }
        if (count==3) {//连续出现三次奇数返回true
            return true;
        }
    }
    return false;
}
public static void main(String args[]){
    int[] arr= new int[]{1,2,3,3,4,3,3,3};
    System.out.println(isConsectiveOdd(arr));
}
  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值