【数据结构与算法】1.0数组与链表

本文探讨了在资源有限的情况下如何处理大规模数据,如利用数组统计全国年龄分布,强调了数组的内存管理和读取效率。同时,解释了栈和堆内存的区别,以及字符串在内存中的存储方式。还介绍了二维数组的内存地址表示,并讨论了链表的特性,包括单链表和双向链表。最后,提出了LRU缓存淘汰算法的设计和约瑟夫问题的链表解决方案。
摘要由CSDN通过智能技术生成

数组

Q1:有一个数据文件,其中包含了全国人民的年龄数据(txt文件大小5G),现在要统计每一个年龄有多少人,已知如下运行条件:单台服务器,2CPU,2G内存,不得使用现成的容器,比如map等

A:解答思路:1. 磁盘文件太大,无法一次读取文件

2. 不允许使用map等容器,但是必须要进行分类统计

3. 这是一道算法题,最好不使用分布式处理

demo:

InputStreamReader isr = new InputStreamReader(new FileInputStream(filename),"UTF-8");

BufferedReader br = new BufferedReader(isr);

int total = 0;//统计总共有多少数据行

int data[] = new int[200];//假设人的年龄分布是200岁以下,人的岁数是自然数,因此设一个大小为200的数组,只要统计每个index的sum就可以得到每个年龄的人数了

while (str=br.readLine() != null){    //一行一行地读O(n)
    int age = Integer.valueOf(str);
    data[age]++;
    total++;     //计算总共有多少人                    
}

for(int i=0;i<200;i++){
    System.out.println(i+": "+data[i]);  //输出统计结果
}

数组的特点:

1. 相同数据的连续集合——插入/删除效率低(O(n))

2. 数组中的元素存储是由先后顺序的,在内存中按照这个先后顺序连续存放在一起。数组一旦申明,内存空间就占用好了。int[m][n]占用内存的大小是m*n

3. 随机访问——读取效率高(O(1))

ArrayList和数组的本质是一样的,但ArrayList是JDK封装的,不需要处理扩容等操作,数组的处理需要开发自己处理

两者之间应如何选用:

1. 不知道数据大小:ArrayList

2. 知道数据大小,且关注读取性能:数组 (注意越界问题,多加判断)

堆栈内存 

堆内存:存放new创建的对象和数组

栈内存:引用变量,速度快,栈的数据时可以共享的,主要存一些基本数据类型

Q2: int a=3; int b=3;这段代码中,3是存储在栈中的还是堆中?

A:JVM运行时,会在栈中创建一个a,再看栈中是否存在3,如果存在,则把a指向3,如果不存在,则新建一个3,并把a指向3;再创建一个b,同样的过程,查找栈中是否存在3,存在则将b指向3

Q3:判断字符串是否相等

String str1 = "abc";
String str2 = "abc";
System.out.println(str1=str2);    //true,原因是"abc"存储在栈中,str1和str2都指向同一个栈地址

str1="bcd";
String str3 = str1;
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;
System.out.println(s3 == s4);    //false;原因是"+"被重载了,调用了StringBuild方法,会new对象
System.out.println(s3.equals(s4));// true,只比较值

    

Q3:二维数组的内存地址是什么 样的?

A:一维数组的内存地址是:int_loc(初始内存地址)+i * typesize(数据类型占用的字节数)

例如:数组int a[3]的存储情况:

a[0] = int_loc+0*typesize;        //这也是数组下标为什么从0开始的原因,方便记录

a[1] = int_loc+1*4个字节32位 

a[2] = int_loc+2*typesize

因此二位数组a[m][n]的内存地址公式:int_loc+(i*n+k)*typesize ,其中0<=i<n, 0<=k<m

链表

链表的特点:

a) 不需要连续的内存空间 b)有指针引用 c)三种最常见的链表结构:单链表、双链表、双向链表和循环链表

单链表的查找时间复杂度:O(n)

单链表的插入/删除时间复杂度:O(1)(指定位置)

双向链表的应用:B+树的叶子节点 

双向链表相对于单链表,双向链表需要额外的两个空间来存储后继结点和前驱结点的地址,增加了额外的存储空间,但是操作效率更高

Q1:如何设计一个LRU缓存淘汰算法?

LRU缓存淘汰算法(Least Recently Used):最近最少被使用的;且LRU算法有内存限制。

设计思路:

有一个链表,新加入的节点:

        a)在链表中是否存在,如果存在,则将该节点删除,并insert到head节点;

        b)若不存在,如果链表未满,则新节点作为head节点;

        c)若不存在,如果链表已满,则删除最后一个节点;再将新节点作为head节点;

Q2:约瑟夫问题:丢手绢,所有的人围起来一圈,开始点数,点到几谁就退出,链表的经典算法?

A:构造一个循环链表

总结:

1. 数据简单易用,在实现上使用的是连续的内存空间,可以借助CPU的缓存机制,预读数组中的数据,访问效率更高;

2. 链表在内存中并不是连续存储,所以对CPU缓存不友好,没有办法有效预读;

3. 数组的缺点是大小固定,一经声明就要占用整块连续内存空间,如果声明的数组过大,系统可能没有足够的连续内存空间分配给它,导致”内存不足OOM“,如果声明的数组过小,则可能出现不够用的情况;

4. 动态扩容:数组需再申请一个更大的内存空间,把原数组拷贝进去,非常费时,链表本身没有大小的限制,天然地支持动态扩容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值