20201022:算法题+选择题

it2025-10-24  9

文章目录

错题一错题二错题三错题四错题五错题六错题七错题八错题九错题十错题十一算法题一:长键按入算法题二:删除排序数组中的重复项算法三:比较含退格的字符串

错题一

下面程序的运行结果() Object obj=new Object(); List aList=new ArrayList(); List bList=new LinkedList(); long t1=System.currentTimeMillis(); for(int i=0;i<50000;i++){ aList.add(0,obj); } long t2=System.currentTimeMillis()-t1; t1=System.currentTimeMillis(); for(int i=0;i<50000;i++){ bList.add(0,obj); } long t3=System.currentTimeMillis()-t1;

ArrayList与LinkedList性能分析比较详解

原文中最后得到的结论如下:

在尾部插入数据,数据量较小时LinkedList比较快,因为ArrayList要频繁扩容,当数据量大时ArrayList比较快,因为ArrayList扩容是当前容量*1.5,大容量扩容一次就能提供很多空间,当ArrayList不需扩容时效率明显比LinkedList高,因为直接数组元素赋值不需new Node

在首部插入数据,LinkedList较快,因为LinkedList遍历插入位置花费时间很小,而ArrayList需要将原数组所有元素进行一次System.arraycopy;

插入位置越往中间,LinkedList效率越低,因为它遍历获取插入位置是从两头往中间搜,index越往中间遍历越久,因此ArrayList的插入效率可能会比LinkedList高;

插入位置越往后,ArrayList效率越高,因为数组需要复制后移的数据少了,那么System.arraycopy就快了,因此在首部插入数据LinkedList效率比ArrayList高,尾部插入数据ArrayList效率比LinkedList高

LinkedList可以实现队列,栈等数据结构,这是它的优势;

错题二

下面有关java threadlocal说法正确的有?

【ThreadLocal介绍】

1、ThreadLocal叫做线程变量,指的是属于当前线程的一个变量,该变量首先对其他线程来说是隔离的,也就是说ThreadLocal为变量在每个线程中都创建了一个副本,每个线程可以访问自己内部的副本变量;

2、作为一个面试常问的点,理解起来就没有那么容易了;

3、它的核心作用就是:实现线程范围的局部变量;

4、在数据库连接的时候,ThreadLocal使用的比较多:

假如这时候有一个客户端频繁的连接数据库资源,服务端需要建立多次的连接和关闭,如果客户量增多,服务器压力太大;这时候可以使用ThreadLocal,它在每个线程中对连接会创建一个副本,且在线程内部任意地方可以使用,线程之间互不影响,这样一来就不存在线程安全问题,也不会影响程序执行性能;

5、ThreadLocal中的set(),是将当前线程对象作为key,线程局部变量作为value存储在ThreadLocalMap中;ThreadLocalMap是ThreadLocal中的一个静态内部类,里面定义了一个Entry来保存数据;

6、也就是说,ThreadLocalMap的键值为ThreadLocal对象,而且可以有多个变量,因此保存在map中;

7、内存泄漏

上图详解了了ThreadLocal和Thread以及ThreadLocalMap三者的关系;

1、Thread中有一个Map,就是ThreadLocalMap;

2、ThreadLocalMap中的key是ThreadLocal,值是我们自己设定的;

3、ThreadLocal是一个弱引用,当为null时,会被当成垃圾回收;

4、如果我们的ThreadLocal是null,也就是要被垃圾回收器回收了,但是ThreadLocalMap生命周期和Thread一样,它不会被回收,这就造成了内存泄漏的问题;

5、解决办法就是:使用完ThreadLocal后,执行remove操作,避免出现内存泄漏的情况;

错题三

URL u =new URL(“http://www.123.com”);。如果www.123.com不存在,则返回______。

我们在执行URL u =new URL("http://www.123.com");这句话的时候确实要抛出异常,但是这个异常属于IOException,是由于字符串格式和URL不符导致的,不管网址是否存在,最后都会返回该网址的一个连接,打印出来就是该网址;

错题四

关键字super的作用是?

1、super关键字用于对父类的访问;

2、可以使用super关键字访问被子类隐藏的变量或者覆盖的方法;

3、每个子类的构造方法第一句都会隐式调用super(),如果父类没有这种形式的构造,编译期间会报错;

4、构造不能被继承;

错题五

以下哪个事件会导致线程销毁?()

1、调用sleep()让线程进入睡眠状态,睡眠指定的时间后再次执行;

2、调用wait()让线程进入等待状态,等待别的线程执行notify()或notifyAll()唤醒后继续执行;

3、调用start()让线程进入就绪状态,得到CPU时间就执行线程;

4、run()方法是线程的具体逻辑方法,执行完,线程就结束。

错题六

下面程序段的时间复杂度是() i = k = 0; while( k < n ){ i ++ ; k += i ; }

在评论区看到一个比较好理解的思路:

i= 1, 2, 3, 4, 5, 6, 7, 8 k= 1, 3, 6, 10, 15, 21, 28, 36 把k的数字两个两个一起看的话, 也就是(1,3), (6,10), (15, 21), (7,8), 求和后可以发现规律: (1+3=4), (6+10=16), (15+21=36), (28+36=64) 也就是2^2, 4^2, 6^2, 8^2...偶数的平方 循环在x^2>=n时终止, 可得x等于根号n, 也就是n^(1/2) 循环的次数是x/2, 时间复杂度为O((1/2)n^(1/2)), 一般而言时间复杂度认为常系数为1, 所以答案就是O(n^(1/2))

错题七

为什么一个负责任的Java程序员想使用嵌套类?

1、为了让非常专门的类的代码与和它一起工作的类联系起来 2、为了支持产生特定事件的新的用户界面 3、为了用Java知识给打动老板,到处使用嵌套类

【使用嵌套类的原因为】:

1、如果B类嵌套在A类里面,即使A类的成员声明为private,B类也可以使用;

2、更可读性:代码在定顶级类里面嵌套小类,让代码更靠近使用的地方;

错题八

final、finally和finalize的区别中,下述说法正确的有?

finalize方法一个对象只能执行一次,只能在第一次进入被回收的队列,而且对象所属于的类重写了finalize方法才会被执行。第二次进入回收队列的时候,不会再执行其finalize方法,而是直接被二次标记,在下一次GC的时候被GC。

错题九

以下JAVA程序的运行结果是什么( ) public static void main(String[] args) { Object o1 = true ? new Integer(1) : new Double(2.0); Object o2; if (true) { o2 = new Integer(1); } else { o2 = new Double(2.0); } System.out.print(o1); System.out.print(" "); System.out.print(o2); }

【三元运算符的计算规则】:

1、若两个操作数不可转换,则不做转换,返回值为Object类型; 2、若两个操作数是明确类型的表达式(比如变量),则按照正常的二进制数字来转换,int类型转换为long类型,long类型转换为float类型等。 3、若两个操作数中有一个是数字S,另外一个是表达式,且其类型标示为T,那么,若数字S在T的范围内,则转换为T类型;若S超出了T类型的范围,则T转换为S类型。 4、若两个操作数都是直接量数字,则返回值类型为范围较大者;

错题十

yield()让当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程获得运行的机会。但实际中无法保证yield()达到让步的目的,因为,让步的线程可能被线程调度程序再次选中。

错题十一

给定以下JAVA代码,这段代码运行后输出的结果是() public class Test { public static int aMethod(int i)throws Exception { try{ return i/10; } catch (Exception ex) { throw new Exception("exception in a aMethod"); }finally{ System.out.printf("finally"); } } public static void main(String[] args){ try { aMethod(0); } catch (Exception ex) { System.out.printf("exception in main"); } System.out.printf("finished"); } }

首先该方法执行正常,没有发生异常,因此依次执行两个try中对应的finally块代码;

算法题一:长键按入

你的朋友正在使用键盘输入他的名字 name。偶尔,在键入字符 c 时,按键可能会被长按,而字符可能被输入 1 次或多次。

你将会检查键盘输入的字符 typed。如果它对应的可能是你的朋友的名字(其中一些字符可能被长按),那么就返回 True。

示例 1: 输入:name = "alex", typed = "aaleex" 输出:true 解释:'alex' 中的 'a''e' 被长按。 示例 2: 输入:name = "saeed", typed = "ssaaedd" 输出:false 解释:'e' 一定需要被键入两次,但在 typed 的输出中不是这样。 示例 3: 输入:name = "leelee", typed = "lleeelee" 输出:true 示例 4: 输入:name = "laiden", typed = "laiden" 输出:true 解释:长按名字中的字符并不是必要的。 提示: name.length <= 1000 typed.length <= 1000 name 和 typed 的字符都是小写字母。

自己的做法:

在做这道题目的时候,我用了一个看起来容易理解的做法解决了,但是时间和空间复杂度都比较高,性能不好:

public boolean isLongPressedName(String name, String typed) { if (typed.length() < name.length()) { return false; } if (typed.length() == name.length() && typed.equals(name)) { return true; } else if (typed.length() == name.length() && !typed.equals(name)) { return false; } StringBuilder regx = new StringBuilder(); for (int i = 0; i < name.length(); i++) { regx.append(name.charAt(i)).append("+"); } return typed.matches(regx.toString()); }

使用正则表达式的特点,将第一个字符串的所有字符拿出来,与+一起拼接成为一个regx正则规则,然后让第二个字符串去匹配他; 为了节省时间,在最开始的时候做出一些判断,如果后一个字符串长度小于前一个字符串的长度,直接返回false; 如果后一个字符串的长度等于前一个字符串的长度,返回是否相等;

官方解法:

双指针

class Solution { public: bool isLongPressedName(string name, string typed) { int i = 0, j = 0; while (j < typed.length()) { if (i < name.length() && name[i] == typed[j]) { i++; j++; } else if (j > 0 && typed[j] == typed[j - 1]) { j++; } else { return false; } } return i == name.length(); } };

【理解】:

1、type中的字符有两个作用:

作为name的一部分,此时会匹配name的一个字符;(此时指针i和j都向前挪动一位)作为长键按入的一部分,此时会和前一个字符相同;(此时指针j向前挪动一位)

2、如果不满足上面的作用,则返回false;

3、在执行完上面的判断之后,再去检查name的没个元素是否都被匹配上了;

算法题二:删除排序数组中的重复项

给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

示例 1: 给定数组 nums = [1,1,2], 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 你不需要考虑数组中超出新长度后面的元素。 示例 2: 给定 nums = [0,0,1,1,1,2,2,3,3,4], 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 你不需要考虑数组中超出新长度后面的元素。 说明: 为什么返回数值是整数,但输出的答案是数组呢? 请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。 你可以想象内部操作如下: // nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 int len = removeDuplicates(nums); // 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。 for (int i = 0; i < len; i++) { print(nums[i]); }

解法:

class Solution { public int removeDuplicates(int[] nums) { int length = nums.length; int i = 0; for (int j = 1; j < length; j++) { if (nums[j] != nums[i]) { i++; nums[i] = nums[j]; } } //System.out.println(Arrays.toString(nums)); return i + 1; } }

这是在学习官方解答之后的代码,这个解法的核心思路是: 1、使用双指针,i指针指向数组中最后一个有意义的元素,j指针指向当前遍历到的元素; 2、如果当前元素不等于数组中最后一个有意义的元素,i 指针挪动一位,i 指针指向的元素的值等于当前遍历到的这个元素; 3、最终返回i指针+1;

算法三:比较含退格的字符串

给定 S 和 T 两个字符串,当它们分别被输入到空白的文本编辑器后,判断二者是否相等,并返回结果。 # 代表退格字符。

注意:如果对空文本输入退格字符,文本继续为空。

示例 1: 输入:S = "ab#c", T = "ad#c" 输出:true 解释:S 和 T 都会变成 “ac”。 示例 2: 输入:S = "ab##", T = "c#d#" 输出:true 解释:S 和 T 都会变成 “”。 示例 3: 输入:S = "a##c", T = "#a#c" 输出:true 解释:S 和 T 都会变成 “c”。 示例 4: 输入:S = "a#c", T = "b" 输出:false 解释:S 会变成 “c”,但 T 仍然是 “b”。 提示: 1 <= S.length <= 200 1 <= T.length <= 200 S 和 T 只含有小写字母以及字符 '#'

自己的做法:

class Solution { public boolean backspaceCompare(String S, String T) { if (S.length() == 0 || T.length() == 0) { return false; } String sNew = dealString(S); String tNew = dealString(T); return sNew.equals(tNew); } private String dealString(String str) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < str.length(); i++) { if (str.charAt(i) == '#') { if (i > 0 && sb.length() > 0) { sb.deleteCharAt(sb.length()-1); } } else { sb.append(str.charAt(i)); } } //System.out.println(sb.toString()); return sb.toString(); } }

【思路】:

1、定义方法,传入当前的字符串,返回值为去掉#之后的字符串; 2、该方法的内部是:遍历字符串,如果当前字符是#,并且StringBuffer长度不为0,则删除最后一个元素;如果当前字符不是#,则拼接进StringBuffer; 3、最终比较两个字符串是否相等;

最新回复(0)