【Java版数据结构】数组

一、例题一:2的n次方

判断一个数是否是2的N次方。比如2 4 8 16,是的;6 10 不是的。就看这个数是不是可以拆成N个2相乘。

解法1:

    public static boolean solution1(int x) {
        // 4 6 16 是2的n次方 , 15 不是
        if (x == 0 || x == 1) {
            return true;
        }
        while (x > 1) {
            int i = (x % 2 == 0) ? (x /= 2) : (x = 0);
        }
        if (x == 1) return true;
        return false;
    }

解法2:

若x满足x = 2^n, x&(x-1) == 0

1000

0111

    public static boolean solution2(int x) {
        // 4 6 16 是2的n次方 , 15 不是
        if ((x & (x - 1)) == 0) return true;
        return false;
    }

二、例题二:高效地读文件(面试经典)

给你一个文件里面包含全国人民(14亿)的年龄数据(0~180),现在要你统计每一个年龄有多少人?
**限制:**给定机器为 单台+1CPU+1G内存。不得使用现成的容器,比如map等。

分析:假设每个年龄数据为2位数,gbk编码下每个数字字符size = 1B,该文件总大小 = 14*(10^8)*2B /1000 /1000 /1000 = 1.4G , 一次放不下,且使用排序算法的话,排序的最高效算法时间复杂度为:O(nlogn) ,其中n=14亿,排不出来,而且内存也不够。如果使用现成的容器可以直接解决,如不使用现成容器,可以考虑用数组来实现。

解法一:直接思路,分布式存储(例如使用hadoop框架,HDFS存储,使用MapReduce计算后合并),有点蠢

解法二:数组算法
在以上情况下你该如何以最高效的方法来解决这个问题?

int a[] = new int[180];

a[0]++; // 0表示的是0岁,a[0]的值表示的就是0有多少人.

12:56
23:5611
52:9999888

下标:数组最优一个特点。这里可以通下标表示成有意义的数据,不只是数据里面的标记,年龄和下标对应。随机访问:可以直接通过下标定位到数组中的某一个数据(高效查询)

public class AgeStas {

	public static void main(String[] args) throws Exception {
		String str = null;
		String fileName = "D:\\JavaEE软开工程师\\算法刷题\\数据结构与算法\\02 基础数据结构\\age1.txt";
		InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName),"UTF-8");

		long start = System.currentTimeMillis();
		BufferedReader br = new BufferedReader(isr);
		int tot = 0 ;	// 14亿
		// 缓存桶
		int data[] = new int[200];
		while((str = br.readLine()) != null){		//一行一行的读 O(n)
			int age = Integer.valueOf(str);
			data[age] ++ ;
			tot ++ ;
		}
		// O(n) 14亿.
		// 单核Cpu处理速度(考虑了和内存的交互速度): 100万/秒 - 1000万/秒
		// 100s ~ 1000s之间
		// 16核 6s ~ 60s之间
		System.out.println("总共的数据大小: " + tot);
		
		for(int i = 0 ; i < 200 ; i ++){//下标从0开始的
			System.out.println(i + ":" + data[i]);
		}
		// 43757ms => 43s
		System.out.println("计算花费的时间为:" + (System.currentTimeMillis() - start) + "ms");
	}
}
// 输出结果
总共的数据大小: 1400000000
0:7780353
1:7778794
2:7773040
3:7779555
4:7776882
5:7773873
6:7778810
7:7777106
8:7781165
9:7782188
10:7774890
11:7776274
12:7778024
13:7777931
...

三、数组的结构

基本数据结构:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IoqJuNhV-1635678831610)(D:\JavaEE软开工程师\算法刷题\数据结构与算法\02 基础数据结构-数组\基本数据结构图.jpg)]

1.数组的定义

所谓数组,是有序的元素序列。 若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。用于区分数组的各个元素的数字编号称为下标。数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按无序的形式组织起来的一种形式。这些无序排列的同类数据元素的集合称为数组。int 的数组你就不能存float 也不能存double

数组是用于储存多个相同类型数据的集合。通常用Array表示,也称之为线性表。

2.特点

(1)数组是相同数据类型的元素的集合。

(2)数组中的各元素的存储是有先后顺序的,它们在内存中按照这个先后顺序连续存放在一起,即内存地址连续。

(3)数组元素用整个数组的名字和它自己在数组中的顺序位置来表示。例如,a[0]表示名字为a的数组中的第一个元素,a[1]代表数组a的第二个元素,以此类推。

3.表现形式

(1)一维数组

Int a[],String a[]

(2)多维数组

Int a[][],int a[][][]。 int a[m][n]:内存空间是多少? m*n*typeSize

a[0][10]: 链表解决,a[0]:->10>2 a[1]->15

4.随机访问:

数组是连续的内存空间和相同类型的数据。正是因为这两个限制,它才有了一个非常重要的特性:随机访问。但有利就有弊,这两个限制也让数组的很多操作变得非常低效,比如要想在数组中删除、插入一个数据,为了保证连续性,就需要做大量的数据搬移工作。

随机访问的重要应用:查找,面试重点, O(1)

5.数组的缺点:插入和删除

O(n)

实现代码:

设数组的长度为n,现在,如果我们需要将一个数据插入到数组中的第k个位置。删除第N个位置的数据.

github链接: [代码待上传]

6.使用数组一定要注意访问越界问题。

7.**思考:**为什么很多计算机编程语言中数组的下标要从0开始呢?

定义一个数组一定会分配内存空间。数组的特点是:内存是一段连续的地址。

int a[] = new int[3];

到内存中申请空间:10001,10002,10003

存数据

a[0] => 10001 ====> 10001+0*typesize

a[1] => 10002 =====> 10001+1*typesize

a[2]=> 10003 =====> 10001+2*typesize

如果我们不从0开始,会浪费cpu计算量

a[1] = 10001+(1-1)

a[2] = 10001+(2-1)

a[3] = 10001+(3-1)

四、ArrayList和数组的比较

本质是一样的,都是数组。ArrayList是JDK封装了。不需要管扩容等操作

数组的话就要你全部操作

两者之间应该如何选用?:

不知道数据大小的肯定选ArrayList。

如果你知道数据的大小而且你又非常关注性能那就用数组。

数组最需要注意的就是越界:所以一定要多加判断,尤其是在开始和结束。测试的时候也一样注意头和尾。

五、jvm内存

Java里面的内存分为几种?

Java分为堆栈两种内存。

什么是堆内存?:存放new创建的对象和数组(凡是new出来的都在堆内存里)

什么是栈内存?引用变量

堆栈都用Java用来存放数据的地方,与C++ / c不一样。java自动管理我们的堆栈。Gc(主要回收堆内存),new出来的你没管过。

堆栈的区别:

1.栈的速度要快

2.栈内存的数据可以共享(缓存),主要存一些基本数据类型。

int a = 3; //在栈中创建变量a 然后给a赋值,先不会创建一个3而是先在栈中找有没有3,如果有直接指向。如果没有就加一个3进来。

int b =3; //首先也要创建一个变量b,把a处创建的3复用进来

六、例题三:String(面试经典)


String str1 = "abc"; String str2 = "abc"; System.out.println(str1==str2);	//true


String str1 = "abc"; String str2 = "abc"; str1 = "bcd"; 
System.out.println(str1 + "," + str2); 	//bcd,abc
System.out.println(str1==str2); 		//false 虽然最开始 str1和str2都指向同一个变量abc但str1引用变化后不会改变str2的

String str1 = "abc"; 
String str2 = "abc"; 
str1 = "bcd"; 
String str3 = str1;  //abc
System.out.println(str3);  //bcd
String str4 = "bcd"; 
System.out.println(str1 == str4);	//true

String str1 = new String("abc"); 
String str2 = "abc"; 
System.out.println(str1==str2); 	//false  new在堆内存中新开了一个对象

String s1 = "ja";
String s2 = "va";
String s3 = "java";
String s4 = s1 + s2; //java 注意这个+号,java里面重载了+,其实调用了stringBuild,会new对象。
System.out.println(s3 == s4);	//false
System.out.println(s3.equals(s4;	//true 只是比较值
))

拓展: 二维数组的内存地址是怎么样的?写出寻址公式

a[2][3]

a[0][0] => 10000 ====> 10000+0 * typesize

a[0][1] => 10004 =====> 10000 +1 * typesize

a[0][2] => 10008 =====> 10000 +2 * typesize

a[1][0] => 10012 =====> 10000 +(1*3 + 0) * typesize

a[1][1] => 10016 =====> 10000 +(1*3 + 1) * typesize

a[1][2] => 10020 =====> 10000 +(1*3 + 2) * typesize

总结

数组是一个最基础最简单的数据结构,必须要完全搞懂。它是存储相同类型的一组数据,最大的两个特点就是

下标和随机访问。缺点就是插入和删除是很慢的,时间复杂度为O(n)。

上一篇:C语言预处理器和输入\输出函数


下一篇:[WC2022] 杂题选讲-钱易