数据结构与算法面试系列-02
数据结构与算法面试系列-02
1. 一个整数,它加上100后是一个完全平方数,加上168又是一个完全平方数,请问该数是多少?
程序分析:在10万以内判断,先将该数加上100后再开方,再将该数加上168后再开方,如果开方后的结果满足如下条件,即是结果。请看具体分析:
程序代码如下:
package com.yoodb.util;
public class Demo03 {
public static void main(String[] args) {
for (int i = 1; i < 1000; i++) {
int m = (int) Math.sqrt((i + 100));
int n = (int) Math.sqrt((i + 100 + 168));
if (m * m == i + 100 && n * n == i + 100 + 168) {
System.out.println("这个数是" + i);
}
}
}
}
运行结果如下:
这个数是21
这个数是261
2. 输入某年某月某日,判断这一天是这一年的第几天?
程序分析:以3月5日为例,应该先把前两个月的加起来,然后再加上5天即本月的第几天,特殊情况,闰年且输入月份大于3时需考虑多加一天。
程序代码如下:
package com.yoodb.util;
import java.util.GregorianCalendar;
import java.util.Scanner;
public class Demo04 {
private static Scanner scan;
public static void main(String[] args) {
scan = new Scanner(System.in);
System.out.println("输入年份:");
int year = scan.nextInt();
System.out.println("输入月份:");
int month = scan.nextInt();
System.out.println("输入日期:");
int day = scan.nextInt();
//判断是否是闰年,GregorianCalendar:判断年份是否是闰年的方法
GregorianCalendar gre = new GregorianCalendar();
boolean isLeapYear = gre.isLeapYear(year);//返回true:是闰年,false:不是闰年
int ap = isLeapYear ? 29 : 28;//判断2月份的天数
int days = 0;
switch (month) {
case 1:
days = day;
break;
case 2:
days = 31 + day;
break;
case 3:
days = 31 + ap + day;
break;
case 4:
days = 31 + ap + 31 + day;
break;
case 5:
days = 31 + ap + 31 + 30 + day;
break;
case 6:
days = 31 + ap + 31 + 30 + 31 + day;
break;
case 7:
days = 31 + ap + 31 + 30 + 31 + 30 + day;
break;
case 8:
days = 31 + ap + 31 + 30 + 31 + 30 + 31 + day;
break;
case 9:
days = 31 + ap + 31 + 30 + 31 + 30 + 31 + 31 + day;
break;
case 10:
days = 31 + ap + 31 + 30 + 31 + 30 + 31 + 31 + 30 + day;
break;
case 11:
days = 31 + ap + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + day;
break;
case 12:
days = 31 + ap + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + day;
break;
default:
System.out.println("月份输入错误!");
break;
}
System.out.println("这一天是这一年的第" + days + "天");
}
}
运行结果如下:
输入年份:
2020
输入月份:
2
输入日期:
10
这一天是这一年的第41天
3. 有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?
程序分析:可填在百位、十位、个位的数字都是1、2、3、4。组成所有的排列后再去掉不满足条件的排列。关注Java精选公众号,内涵算法题100+道面试题,其他面试题3000+道题。
程序代码如下:
package com.yoodb.util;
public class Demo05 {
public static void main(String[] args) {
int count = 0;
for (int x = 1; x < 5; x++) {
for (int y = 1; y < 5; y++) {
for (int z = 1; z < 5; z++) {
if (x != y && y != z && x != z) {
count++;
System.out.print(x * 100 + y * 10 + z + "\t");
if (count % 4 == 0) {
System.out.println();
}
}
}
}
}
System.out.println("共有" + count + "个三位数");
}
}
运行结果如下:
123 124 132 134
142 143 213 214
231 234 241 243
312 314 321 324
341 342 412 413
421 423 431 432
共有24个三位数
4. 有5个人坐在一起,问第五个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第三个人,又说比第2人大两岁。问第2个人,说比第一个人大两岁。最后问第一个人,他说是10岁。请问第五个人多大?
程序分析:
利用递归的方法,递归分为回推和递推两个阶段。要想知道第五个人岁数,需知道第四人的岁数,依次类推,推到第一人(10岁),再往回推。
程序代码
public class Demo09 {
public static int getAge(int n) {
if (n == 1) {
return 10;
}
return 2 + getAge(n - 1);
}
public static void main(String[] args) {
System.out.println("第五个的年龄为" + getAge(5));
}
}
运行结果
第五个的年龄为18
5. 给一个不多于5位的正整数,要求:一、求它是几位数,二、逆序打印出各位数字。
程序分析:
利用正则表达式判断是否输入信息为数字,通过调用字符串的封装源码方法,进行逆序排序。
程序代码
import java.util.Scanner;
public class Demo10 {
private static Scanner in;
public static void main(String[] args) {
System.out.println("请输入数字:");
in = new Scanner(System.in);
String str = in.next();
if (str.matches("\\d+")) {
System.out.print("输入的是" + str.length() + "位数");
StringBuffer buf = new StringBuffer(str);
System.out.println("逆序打印:" + buf.reverse());
}
}
}
运行结果
请输入数字:
871236
输入的是6位数,逆序打印:632178
6. 一个5位数,判断它是不是回文数。即12321是回文数,个位与万位相同,十位与千位相同。
程序分析
首先判断输入内容是否为五位数数字,之后通过循环判断个位与万位相等,十位与千位相等。
程序代码
import java.util.Scanner;
public class Demo11 {
private static Scanner in;
public static void main(String[] args) {
System.out.println("请输入数字:");
in = new Scanner(System.in);
String str = in.next();
int l = Integer.parseInt(str);
if (l < 10000 || l > 99999) {
System.out.println("请输入正确的五位数字!");
System.exit(0);
}
boolean is = false;
char[] ch = str.toCharArray();
for (int i = 0; i < ch.length / 2; i++) {
if (ch[i] != ch[ch.length - i - 1]) {
is = false;
} else {
is = true;
}
}
if (is) {
System.out.println("这是一个回文!");
} else {
System.out.println("不是一个回文!");
}
}
}
运行结果
请输入数字:
12321
这是一个回文!
7. 请输入星期几的第一个字母来判断一下是星期几,如果第一个字母一样,则继续判断第二个字母。
程序分析:
用情况语句比较好,如果第一个字母一样,则判断用情况语句或if语句判断第二个字母。
程序代码
import java.util.Scanner;
public class Demo12 {
private static Scanner in;
public static void main(String[] args) {
char weekSecond;
in = new Scanner(System.in);
System.out.println("请输入星期的第一个字母:");
String letter = in.next();
if (letter.length() == 1) {
char weekFirst = letter.charAt(0);
switch (weekFirst) {
case 'm':
case 'M':
System.out.println("星期一(Monday)");
break;
case 't':
case 'T':
System.out
.print("由于星期二(Tuesday)与星期四(Thursday)均以字母T开头,故需输入第二个字母才能正确判断:");
letter = in.next();
if (letter.length() == 1) {
weekSecond = letter.charAt(0);
if (weekSecond == 'U' || weekSecond == 'u') {
System.out.println("星期二(Tuesday)");
break;
} else if (weekSecond == 'H' || weekSecond == 'h') {
System.out.println("星期四(Thursday)");
break;
} else {
System.out.println("Error!");
break;
}
} else {
System.out.println("输入错误,只能输入一个字母,程序结束!");
break;
}
case 'w':
case 'W':
System.out.println("星期三(Wednesday)");
break;
case 'f':
case 'F':
System.out.println("星期五(Friday)");
break;
case 's':
case 'S':
System.out
.print("由于星期六(Saturday)与星期日(Sunday)均以字母S开头,故需输入第二个字母才能正确判断:");
letter = in.next();
if (letter.length() == 1) {
weekSecond = letter.charAt(0);
if (weekSecond == 'A' || weekSecond == 'a') {
System.out.println("星期六(Saturday)");
break;
} else if (weekSecond == 'U' || weekSecond == 'u') {
System.out.println("星期日(Sunday)");
break;
} else {
System.out.println("Error!");
break;
}
} else {
System.out.println("输入错误,只能输入一个字母,程序结束!");
break;
}
default:
System.out.println("输入错误,不能识别的星期值第一个字母,程序结束!");
break;
}
} else {
System.out.println("输入错误,只能输入一个字母,程序结束!");
}
}
}
运行结果
请输入星期的第一个字母:
m
星期一(Monday)
8. 判断 0-100 之间有多少个素数,并输出所有素数。
程序分析:
质数(prime number)又称素数,有无限个。
质数定义为在大于1的自然数中,除了1和它本身以外不再有其他因数。
程序代码
public class Demo13 {
public static void main(String args[]) {
int sum, i;
for (sum = 2; sum <= 100; sum++) {
for (i = 2; i <= sum / 2; i++) {
if (sum % i == 0)
break;
}
if (i > sum / 2)
System.out.print(sum + " ");
}
System.out.println("都是素数");
}
}
或
public class Demo131 {
public static void main(String args[]) {
int w = 1;
for (int i = 2; i <= 100; i++) {
for (int j = 2; j < i; j++) {
w = i % j;
if (w == 0)
break;
}
if (w != 0)
System.out.print(i + " ");
}
System.out.println("都是素数");
}
}
运行结果
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 都是素数
9. 如何对 10 个数从小到大进行排序?
程序分析
可以利用选择法,即从后9个比较过程中,选择一个最小的与第一个元素交换,下次类推,即用第二个元素与后8个进行比较,并进行交换。
程序代码
import java.util.Arrays;
import java.util.Scanner;
public class Demo14 {
private static Scanner in;
public static void main(String[] args) {
System.out.println("请输入10个数:");
in = new Scanner(System.in);
int[] arr = new int[10];
for (int i = 0; i < 10; i++) {
arr[i] = in.nextInt();
}
System.out.println("原数组为:");
for (int x : arr) {
System.out.print(x + "\t");
}
Arrays.sort(arr);
System.out.println();
System.out.println("排序后为:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "\t");
}
}
}
运行结果
请输入10个数:
43 343 98 93 329 23 12 34 54 68
原数组为:
43 343 98 93 329 23 12 34 54 68
排序后为:
12 23 34 43 54 68 93 98 329 343
10. 求一个3*3矩阵主对角线元素之和。
程序分析:
主对角线(principal diagonal)是从左上角到右下角的对角线。
利用双重for循环控制输入二维数组,再将a[i][j]累加后输出。
程序代码
public class Demo15 {
public static void main(String[] args) {
int sum = 0;
int array[][] = { { 2, 7, 3 }, { 4, 9, 6 }, { 5, 1, 8 } };
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) {
if (i == j)
sum = sum + array[i][j];
}
System.out.println("对角线元素之和:" + sum);
}
}
运行结果
对角线元素之和:19
11. 有一个已经排好序的数组。现输入一个数,要求按原来的规律将它插入数组中。
程序分析:
首先随机生成一个混乱的数组,通过判断每个数是否大于最后一个数,然后再考虑插入中间的数的情况,插入后此元素之后的数,依次后移一个位置。
程序代码
import java.util.Random;
public class Demo16 {
public static void main(String[] args) {
int temp = 0;
int arr[] = new int[12];
Random r = new Random();
for (int i = 0; i <= 10; i++)
arr[i] = r.nextInt(1000);
for (int i = 0; i <= 10; i++)
System.out.print(arr[i] + "\t");
for (int i = 0; i <= 9; i++)
for (int k = i + 1; k <= 10; k++)
if (arr[i] > arr[k]) {
temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
}
System.out.println();
for (int k = 0; k <= 10; k++)
System.out.print(arr[k] + "\t");
arr[11] = r.nextInt(1000);
for (int k = 0; k <= 10; k++)
if (arr[k] > arr[11]) {
temp = arr[11];
for (int j = 11; j >= k + 1; j--)
arr[j] = arr[j - 1];
arr[k] = temp;
}
System.out.println();
for (int k = 0; k <= 11; k++)
System.out.print(arr[k] + "\t");
}
}
运行结果
265 882 280 873 848 283 423 549 569 494 52
52 265 280 283 423 494 549 569 848 873 882
52 265 280 283 423 494 549 569 701 848 873 882
12. 输入两个正整数m和n,求其最大公约数和最小公倍数。
程序源码
import java.util.Scanner;
public class Tests {
public static void main(String[] args) {
int a, b, num1, num2, temp;
System.out.println("请你输入需要计算的两个数字:\n");
Scanner sc = new Scanner(System.in);
num1 = sc.nextInt();
num2 = sc.nextInt();
if (num1 < num2)/* 交换两个数,使大数放在num1上 */
{
temp = num1;
num1 = num2;
num2 = temp;
}
a = num1;
b = num2;
//辗转相除法一般指欧几里得算法
while (b != 0)/* 利用辗除法,直到b为0为止 */
{
temp = a % b;
a = b;
b = temp;
}
System.out.println("公约数" + a);
System.out.println("公倍数" + num1 * num2 / a);
}
}
输出结果
请你输入需要计算的两个数字:
65
78
公约数13
公倍数390
13. 什么是希尔排序?
希尔排序(Shell Sort)是DL.Shell在1959年提出的,是插入排序的一种,它是是直接插入排序算法的一种更高版本的改进版本。
其实质是一种分组排序。把数据分成几组,然后再进行组内插入排序,不断重复这样的分组过程,直到只比较相邻元素的最后一趟排序为止。
通俗的讲就是把记录按步长gap分组,对每组记录采用直接插入排序方法进行排序;随着步长逐渐减小,所分成的组包含的记录越来越多;当步长值减小到1时,整个数据合成一组,构成一组有序记录,完成排序。
14. Java 中如何编写一个希尔排序算法?
解题思路
假设一组数[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ],如果以步长为5开始进行排序,可以将这列表放在有5列的表中来更好地描述算法,这样他们就应该看起来是这样(竖着的元素是步长组成):
13 14 94 33 82
25 59 94 65 23
45 27 73 25 39
10
然后对每列进行排序:
10 14 73 25 23
13 27 94 33 39
25 59 94 65 82
45
将上述四行数字,依序接在一起时得到:[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ]。这时10已经移至正确位置,然后再以3为步长进行排序:
10 14 73
25 23 13
27 94 33
39 25 59
94 65 82
45
排序之后变为:
10 14 13
25 23 33
27 25 59
39 65 73
45 94 82
94
最后以1步长进行排序,此时就是简单的插入排序。
程序代码
Java中实现希尔排序算法代码如下:
package com.jingxuan.system;
import java.util.Arrays;
public class ShellSort {
public static void main(String[] args) {
int[] num = { 7, 6, 9, 3, 1, 5, 2, 4 };
System.out.println("未排序的数组:" + Arrays.toString(num));
// 增量序列的选择没有具体的公式,可以根据自己的数据取个合适的增量序列
for (int increment = num.length / 2; increment > 0; increment = increment / 2) {
// 根据增量对数组进行分组
for (int i = increment; i < num.length; i++) {
int index = i;
// 进行插入排序
// 注意:index-increment值的变化
while ((index - increment) >= 0 && num[index] < num[index - increment]) {
int temp = num[index];
num[index] = num[index - increment];
num[index - increment] = temp;
index = index - increment;
}
}
}
System.out.println("排序后的数组:" + Arrays.toString(num));
}
}
运行结果
未排序的数组:[7, 6, 9, 3, 1, 5, 2, 4]
排序后的数组:[1, 2, 3, 4, 5, 6, 7, 9]
15. 什么是二分法?使用时注意事项?
二分法查找(Binary Search)也称折半查找,是指当每次查询时,将数据分为前后两部分,再用中值和待搜索的值进行比较,如果搜索的值大于中值,则使用同样的方式(二分法)向后搜索,反之则向前搜索,直到搜索结束为止。
二分法使用的时候需要注意:二分法只适用于有序的数据,也就是说,数据必须是从小到大,或是从大到小排序的。
16. Java 中如何实现二分法算法?
时间复杂度
1)最坏情况查找最后一个元素(或者第一个元素)Master定理T(n)=T(n/2)+O(1)所以T(n)=O(logn)。
2)最好情况查找中间元素O(1)查找的元素即为中间元素(奇数长度数列的正中间,偶数长度数列的中间靠左的元素)。
空间复杂度
S(n)=n
分析
在有序的N个元素的数组中查找输进去的数据x值。算法如下:
1)确定查找范围front=0,end=N-1,计算中项mid=(front+end)/2。
2)若a[mid]=x或front>=end,则结束查找;否则,向下继续。
3)若a[mid]<x,说明待查找的元素值只可能在比中项元素大的范围内,则把mid+1的值赋给front,并重新计算mid,转去执行步骤2;若a[mid]>x,说明待查找的元素值只可能在比中项元素小的范围内,则把mid-1的值赋给end,并重新计算mid,转去执行步骤2。
实例
package com.jingxuan.system;
public class BinarySearch {
public static void main(String[] args) {
// 二分法查找
int[] binaryNums = { 1, 6, 15, 18, 27, 50 };
int findValue = 27; // 查找27值
int binaryResult = binarySearch(binaryNums, 0, binaryNums.length - 1, findValue);
System.out.println("元素第一次出现的位置(索引从0开始):" + binaryResult);
}
/**
* 二分查找,返回该值第一次出现的位置(下标从 0 开始)
*
* @param nums 查询数组
* @param start 开始下标
* @param end 结束下标
* @param findValue 要查找的值
* @return int
*/
private static int binarySearch(int[] nums, int start, int end, int findValue) {
if (start <= end) {
// 中间位置
int middle = (start + end) / 2;
// 中间的值
int middleValue = nums[middle];
if (findValue == middleValue) {
// 等于中值直接返回
return middle;
} else if (findValue < middleValue) {
// 小于中值,在中值之前的数据中查找
return binarySearch(nums, start, middle - 1, findValue);
} else {
// 大于中值,在中值之后的数据中查找
return binarySearch(nums, middle + 1, end, findValue);
}
}
return -1;
}
}
执行结果如下:
元素第一次出现的位置(索引从0开始):4
17. 什么是斐波那契数列?
斐波那契数列(Fibonacci Sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711…… 在数学上,斐波那契数列以如下被以递推的方法定义:
F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用。
斐波那契数列之所以又称黄金分割数列,是因为随着数列项数的增加,前一项与后一项之比越来越逼近黄金分割的数值 0.6180339887……
斐波那契数列指的是这样一个数列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711……
斐波那契数列的特征:第三项开始(含第三项)它的值等于前两项之和。
18. Java 中实现斐波那契数列有哪些方法?
递归实现
当输入n时,获取斐波那契数列的第n个数的值。
public static int fibonacci(int n){
if (n == 1 || n == 2){ //特殊情况,分开讨论
return 1;
}
if (n > 2) {
return fibonacci(n - 1) + fibonacci(n - 2); //递归调用
}
return -1; //如果输入错误的n,一律返回-1
}
这种实现方式比较简单,但是效率低。当n>=40时,会发现计算时间明显变长,当n接近50时,运行窗口需等待很久才有反应。
注意:由于int的取值范围有限,最大值为(2^32)-1 = 2147483647,当n>46的时候,会发生取值范围溢出的情况,所以这里如果想要验证n>46时的计算耗时情况,请将返回值类型int改为long。
for循环实现
public static long fibonacci2(int n) {
if (n < 1) {
return -1;
}
if (n ==1 || n == 2) {
return 1;
}
long a =1l, b= 1l, c =0l; //定义三个long类型整数
for (int i = 0; i < n - 2; i++) {
c = a + b; //第3个数的值等于前两个数的和
a = b; //第2个数的值赋值给第1个数
b = c; //第3个数的值赋值给第2个数
}
return c;
}
相比第一种方式明显计算速度提高了不是一点两点,哪怕n>10000,都能瞬间完成计算。
for循环和数组方式实现
类似第二种实现方式,只不过把数据都放到数组中,可以取出斐波那契数列的第1个一直到第n个的数值。采用long类型,防止溢出。
public static long fibonacci3(int n) {
if (n < 1) {
return -1;
}
if (n == 1 || n == 2) {
return 1;
}
long[] arr = new long[n];
arr[0] = arr[1] = 1; //第一个和第二个数据特殊处理
for (int i = 2; i < n; i++) {
arr[i] = arr[i -2] + arr[i - 1];
arr[n -1] = arr[i]; //数列第n个数对应数组arr[n - 1]
}
return arr[n - 1];
}
19. 求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个0~9之间的数字。例如2+22+222+2222+22222(此时共有5个数相加),几个数相加有键盘控制。
欢迎大家关注微信公众号: Java精选 ,专注分享前沿资讯,BATJ 大厂面试题解读,架构技术干货,微服务、高可用等架构设计,10年开发老兵帮你少走弯路,欢迎各领域程序员交流学习!
此类面试题只能在微信小程序: Java精选面试题 ,查阅全部内容,感谢支持!
20. Java 中如何计算出 1000 以内的所有完数?
程序分析
完全数(Perfect number),又称完美数或完备数,是一些特殊的自然数。它所有的真因子(即除了自身以外的约数)的和(即因子函数),恰好等于它本身。如果一个数恰好等于它的因子之和,则称该数为“完全数”。第一个完全数是6,第二个完全数是28,第三个完全数是496,后面的完全数还有8128、33550336等等。
第一个完全数是6,它有约数1、2、3、6,除去它本身6外,其余3个数相加,1+2+3=6。
程序代码
package com.jingxuan.system;
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.println(i);
}
}
}
执行结果
6
28
496