java 数据算法面试题库
8 738最热 | 最新
- 【程序 5】 题目:利用条件运算符的嵌套来完成此题:学习成绩> =90 分的同学用 A 表示,60-89 分之间的用 B 表示,60 分以下的用 C 表示。 1.程序分析:(a> b)?a:b 这是条件运算符的基本例子。 import javax.swing.*; public class ex5 { public static void main(String[] args){ String str=""; str=JOptionPane.showInputDialog("请输入 N 的值(输入 exit 退出):"); int N; N=0; try{ N=Integer.parseInt(str); } catch(NumberFormatException e){ e.printStackTrace(); } str=(N>90?"A":(N>60?"B":"C")); System.out.println(str); } } 【程序 6】 题目:输入两个正整数 m 和 n,求其最大公约数和最小公倍数。 1.程序分析:利用辗除法。 最大公约数: public class CommonDivisor{ public static void main(String args[]) { commonDivisor(24,32); } static int commonDivisor(int M, int N) { if(N<0||M<0) { System.out.println("ERROR!"); return -1; } if(N==0) { System.out.println("the biggest common divisor is :"+M); return M; } return commonDivisor(N,M%N); } } 最小公倍数和最大公约数: import java.util.Scanner; public class CandC { //下面的方法是求出最大公约数 public static int gcd(int m, int n) { while (true) { if ((m = m % n) == 0) return n; if ((n = n % m) == 0) return m; } } public static void main(String args[]) throws Exception { //取得输入值 //Scanner chin = new Scanner(System.in); //int a = chin.nextInt(), b = chin.nextInt(); int a=23; int b=32; int c = gcd(a, b); System.out.println("最小公倍数:" + a * b / c + "\n 最大公约数:" + c); } } 【程序 7】 题目:输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数。 1.程序分析:利用 while 语句,条件为输入的字符不为 '\n '. import java.util.Scanner; public class ex7 { public static void main(String args[]) { System.out.println("请输入字符串:"); Scanner scan=new Scanner(System.in); String str=scan.next(); String E1="[\u4e00-\u9fa5]"; String E2="[a-zA-Z]"; int countH=0; int countE=0; char[] arrChar=str.toCharArray(); String[] arrStr=new String[arrChar.length]; for (int i=0;i<arrChar.length ;i++ ) { arrStr[i]=String.valueOf(arrChar[i]); } for (String i: arrStr ) { if (i.matches(E1)) { countH++; } if (i.matches(E2)) { countE++; } } System.out.println("汉字的个数"+countH); System.out.println("字母的个数"+countE); } } 【程序 8】 题目:求 s=a+aa+aaa+aaaa+aa...a 的值,其中 a 是一个数字。例如 2+22+222+2222+22222(此时共有 5 个数相加),几个数相加有键盘控制。 1.程序分析:关键是计算出每一项的值。 import java.io.*; public class Sumloop { public static void main(String[] args) throws IOException { int s=0; String output=""; BufferedReader stadin = new BufferedReader(new InputStreamReader(System.in)); System.out.println("请输入 a 的值"); String input =stadin.readLine(); for(int i =1;i<=Integer.parseInt(input);i++) { output+=input; int a=Integer.parseInt(output); s+=a; } System.out.println(s); } } 【程序 9】 题目:一个数如果恰好等于它的因子之和,这个数就称为 "完数 "。例如 6=1+2+3.编程 找出 1000 以内的所有完数。 public class Wanshu { public static void main(String[] args) { int s; for(int i=1;i<=1000;i++) { s=0; for(int j=1;j<i;j++) if(i % j==0) s=s+j; if(s==i) System.out.print(i+" "); } System.out.println(); } }
- 【程序 1】 题目:古典问题:有一对兔子,从出生后第 3 个月起每个月都生一对兔子,小兔子长到第四个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 1.程序分析: 兔子的规律为数列 1,1,2,3,5,8,13,21.... public class exp2{ public static void main(String args[]){ int i=0; for(i=1;i<=20;i++) System.out.println(f(i)); } public static int f(int x) { if(x==1 || x==2) return 1; else return f(x-1)+f(x-2); } } 或 public class exp2{ public static void main(String args[]){ int i=0; math mymath = new math(); for(i=1;i<=20;i++) System.out.println(mymath.f(i)); } } class math { public int f(int x) { if(x==1 || x==2) return 1; else return f(x-1)+f(x-2); } } 【程序 2】 题目:判断 101-200 之间有多少个素数,并输出所有素数。 1.程序分析:判断素数的方法:用一个数分别去除 2 到 sqrt(这个数),如果能被整除, 则表明此数不是素数,反之是素数。 public class exp2{ public static void main(String args[]){ int i=0; math mymath = new math(); for(i=2;i<=200;i++) if(mymath.iszhishu(i)==true) System.out.println(i); } } class math { public int f(int x) { if(x==1 || x==2) return 1; else return f(x-1)+f(x-2); } public boolean iszhishu(int x) { for(int i=2;i<=x/2;i++) if (x % 2==0 ) return false; return true; } } 【程序 3】 题目:打印出所有的 "水仙花数 ",所谓 "水仙花数 "是指一个三位数,其各位数字立方和等于该数本身。例如:153 是一个 "水仙花数 ",因为 153=1 的三次方+5 的三次方+3 的三次方。 1.程序分析:利用 for 循环控制 100-999 个数,每个数分解出个位,十位,百位。 public class exp2{ public static void main(String args[]){ int i=0; math mymath = new math(); for(i=100;i<=999;i++) if(mymath.shuixianhua(i)==true) System.out.println(i); } } class math { public int f(int x) { if(x==1 || x==2) return 1; else return f(x-1)+f(x-2); } public boolean iszhishu(int x) { for(int i=2;i<=x/2;i++) if (x % 2==0 ) return false; return true; } public boolean shuixianhua(int x) { int i=0,j=0,k=0; i=x / 100; j=(x % 100) /10; k=x % 10; if(x==i*i*i+j*j*j+k*k*k) return true; else return false; } } 【程序 4】 题目:将一个正整数分解质因数。例如:输入 90,打印出 90=2*3*3*5。 程序分析:对 n 进行分解质因数,应先找到一个最小的质数 k,然后按下述步骤完成: (1)如果这个质数恰等于 n,则说明分解质因数的过程已经结束,打印出即可。 (2)如果 n <> k,但 n 能被 k 整除,则应打印出 k 的值,并用 n 除以 k 的商,作为新的正整数你,重复执行第一步。 (3)如果 n 不能被 k 整除,则用 k+1 作为 k 的值,重复执行第一步。 public class exp2{ public exp2(){} public void fengjie(int n){ for(int i=2;i<=n/2;i++){ if(n%i==0){ System.out.print(i+"*"); fengjie(n/i); } } System.out.print(n); System.exit(0);///不能少这句,否则结果会出错 } public static void main(String[] args){ String str=""; exp2 c=new exp2(); str=javax.swing.JOptionPane.showInputDialog("请输入 N 的值(输入 exit 退出):"); int N; N=0; try{ N=Integer.parseInt(str); }catch(NumberFormatException e){ e.printStackTrace(); } System.out.print(N+"分解质因数:"+N+"="); c.fengjie(N); } }
- 【程序 10】 题目:一球从 100 米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在 第 10 次落地时,共经过多少米?第 10 次反弹多高? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Ex10 { public static void main(String[] args) { double s=0; double t=100; for(int i=1;i<=10;i++) { s+=t; t=t/2; } System.out.println(s); System.out.println(t); } } 【程序 11】 题目:有 1、2、3、4 个数字,能组成多少个互不相同且无重复数字的三位数?都是多少? 1.程序分析:可填在百位、十位、个位的数字都是 1、2、3、4。组成所有的排列后再去 掉不满足条件的排列。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Wanshu { public static void main(String[] args) { int i=0; int j=0; int k=0; int t=0; for(i=1;i<=4;i++) for(j=1;j<=4;j++) for(k=1;k<=4;k++) if(i!=j && j!=k && i!=k) {t+=1; System.out.println(i*100+j*10+k); } System.out.println (t); } } 【程序 12】 题目:企业发放的奖金根据利润提成。利润(I)低于或等于 10 万元时,奖金可提 10%;利润高于 10 万元,低于 20 万元时,低于 10 万元的部分按 10%提成,高于 10 万元的部分,可可提成 7.5%;20 万到 40 万之间时,高于 20 万元的部分,可提成 5%;40 万到 60 万之间时高于 40 万元的部分,可提成 3%;60 万到 100 万之间时,高于 60 万元的部分,可提成 1.5%,高于 100 万元时,超过 100 万元的部分按 1%提成,从键盘输入当月利润 I,求应发放奖金总数? 1.程序分析:请利用数轴来分界,定位。注意定义时需把奖金定义成长整型。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import java .util.*; public class test { public static void main (String[]args){ double sum;//声明要储存的变量应发的奖金 Scanner input =new Scanner (System.in);//导入扫描器 System.out.print ("输入当月利润"); double lirun=input .nextDouble();//从控制台录入利润 if(lirun<=100000){ sum=lirun*0.1; }else if (lirun<=200000){ sum=10000+lirun*0.075; }else if (lirun<=400000){ sum=17500+lirun*0.05; }else if (lirun<=600000){ sum=lirun*0.03; }else if (lirun<=1000000){ sum=lirun*0.015; } else{ sum=lirun*0.01; } System.out.println("应发的奖金是"+sum); } } 后面其他情况的代码可以由读者自行完善.
- 不太好找哦,有的过年后才考虑找工作
- hashmap 原理 ①jdk1.7 底层数组+链表,entry 对象存放 key 和 value,数组存 entry 对象,相同数组下标用链表连着。jdk1.8 是底层数组+链表+红黑树,entry 变 Node 对象,为什么是红黑树呢?红黑树插入和查询速度都比较平衡平均。 ②下标怎么得到? 下标通过 key 的 hash 得到,所以就能很快通过 key 找到数组的位置。 hash 得到的是一个很随机的值,那怎么能作为下标呢? HashMap 的容量-1 做按位与操作,而不是%求余。(这里有个硬性要求,容量必须是 2 的指数倍,如果指定容量大小为 10,其实实际大小是 16,因为 HashMap 的初始化函数中规定容量大小要是 2 的指数倍,即 2,4,8,16,所以当指定容量为 10 时,实际容量为 16。) 这里其实做【位与运算】 【异或运算】相同为 0,不同为 1 【位与运算】只有一个为 1 就是 1 2 的指数倍-1 的二进制就都是左全 0 右全 1。那么跟(2^n - 1)做按位与运算的话,得到的值就一定在【0,(2^n - 1)】区间内,这样的数就刚合适可以用来作为哈希表的容量大小,因为往哈希表里插入数据,就是要对其容量大小取余,从而得到下标。所以用 2^n 做为容量大小的话,就可以用按位与操作替代取余操作,提升计算效率。2.便于动态扩容后的重新计算哈希位置时能均匀分布元素:因为动态扩容仍然是按照 2 的指数倍,所以按位与操作的值的变化就是二进制高位+1,比如 16 扩容到 32,二进制变化就是从 0000 1111(即 15)到 0001 1111(即 31),那么这种变化就会使得需要扩容的元素的哈希值重新按位与操作之后所得的下标值要么不变,要么+16(即挪动扩容后容量的一半的位置),这样就能使得原本在同一个链表上的元素均匀(相隔扩容后的容量的一半)分布到新的哈希表中。 例如:数组默认长度 16,先把 16 减去 1 等于 15,15 的二进制是 1111,然后用得到的 hash 值和这个 1111 做与操作,所以最小是 0,最大是 1111,就是 15。如果扩容到 32,32-1 等于 31,31 二进制是 11111,扩容遍历所有的 entry,原来是链表的现在可能放到数组来。 ③下标冲突 如果得到的下标冲突,就会在那个位置生成一个链表,每次新元素插入到那个链表的头部, 【为什么不是尾部?】不是因为每次遍历才能找到尾部,因为每次 put 需要判断 enry 对象是否已经存在,所以还是需要遍历链表,是因为最后 put 进来的值,也最可能先用到,但是插入到头部还要把当前这个 entry 替换到数组这个位置,因为 get 寻找的时候,链表没法往上找。 jdk1.8 以后是尾插法,因为引入红黑树之后,就需要判断单链表的节点个数(超过 8 个后要转换成红黑树),所以干脆使用尾插法,正好遍历单链表,读取节点个数。也正是因为尾插法,使得 HashMap 在插入节点时,可以判断是否有重复节点。 ④长度 16,16-1,15 二进制是 1111,任何二进制数与 1111 最大都是下标 15,为什么还要用高位右移?右移还能控制在 16 以内吗?什么时候才需要右移? 【异或运算】相同为 0,不同为 1 int 是 4 个字节,32 位,用高 16 位和低 16 位做一个异或运算,然后再用得到的值和容量-1 做位与运算,尽可能多地参与按位与操作,从而减少哈希冲突。 如果 HashMap 容量比较小而 hash 值比较大的时候,哈希冲突就容易变多。基于 HashMap 的 indexFor 底层设计,假设容量为 16,那么就要对二进制 0000 1111(即 15)进行按位与操作,那么 hash 值的二进制的高 28 位无论是多少,都没意义,因为都会被 0&,变成 0。所以哈希冲突容易变多。那么 hash(Obeject k)方法中在调用 k.hashCode()方法获得 hash 值后,进行的一步运算:h^=(h>>>20)^(h>>>12);有什么用呢?首先,h>>>20 和 h>>>12 是将 h 的二进制中高位右移变成低位。其次【异或运算】是利用了特性:同 0 异 1 原则,尽可能的使得 h>>>20 和 h>>>12 在将来做取余(按位与操作方式)时都参与到运算中去。综上,简单来说,通过 h^=(h>>>20)^(h>>>12);运算,可以使 k.hashCode()方法获得的 hash 值的二进制中高位尽可能多地参与按位与操作,从而减少哈希冲突。 ⑤entry 对象有个 hash 字段,这个对象的,为什么呢? 用于判断同一数组位置的链表的对象,hash 不一样绝对不是同一个对象 对象相同,哈希值一定相同,先判断 hash,再判断 key 的值 ⑥jdk1.8 链表和红黑树混用 hash 查找速度 O(1),只需要一次 链表查找速度 O(n),n 个元素就 n 次 红黑树 O(log n) 当链表长度等于 8 就变成链表,小于 6 就换成链表 jdk1.8 以后又加了红黑树,当链表节点个数超过 8 个(m 默认值)以后,开始使用红黑树, 使用红黑树一个综合取优的选择,相对于其他数据结构,红黑树的查询和插入效率都比较高。 而当红黑树的节点个而当红黑树的节点个数小于 6 个(默认值)以后,又开始使用链表。这两个阈值为什么不相同呢? 主要是为了防止出现节点个数频繁在一个相同的数值来回切换,举个极端例子,现在单链表的节点个数是 9,开始变成红黑树,然后红黑树节点个数又变成 8,就又得变成单链表,然后节点个数又变成 9,就又得变成红黑树,这样的情况消耗严重浪费,因此干脆错开两个阈值的大小,使得变成红黑树后“不那么容易”就需要变回单链表,同样,使得变成单链表后,“不那么容易”就需要变回红
- hashmap 原理 ①jdk1.7 底层数组+链表,entry 对象存放 key 和 value,数组存 entry 对象,相同数组下标用链表连着。jdk1.8 是底层数组+链表+红黑树,entry 变 Node 对象,为什么是红黑树呢?红黑树插入和查询速度都比较平衡平均。 ②下标怎么得到? 下标通过 key 的 hash 得到,所以就能很快通过 key 找到数组的位置。 hash 得到的是一个很随机的值,那怎么能作为下标呢? HashMap 的容量-1 做按位与操作,而不是%求余。(这里有个硬性要求,容量必须是 2 的指数倍,如果指定容量大小为 10,其实实际大小是 16,因为 HashMap 的初始化函数中规定容量大小要是 2 的指数倍,即 2,4,8,16,所以当指定容量为 10 时,实际容量为 16。) 这里其实做【位与运算】 【异或运算】相同为 0,不同为 1 【位与运算】只有一个为 1 就是 1 2 的指数倍-1 的二进制就都是左全 0 右全 1。那么跟(2^n - 1)做按位与运算的话,得到的值就一定在【0,(2^n - 1)】区间内,这样的数就刚合适可以用来作为哈希表的容量大小,因为往哈希表里插入数据,就是要对其容量大小取余,从而得到下标。所以用 2^n 做为容量大小的话,就可以用按位与操作替代取余操作,提升计算效率。2.便于动态扩容后的重新计算哈希位置时能均匀分布元素:因为动态扩容仍然是按照 2 的指数倍,所以按位与操作的值的变化就是二进制高位+1,比如 16 扩容到 32,二进制变化就是从 0000 1111(即 15)到 0001 1111(即 31),那么这种变化就会使得需要扩容的元素的哈希值重新按位与操作之后所得的下标值要么不变,要么+16(即挪动扩容后容量的一半的位置),这样就能使得原本在同一个链表上的元素均匀(相隔扩容后的容量的一半)分布到新的哈希表中。 例如:数组默认长度 16,先把 16 减去 1 等于 15,15 的二进制是 1111,然后用得到的 hash 值和这个 1111 做与操作,所以最小是 0,最大是 1111,就是 15。如果扩容到 32,32-1 等于 31,31 二进制是 11111,扩容遍历所有的 entry,原来是链表的现在可能放到数组来。 ③下标冲突 如果得到的下标冲突,就会在那个位置生成一个链表,每次新元素插入到那个链表的头部, 【为什么不是尾部?】不是因为每次遍历才能找到尾部,因为每次 put 需要判断 enry 对象是否已经存在,所以还是需要遍历链表,是因为最后 put 进来的值,也最可能先用到,但是插入到头部还要把当前这个 entry 替换到数组这个位置,因为 get 寻找的时候,链表没法往上找。 jdk1.8 以后是尾插法,因为引入红黑树之后,就需要判断单链表的节点个数(超过 8 个后要转换成红黑树),所以干脆使用尾插法,正好遍历单链表,读取节点个数。也正是因为尾插法,使得 HashMap 在插入节点时,可以判断是否有重复节点。 ④长度 16,16-1,15 二进制是 1111,任何二进制数与 1111 最大都是下标 15,为什么还要用高位右移?右移还能控制在 16 以内吗?什么时候才需要右移? 【异或运算】相同为 0,不同为 1 int 是 4 个字节,32 位,用高 16 位和低 16 位做一个异或运算,然后再用得到的值和容量-1 做位与运算,尽可能多地参与按位与操作,从而减少哈希冲突。 如果 HashMap 容量比较小而 hash 值比较大的时候,哈希冲突就容易变多。基于 HashMap 的 indexFor 底层设计,假设容量为 16,那么就要对二进制 0000 1111(即 15)进行按位与操作,那么 hash 值的二进制的高 28 位无论是多少,都没意义,因为都会被 0&,变成 0。所以哈希冲突容易变多。那么 hash(Obeject k)方法中在调用 k.hashCode()方法获得 hash 值后,进行的一步运算:h^=(h>>>20)^(h>>>12);有什么用呢?首先,h>>>20 和 h>>>12 是将 h 的二进制中高位右移变成低位。其次【异或运算】是利用了特性:同 0 异 1 原则,尽可能的使得 h>>>20 和 h>>>12 在将来做取余(按位与操作方式)时都参与到运算中去。综上,简单来说,通过 h^=(h>>>20)^(h>>>12);运算,可以使 k.hashCode()方法获得的 hash 值的二进制中高位尽可能多地参与按位与操作,从而减少哈希冲突。 ⑤entry 对象有个 hash 字段,这个对象的,为什么呢? 用于判断同一数组位置的链表的对象,hash 不一样绝对不是同一个对象 对象相同,哈希值一定相同,先判断 hash,再判断 key 的值 ⑥jdk1.8 链表和红黑树混用 hash 查找速度 O(1),只需要一次 链表查找速度 O(n),n 个元素就 n 次 红黑树 O(log n) 当链表长度等于 8 就变成链表,小于 6 就换成链表 jdk1.8 以后又加了红黑树,当链表节点个数超过 8 个(m 默认值)以后,开始使用红黑树, 使用红黑树一个综合取优的选择,相对于其他数据结构,红黑树的查询和插入效率都比较高。 而当红黑树的节点个
- 枚举(Enumeration) 枚举(Enumeration)接口虽然它本身不属于数据结构,但它在其他数据结构的范畴里应用很广。 枚举(The Enumeration)接口定义了一种从数据结构中取回连续元素的方式。 例如,枚举定义了一个叫 nextElement 的方法,该方法用来得到一个包含多元素的数据结构的下一个元素。