目录
编程题
-
另类加法 :
牛客网https://www.nowcoder.com/questionTerminal/e7e0d226f1e84ba7ab8b28efc6e1aebc
给定两个int A和B。编写一个函数返回A+B的值,但不得使用+或其他算数运算符。
测试样例:输入1 和 2, 返回3.
解: 如果不能使用基本的数学运算符, 但是可以使用逻辑运算符, 也就是二进制加法,
对于二进制的逻辑运算法, 我们发现有如下的特点:
位的异或运算跟求'和'的结果一致:
异或 1^1=0 1 ^ 0=1 0^0 = 0
求和 1+1=0 1+0 = 1 0+0 = 0
位的与运算跟求'进位‘的结果一致:
位与 1&1=1 1&0 = 0 0&0 = 0
进位 1+1=1 1+0 = 0 0+0 = 0
于是可以用异或运算和与运算来表示加法
public int addAB(int A, int B) {
int xor,and;
while(B!=0){
xor = A^B;
and = (A&B)<<1;
A=xor;
B=and;
}
return A;
}
-
判断子序列
判断子序列_牛客网https://www.nowcoder.com/practice/39be6c2d0a9b4c30a7b04053d5960a84?tpId=117&tqId=39453&ru=/exam/oj
描述
给定两个字符串 S 和 T ,判断 S 是否是 T 的子序列。
即是否可以从 T 删除一些字符转换成 S。
解题思路1 : 首先将字符串S全部放入一个栈中(以一个一个字符的形式_Character), 让S字符串的首字母在栈顶,随时可以访问到, 然后去遍历另外一个字符串T, 如果这个字符串T 等于的i下标的字符等于栈顶元素, 那么就出栈这个元素, 然后接着遍历, 只要栈为空, 就是子序列, 否则不是子序列, 返回false;
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param S string字符串
* @param T string字符串
* @return bool布尔型
*/
public boolean isSubsequence (String S, String T) {
Stack<Character> stack = new Stack<>();
for(int i = S.length() - 1; i >= 0; i--) {
stack.push(S.charAt(i));
}
for(int i = 0; i < T.length(); i++) {
if (stack.isEmpty()) {
return true;
}
if (T.charAt(i) == stack.peek()) {
stack.pop();
}
}
return stack.isEmpty() ? true : false;
}
}
解题思路2 : 直接使用两个for循环或者是while循环遍历
public boolean isSubsequence (String S, String T) {
int lenS = S.length();
int lenT = T.length();
int i = 0;
int j = 0;
while(i != lenS && j != lenT) {
if (T.charAt(j) == S.charAt(i)) {
i ++;
}
j++;
}
return i == lenS;
}
-
点击消除
解题思路1: 创建一个栈, 在放入一个元素之前, 先看看这个元素是否与栈顶元素, 如果相同那么就把栈顶元素pop出栈, 然后再看下一个元素. 例如: 我们要放入字符串"abbc", 首先把a放入栈, 然后开始遍历, 从左到右遍历, 先是第一个b, 然后是第二个b, 由于栈顶元素是b和当前遍历到的元素相同, 于是就pop出这个b, 然后遍历下一个c, 最后遍历完所有的元素, 栈中只剩下ac, 然后将其输出即可.
实现:
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
Stack<Character> stack = new Stack<>();
String ret = in.nextLine();
int len = ret.length();
int i = 0;
while(i != len) {
if (stack.isEmpty()) {
stack.push(ret.charAt(i));
}else if (stack.peek() == ret.charAt(i)) {
stack.pop();
}else {
stack.push(ret.charAt(i));
}
i ++ ;
}
StringBuffer string = new StringBuffer();
while(!stack.isEmpty()) {
string.append(stack.pop());
}
if (string.length() == 0) {
System.out.println("0");
}else {
for(int j = string.length() - 1; j >=0; j--) {
System.out.print(string.charAt(j));
}
}
}
}
解题思路2 : 其实这个思路2的逻辑和思路1类似, 但是思路1 使用堆栈实现的, 我们接下来的思路是通过一个StringBuilder实现的. 首先我们去遍历字符串, 如果遍历的这个原字符串的元素和StringBuilder的字符串末尾元素相同, 那么就删除StringBuilder的末尾元素, 然后继续遍历原字符串的下一个字符. 如果原字符串遍历的字符不等于StringBuilder 的末位字符, 那么就向StringBuilder里面appenb这个遍历到的原字符串:
实现:
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
Stack<Character> stack = new Stack<>();
String ret = in.nextLine();
StringBuilder string = new StringBuilder();
for(int i = 0; i < ret.length(); i++) {
if (string.length() == 0) {
string.append(ret.charAt(i));
}else if (string.charAt(string.length() - 1) == ret.charAt(i)) {
string.deleteCharAt(string.length() - 1);
} else {
string.append(ret.charAt(i));
}
}
if (string.length() == 0) {
System.out.print("0");
}else {
System.out.print(string.toString());
}
}
}
选择题
-
多线程
线程的启动又t.start调用, 如果只是使用t.run() 方法, 那么这个run()只会被当做普通的方法被调用, 也就是先执行run后打印bar, 输出结果为foobar
-
HashSet
HashSet内部使用Map保存数据,即将HashSet的数据作为Map的key值保存,这也是HashSet中元素不能重复的原因。而Map中保存key值前,会去判断当前Map中是否含有该key对象,内部是先通过key的hashCode,确定有相同的hashCode之后,再通过equals方法判断是否相同。
-
关于ASCLL码和ANSI码的说法
A、标准ASCII只使用7个bit,扩展的ASCII使用8个bit。
B、ANSI通常使用 0x00~0x7f 范围的1 个字节来表示 1 个英文字符。超出此范围的使用0x80~0xFFFF来编码,即扩展的ASCII编码。不同 ANSI 编码之间互不兼容。在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码;在繁体中文Windows操作系统中,ANSI编码代表Big5;在日文Windows操作系统中,ANSI 编码代表 Shift_JIS 编码。
C、ANSI通常使用 0x00~0x7f 范围的1 个字节来表示 1 个英文字符,即ASCII码
D、ASCII码包含一些特殊空字符
-
关于字符串的比较
首先, 两个类的实例test和testB里面都有一个私有的字符串"abc", 首先是使用.equals()方法去比较, 由于没有重写equals方法, 所以传入比较的两个实例对象就会使用Object类的equals方法进行比较:
test.name.equals(test2.name); 是传入两个字符串进行比较, 他会调用String类中的equals方法, 这个equals方法覆盖了Object类中的equals方法:
后面的test.name == test2.name, 由于test.name = "abc"是常量字符串, 被直接放入了常量池当中, 后面创建对象test2的时候, jvm会去常量池中查找这个即将要赋值给变量的常量是否已经存在由于两次新建对象的内容都是一一样的"abc", 所以这个test2.name 的引用就会直接指向常量池中已经存在的字符串"abc", 所以test.name == test2.name 返回true;
-
关于ArrayList增容问题
ArrayList list = new ArrayList(20)中 list扩容几次
首先, 我们new一个无参数的ArrayList的时候, 其实默认初始化的是一个空的数组:
等我们向里面添加元素的时候, 才会扩容到10个元素的容量:
如果你elementData是这个空数组的话, 那么就会扩容到 默认初始值10 和 最小Capacity里面的最大值. 这个minCapacity就是 当前数组的Size + 1:
如果你有0 个元素 , 那么minCapacity(最小需求量)的值就为0 + 1 = 1, 那么这两个值中最大的就是10, 所以第一次增加元素之后, 数组的长度就被扩大到10了.
但是这里面直接传入的参数是20:
于是就调用另外一个构造方法了, 这个构造方法中initialCapacity就是传入的20这个大小的参数, 如果这个值大于0, 那么初始化的数组大小就是你输入的这个值, 如果你输一个0, 那么就会自动初始化一个EMPTY_ELEMENTDATA的空数组.
所以ArrayList list = new ArrayList(20)是直接调用的有参数的构造方法, 扩容0次, 没有调用扩容参数,.
但是如果最小所需要的空间减去数组现在的长度> 0的话, 就会执行grow()方法, 如下
oldCapacity是现在数组的长度, newCapacity是新的数组的长度. 其中newCapacity的值为oldCapacity + (oldCapacity >> 1); 也就是1.5倍扩容, 如果新的容量还是小于最小的需求量, 也就是minCapacity, 那么就直接将这个最小需求量当做新的数组长度, 也就是newCapacity = minCapacity, 如果新扩容的newCapacity比MAX_ARRAY_SIZE还要大, 那么就调用hugeCapacity方法来扩容:
这个MAX_ARRAY_SIZE也就是Integer的最大值 - 8:
所以, 如果最小需求量大于Integer最大值- 8 的时候, 就返回Integer的最大值, 否则就返回Integer的最大值-8.
-
二叉树: 由 3个节点构成的二叉树有( )种形态
可有卡特兰数公式 NTypes = C(2n,n) / (n + 1)可得
一共有 ( 6! / (3! * 3!)) / (3 + 1) = 5种
也可以一一列举来做
-
字符串拼接问题
首先, 在进行输出的时候, 字符串 + 基本数据类型 可以组合成一个拼接字符串, 例如
public class Main {
public String name = "abc";
public static void main(String[] args) {
Main tem = new Main();
System.out.println( tem.name + true);
}
}
将会输出字符串: "abctrue". 当然不只是boolean类型, 还有其他的数据类型也支持字符串拼接, 但是在拼接的过程中, 计算的顺序时从左向右的, 例如执行:
System.out.print("is"+ 100 + 5);
和
System.out.print(100 + 5 + "is"); 的结果是不一样的,
第一个结果为 "is1005", 第二个的结果为: "105is".
- 对继承的理解
首先对于这个类, 父类中的run方法被private修饰, 所以不能被子类继承, 所以并不和子类中的run方法构成重写, 因此不与父类中run方法的final所冲突. 所以直接输出Car中的run方法.
-
对静态字段的理解
public class P {
public static int abc = 123;
static{
System.out.println("P is init");
}
}
public class S extends P {
static{
System.out.println("S is init");
}
}
public class Test {
public static void main(String[] args) {
System.out.println(S.abc);
}
}
这里的<br / > 为换行, 这里是牛客网的一个bug.
首先, 属于被引用的不会触发子类的初始化,不会初始化子类的几种:
1. 调用的是父类的static方法或者字段
2.调用的是父类的final方法或者字段
3. 通过数组来引用