这边博客主要是介绍一道面试题的解法。
取一个数组里面第二大的数据。这个题目本身其实不是难,有多种解法,但是比较容易忽略的是这道题目里面其实有几个坑的地方
1. 数组为空或数组只有一个数据,数组非法
2. 数组里面所有数据都相等,不存在第二大的数据
3. 数组里面有多个最大值,
下面来介绍几种不同的方法实现
第一种 排序
/**
* 从一个从大到小排序的数组中选中第二大的数
* @param arr 数组
* @return
* @throws Exception
*/
public static int getSecondMax2(int[] arr) throws Exception {
if (null == arr || arr.length <= 1) { // 数组为空或者数组没有数据数组只有一个数据
throw new Exception("数组非法");
}
BubbleSort.sort(arr);
// 循环遍历
for (int i=0; i < arr.length - 1; i++) {
if (arr[i] == arr[i+1]) { // 相等则继续向下进行
continue;
}
return arr[i + 1]; // 不等直接返回
}
throw new Exception("不存在"); // 说明不存在这样的数据
}
代码分析
首先应该对数组是否合法进行判断
然后是对数组进行排序
后面的代码是需要注意的地方,不可以直接返回第二个数据。因为数组里面可能存在多个最大数据,这个时候第二大的数据根本不在第二个位置。
所以开始循环比较相邻俩个数据是否相等,相等继续,不相等返回当前索引的下一个数据即可,如果比较到最后一个数据还是相等,则表示该数组不存在第二大的数据。
其实这个地方类比插入排序和希尔排序和二分法查找,会发现可以进一步优化,这样会比不想比较相邻的俩个数据快一点。
第二种 比较
public static int getSecondMax(int[] arrs) throws Exception {
if (null == arrs || arrs.length <= 1) {
throw new Exception("数组非法");
}
int max1 = 0, max2 = 0; // 用来记录最大值和第二大的值
for (int i = 0; i < arrs.length; i++) { // 遍历数组
if (i == 0) { // 第一个数据直接赋值给最大值
max1 = arrs[i];
} else { // 从第二个数据开始进行比较
if (arrs[i] == max1) { // 与最大值相等,则不需要执行下面的,循环继续
continue;
}
if (arrs[i] > max1) { // 当前数据大于最大值
max2 = max1; // 将max1赋值给max2
max1 = arrs[i]; // 为max1重新赋值
} else { // 当前数据小于最大值
if (flag) { // max2已经赋值了
max2 = Math.max(max2, arrs[i]); // 为max2重新赋值,比较获取较大的值
} else { // max2没有赋值
max2 = arrs[i];
}
flag = true; // 修改是否为max2赋值的标记
}
}
}
if (!flag) { // 如果max2没有赋值 这说明不存在
throw new Exception("不存在");
}
return max2;
}
第三种 栈
public static int getSecondMax3(int[] arrs) throws Exception {
if (null == arrs || arrs.length <= 1) { // 数组为空或者数组没有数据数组只有一个数据
throw new Exception("数组非法");
}
Stack<Integer> stack1 = new Stack<>();
Stack<Integer> stack2 = new Stack<>();
for (int i = 0; i < arrs.length; i++) {
if (i == 0) { // 第一个数据直接入栈stack1
stack1.push(arrs[i]);
} else {
if (arrs[i] > stack1.peek()) { // 比较当前数据和stack1 的栈顶数据 大于
int temp = stack1.pop(); // 移除stack1栈顶数据
stack1.push(arrs[i]); // 将当前的数据入栈stack1
if (!stack2.empty()) { // 如果栈stack2不为空
if (temp > stack2.peek()) { // 取栈stack2栈顶元素与stack1弹出的栈顶元素比较 小于
stack2.pop(); // stack2 移除栈顶元素
stack2.push(temp); // stack1弹出的栈顶元素 入栈stack2
}
} else { // stack2栈为空
stack2.push(temp); // stack1弹出的栈顶元素 入栈stack2
}
} else if (arrs[i] < stack1.peek()) { // 比较当前数据和stack1 的栈顶数据 小于
if (!stack2.empty()) { // 如果栈stack2不为空
if (arrs[i] > stack2.peek()) { // 比较当前元素和stack2栈顶元素, 大于
stack2.pop(); // 移除stack2栈顶元素
stack2.push(arrs[i]); // 当前元素入栈stack2
}
} else { // stack2栈为空
stack2.push(arrs[i]); // 当前元素直接入栈
}
}
// 当前元素和stack1栈顶元素相等不需要做处理
}
}
if (stack2.empty()) { // 如果stack2栈为空 则说明不存在这样的数据
throw new Exception("不存在");
}
return stack2.pop(); // 存在返回stack2 栈顶元素
}
下面俩种方式代码注释已经解释的很详细,就不在赘述了。