数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按无序的形式组织起来的一种形式。 这些无序排列的同类数据元素的集合称为数组。
首先数组他就是归同一类变量的一种统一办法,把它们集中起来然后用数字(角标)来进行管理,将他们排列,当然也可以利用角标来索引获取某一个字符或者数字。相当于用于保存一组元素的容器
数组的定义
1.1 数组的优点:
- 提高代码的简洁性和扩展性,且同时开辟多个空间,提高了效率
- 分类存储,且空间是连续的,容易查找
- 它能解决多变量多数据的存储问题,方便后期统一维护操作
1.2 数组的特点:
- 数组存储的是相同类型的一组元素
- 数组的定义类型可以是任意类型,包含基本类型或引用类型
- 数组本身也属于引用类型,内存的分配和基本类型不同
1.3数组的四要素
- 数据类型:任意类型[]
- 数组名
- 值(对象)
- ★下标 :从0开始
- 访问某个具体的成员,需要通过:数组名[下标]的方式!
对数组的小总结
- 数组是每个空间大小相等的且连续的地址的存储空间
- 数组存在于堆内存中,一般在堆中存储的数据都称之为对象,在堆内存创建的对象都会有默认值
- 整数类型默认 0 2.浮点类型默认 0.0 3. 布尔类型默认false 4.引用数据类型 默认null
- 利用角标来访问数据中的元素
- 数组变量存的是数组在堆内存中首元素的地址(后面的元素通过角标计算可以得到)
- 数组发生的错误就是主要有这两种
- ArrayIndexOutOfBoundsException 数组角标越界,
- NullPointerException 空指针异常
创建数组:
数据类型[] 数组名=new 数据类型[长度] 创建数组只指定长度不指定内容。
数据类型[] 数组名=new 数据类型[]{1,2,3} 创建数组指定内容
数据类型[] 数组名={1,2,3,4,5}; 创建数组指定内容(指定长度)
数组的存储方式与内存分析
首先我们的数组为什么会提供角标来访问数据,因为数组里存储的只有首变量空间的地址。
通过:首地址+角标*数据类型的大小=(角标+1)位置的地址(因为数组角标是从0开始的),这就是为什么数组访问数据时比较方便和快。
数组的各种操作(查找,排序,赋值,遍历,最大最小值)
这些都有共同点:循环遍历过程中都是通过角标的0-arr.length来计算的所以都是在比较或者灵活运用角标来表示一些字符数字,下面来看几个经典案例:
遍历
class Lei{
public static void main(String[] args){
//遍历操作
bianli();
}
public static void bianli(){
int[] arr={1,2,3,4,5,6,7,8,9};//[0,8]
//数组只有一个唯一的属性 length 数组的长度
System.out.println(arr.length);
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
}
}
赋值
import java.util.Scanner;
class Lei{
public static void main(String[] args){
//赋值操作
fuzhi();
public static void fuzhi(){
Scanner scanner=new Scanner(System.in);
int[] arr2=new int[10];
for(int i=0;i<arr2.length;i++){
System.out.print("请输入1个数字:");
arr2[i]=scanner.nextInt();
}
for(int i=0;i<arr2.length;i++){
System.out.print(arr2[i]+" ");
}
}
}
最大值最小值
import java.util.Scanner;
class Lei{
public static void main(String[] args){
maxormin();
}
public static void maxormin(){
//获取最大的值10 获取最小值的角标4
int[] arr={10,2,8,3,1,6,4,7,9,5};
int max=arr[0]; //先从第一个开始取最大值
int min_index=0; //令第一个为最小值角标0
for(int i=0;i<arr.length;i++){ //初始值0 限制条件小于等于length-1
if(arr[i]>max){
max=arr[i];//更新最大值
}
if(arr[i]<arr[min_index]){
min_index=i;//更新最小值角标
}
}
System.out.println("最大值"+max);
System.out.println("最小值角标"+min_index);
}
}
二分查找(必须有序)和线性查找的优缺点
就比如在一个区间中找一个数字那么最笨的办法就是从小到大一次遍历查找当找到就退出循环,这是线性查找的缺点,如果这个数字在最后那么就太耗时;二分查找就是把区间一分为二分为二,把查找的那个数放到区间内,把另一个区间舍去,重复如此,直到找到,那么大多数还是比较省时的。
那么具体的做法是什么样子的呢
线性查找
class Test06{
public static void main(String[] args){
//线性查找
linearSearch();
public static void linearSearch(){
int[] arr={10,2,8,3,1,6,4,7,9,5};
int key=11;
int index=-1; //key元素不存在
for(int i=0;i<arr.length;i++){
if(arr[i]==key){
index=i; //如果找到了key值那么数组角标等于index
break; //此时跳出循环即可
}
}
System.out.println(index);
}
}
二分查找
这是数组 int[] arr={12,17,21,32,38,41,46,49,50,50,51,59,60}的最大深度
二分思想就是判断找的数和arr[(0+arr.length-1)/2]大小如果大舍弃左边,如果小舍弃右边
舍弃左边 那么初值就是刚才中值角标加一
舍弃右边 那么初值就是刚才中值角标减一
下面用代码实现:
class Lei{
public static void main(String[] args){
//二分查找
binarySearch();
}
public static void binarySearch(){
int[] arr={12,17,21,32,38,41,46,49,50,50,51,59,60};
int key=46;
int index=-1; //判断是否能找到
int min_index=0;//初值角标0
int max_index=arr.length-1;//最大角标
int mid_index=(min_index+max_index)/2;//中间角标
while(arr[mid_index]!=key){
if(key<arr[mid_index]){ //利用角标不断夹逼然后最终找到值
max_index=mid_index-1;
}
if(arr[mid_index]<key){
min_index=mid_index+1;
}
if(min_index>max_index){ //当小角标大于大角标时候那么就是说没找到
index=-1;
break; //循环跳出输出 -1即可
}
mid_index=(min_index+max_index)/2;
}
System.out.println(mid_index);
}
可变长参数
当我们具体不确定给一个函数传多少参数的话,那么可以使用可变长参数(这些参数必须是相同的类型)
例:public static void multArguments(int ...nums){
也可以把定义固定参数,但是必须放在可变长参数的前面,意思就是说从左到右依次给,剩下的就都是可变长参数。那么就意味着一个参数列表中最多只能出现一个可变长参数。
foreach循环
- 它是简化过后的for循环
- 循环的基本格式
for(循环数据类型 循环变量名 : 可迭代对象 ){
循环体;
}
优点:是为了避免那些可迭代对象的元素访问顺序比较复杂
缺点:仅仅只能访问元素,但不能修改元素。
class Lei{
public static void main(String[] a){
for(int i=0;i<a.length;i++){
System.out.println(a[i]);
}
multiArguments(1,2,3,4,5,6,7);
public static void multiArguments(int ... nums){
for(int num : nums){ //num代表的是nums[i] i∈(0~nums.length)
System.out.println(num);
}
System.out.println();
}
下面是Array类的封装函数
1.首先是Math,Scanner工具类,String(不是工具类)
2.函数封装方法(下面写出常用的封装函数的底层实现)
2.1.static int binarySearch(int[] a, int key) (使用二分查找的方法来搜索指定数组)
class Test06{
public static void main(String[] args){
binarySearch();
}
public static void binarySearch(){
//二分查找有个前提 数组必须有序
int[] arr={12,17,21,32,38,41,46,49,50,50,51,59,60};
int key=46;
int index=-1;
int min_index=0;
int max_index=arr.length-1;
int mid_index=(min_index+max_index)/2;
while(arr[mid_index]!=key){
if(key<arr[mid_index]){
max_index=mid_index-1;
}
if(arr[mid_index]<key){
min_index=mid_index+1;
}
if(min_index>max_index){
index=-1;
break;
}
mid_index=(min_index+max_index)/2;
}
System.out.println(mid_index);
}
}
2.2. static void sort(int[] a) (对指定int型数组进行升序排序)
因为升序方法很多 这里就不多写了 使用最稳定的插入排序
class Lei{
public static void main(String[] args){
insertSort();
public static void insertSort(){
int[] arr={8,5,9,2,7,4,6,1,3};
int e;
int j;
for(int i=1;i<arr.length;i++){
e=arr[i];
for(j=i;j>0&&arr[j-1]>e;j--){
arr[j]=arr[j-1];
}
arr[j]=e;
System.out.println(Arrays.toString(arr));
}
/*这是一种比较费时的算法 上面是优化过的 不用一步一步换了
for(int i=1;i<arr.length;i++){
for(int j=i;j>0&&arr[j-1]>arr[j];j--){
swap(arr,j,j-1);
}
}
*/
2.3. static int[] copyOf(int[] original, int newLength) (复制指定的数组,长度)
class Lei{
/*
数组的扩容,无非就是创建一个新的数组 将元素赋值进去之后
将新数组的地址返回即可
*/
public static void main(String[] args){
int[] arr=new int[]{1,2,3};
arr=copyOf(arr,arr.length+1);
arr[arr.length-1]=10;
System.out.println(arr[3]);
}
public static int[] copyOf(int[] arr,int newLen){
int[] newArr=new int[newLen]; //新数组 新长度
for(int i=0;i<arr.length;i++){
newArr[i]=arr[i];
}
return newArr;
}
}
2.4. static String toString(int[] a) (返回指定数组内容的字符串表示形式。)
public static void show(int[] arr){
//[1,2,3,4,5,6,7,8,9]
String s="[";
for(int i=0;i<arr.length;i++){
if(i==arr.length-1){
s+=arr[i]+"]";
}else{
s+=arr[i]+",";
}
}
System.out.println(s);
}
}
下面是例题
1.
步骤:
1.首先采用计数排序的思想,
2.创建一个长度为101的数组
注意就是把数值和角标进行统一 这个题限制了数字的范围并且是证书所以用计数思想最简洁
代码如下:
import java.util.Scanner;
class Lei5_1{
public static void main(String[] args){
int[] arr=new int[101]; //arr[i] 就表示数字i出现的次数
Scanner scanner=new Scanner(System.in);
System.out.print("Enter numbers:");
while(true){
int num=scanner.nextInt(); //把用户输入的数值给num
if(num==0){ //如果num=0时那么结束循环
break;
}
arr[num]++; //重复出现 计数加一 (默认为0)
}
for(int i=0;i<arr.length;i++){
if(arr[i]!=0){ //如果不等于0 那么输出i的值出现了arr[i]次
System.out.println(i+" occurs "+arr[i]+(arr[i]>1?" times":" time"));
}
}
}
}
2.
步骤:去重复操作
1.定义一个长度为10的数组 把数字往里面存放
2.使用Boolean类型的contains函数 判断是否存在重复
3.使用copyOf函数复制原来的数组定义新长度 输入一个数 数组长度加一 一直处于动态变化过程中
mport java.util.*;
class Lei2{
public static void main(String[] args){
func();
}
public static void func(){
int[] arr=new int[10];
Scanner scanner = new Scanner(System.in);
System.out.print("Enter numbers:");
for(int i=0;i<10;i++){
int num=scanner.nextInt();//用户输入赋给num
if(!contains(arr,num)){ //如果用户输入num 里数组没有
arr=copyOf(arr,arr.length+1);//那么就再复制一个空间 存放num
arr[arr.length-1]=num; //直输入到数组的最大长度为止
}
}
System.out.println(Arrays.toString(arr));
}
public static boolean contains(int[] arr,int key){
for(int i=0;i<arr.length;i++){ //遍历 判断输入key值 arr数组里面是否有包含的
if(arr[i]==key){
return true; //如果有就返回true
}
}
return false;
}
public static int[] copyOf(int[] arr,int newLen){
int[] newArr=new int[newLen]; //定义新数组
for(int i=0;i<arr.length;i++){
newArr[i]=arr[i]; //把原来数组的数值按照角标对应存放
}
return newArr;
}
}
3.
分析:
1.首先看题中给出的参数都有什么关系 不难看出 槽子个数等于球下落经过钉子的次数加一
2.提示用户输入槽子的个数和小球的个数
3.根据已经有的球和槽子的个数去随机创建一个小球下落的路径
4.创建字符串 L R 路径 都是随机产生的
5.打印路径得知最终所落入的槽子(只要判断路径中出现了几个R即可判断那个槽子)
代码如下
import java.util.*;
class Lei3{
public static void main(String[] args){
Scanner scanner=new Scanner(System.in);
System.out.print("Enter the number of balls to drop:");
int balls=scanner.nextInt(); //输入球的个数
System.out.print("Enter the number of slots in the bean machine:");
int slots=scanner.nextInt(); //输入槽子的个数
int[] arr=new int[slots]; //定义槽子数组
//几个球几个路径path
for(int i=0;i<balls;i++){
String path=getPath(slots);
System.out.println(path);
arr[getR(path)]++; //只要看当前路径中R的个数即可并且拼接字符串数组
}
System.out.println(Arrays.toString(arr));//输出字符串数组
show(arr);
}
public static void show(int[] arr){ //打印题中的结果
int w=arr.length;
int h=0;
for(int i=0;i<arr.length;i++){
if(arr[i]>h){
h=arr[i];
}
}
for(int i=h-1;i>=0;i--){
for(int j=0;j<w;j++){
if(i<arr[j]){
System.out.print("O");
}else{
System.out.print(" ");
}
}
System.out.println();
}
}
public static int getR(String path){
int count=0;
for(int i=0;i<path.length();i++){ //遍历路径字符串的长度把路径里的R出现的次数记录
if(path.charAt(i)=='R'){
count++;
}
}
return count; //返回R出现的次数值 就可以看出球在那个槽子
}
public static String getPath(int slots){
//根据槽子的个数计算每一个球下落的路径
Random random=new Random(); //使用随机数
String path=""; //定义空串路径
for(int j=0;j<slots-1;j++){
if(random.nextInt(2)==0){ //如果随机数为0时 那么路径字符串加L
path+="L";
}else{ //向右
path+="R"; //如果随机数为1时 那么路径字符串加R
}
}
return path; //返回打印的字符串路径
}
}
4.
.
分析:
1.两个有序数组合并采用创建新数组长度等于两数组长度之和
2.定义三个指针 p1 p2 p3 都从角标0开始
- 结束条件是p1等于list1.length并且p2等于list2.length
- 下来判断那个数组长度长 如果移动小长度数组末端时 那么p3只能跟着大长度数组角标继续往后移动
- 当p1和p2都没超过数组长度时 p3角标则会跟着数组值小的那组角标移动 (所以是移动一次比一次)知道满足结束条件循环
import java.util.*;
class Demo05_07{
public static void main(String[] args){
int[] list1={1,3,5,7,9};
int[] list2={2,4,6,8,10}; //定义两个数组
System.out.println(Arrays.toString(merge(list1,list2)));
}
public static int[] merge(int[] list1,int[] list2){
if(list1==null&&list2==null){ //先判断两个是否为空
return null;
}
if(list1==null){
return list2;
}
if(list2==null){
return list1;
}
//只有两个都不是null的情况再考虑具体操作
int[] list3=new int[list1.length+list2.length]; //定义新数组长度为两数组之和
int p1=0; //数组1指针
int p2=0; //数组2指针
int p3=0; //数组3指针
while(true){
if(p1==list1.length&&p2==list2.length){
break;//结束条件如果两个指针都移动到末端那么循环结束
}
if(p1<list1.length&&p2==list2.length){
//指针2移动到数组2末端时 指针1还在继续
list3[p3++]=list1[p1++]; //数组3的指针跟随指针1并且数组1把值直接挪过来
}else if(p1==list1.length&&p2<list2.length){
//指针1移动到数组1末端时 指针2还在继续
list3[p3++]=list2[p2++]; //数组3的指针跟随指针1并且把数组1的值直接挪过来
}else{ //当两个数组都没结束时
if(list1[p1]<=list2[p2]){
//如果数组一小于数组二的值那么 把数组一的值给数组三 并且指针同时往后移动
list3[p3++]=list1[p1++];
}else{
//如果数组二小于数组一的值那么 把数组一的值给数组三 并且指针同时往后移动
list3[p3++]=list2[p2++];
}
}
}
return list3; //程序结束时那么数组三中把数据已经排好序 然后返回
}
}
5.
分析:
1.创建一个单词表
2.随机从单词表抽取一个单词 定义猜错的个数
3.因为要猜多个单词所以使用while(true)循环一个单词猜完提示用户是否猜下一个单词
4.随机抽取取一个单词使用random随机数 创建单词的状态表 默认值为false(密文)
import java.util.*;
class Lei5{
/*
数据 一组单词的明文 单词的密文 单词的状态
program
1000000 r r
pr**r**
*/
public static void main(String[] args){
Scanner scanner=new Scanner(System.in);
Random random=new Random();
//1.创建一个单词表
String[] words={"naruto","kakashi","sasuke","banana","java","program"};
//10.最后再去做多单词猜测
while(true){
//2.随机从单词表中抽取一个单词
String word=words[random.nextInt(words.length)];
//3.创建一个该单词的状态表 默认值是false(密文)
boolean[] status=new boolean[word.length()];
int miss=0; //猜错的个数
//4.开始猜一个单词
while(true){
//5.根据单词和状态表 决定密文形式
String ciphertext=getCipherText(word,status);
//6.输出密文并提示用户输入字母
System.out.print("Enter a letter in word "+ciphertext+" >");
char letter=scanner.nextLine().charAt(0);//"p".charAt(0)
//7.判断单词中是否有该字母
if(isContainsLetter(word,letter)){
//8.改变单词状态表 已修改/未修改
//true 表示从未修改 第一次来的
//false 表示已修改 不是第一次来 提示已经存在
if(!changeWordStatus(word,status,letter)){
System.out.println("\t "+letter+" is already in the word");
}
}else{
System.out.println("\t "+letter+" is not in the word");
miss++;
}
//9.是否结束
if(isFinish(status)){
System.out.println("The word is "+word+". You miss "+miss+" time");
break;
}
}
System.out.print("Do you want to guess another word?Enter y or n:");
String choice=scanner.nextLine();
if(choice.equals("n")){
System.out.println("Welcome!Thank you! FUCK PROGRAM!");
break;
}
}
}
public static boolean isFinish(boolean[] status){
for(int i=0;i<status.length;i++){
if(!status[i]){
return false;
}
}
return true;
}
public static boolean changeWordStatus(String word,boolean[] status,char letter){
for(int i=0;i<word.length();i++){
if(word.charAt(i)==letter){
if(status[i]){
return false; //说明已经修改
}else{
status[i]=true;
}
}
}
return true;
}
public static boolean isContainsLetter(String word,char letter){
for(int i=0;i<word.length();i++){
if(word.charAt(i)==letter){
return true;
}
}
return false;
}
public static String getCipherText(String word,boolean[] status){
String ciphertext="";
for(int i=0;i<status.length;i++){
if(status[i]){
ciphertext+=word.charAt(i);
}else{
ciphertext+="*";
}
}
return ciphertext;
}
}