昨晚的解决方案得到了他的认可,但是磕磕碰碰还是暴露了博主不少的不足,例如考虑问题的全面性,还有代码使用的灵活性,等等。
今天挑战的是个二维数组排序问题。其实排序算法是非常经典的算法了,真要罗列,可能有很多算法,但是常用的大概就是,冒泡,直接插入,归并排序,堆排序等等。的确我对归并和堆排序不是很熟悉,因为曾经在资料上看到一般情况直接排序比较稳定又时间复杂度比较高,遇到问题就只想到了冒泡和直接排序去解决。这可能就是所谓的思维定式吧,要打破束缚应该尝试更多的解决方案,能不能解决问题是一个进步,但是能不能深入探究用更好的方法解决问题才是看出一个人有没有激情的重要考量因素。不过面试也是一个心理战,可能是在考验我的抗压能力吧,不过我还是挺享受这个过程的,因此拖了一天我也非常有兴趣的解决问题,反而是我怕浪费了架构师的时间,博主试探性的问了下,对方也感觉是个极客,交流很nice。我就放手去干了,努力去优化每一个步骤。
题目如下:
public static void main(String[] args) {
assert Arrays.equals(
new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9},
merge(new int[][] {
new int[]{1, 2, 3},
new int[]{4, 5, 6},
new int[]{7, 8, 9}
})
);
assert Arrays.equals(
new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9},
merge(new int[][] {
new int[]{1, 4, 7},
new int[]{2, 5, 8},
new int[]{3, 6, 9}
})
);
assert Arrays.equals(
new int[]{1, 2, 2, 3, 3, 3, 5, 6, 7, 8, 9},
merge(new int[][] {
new int[]{1, 2, 3},
new int[]{2, 3, 5, 6},
new int[]{3, 7, 8, 9}
})
);
System.out.println("Cong!");
虽然被批评我难道不看题么,但是我到昨晚了都没看到题目在哪里,完全是在猜测他的意思,大概就是输入个二维整形矩阵,返回一个融合的升序数组。这很简单嘛:
version 1:
int count = 0;
ArrayList list = new ArrayList();
for(int[] a : orderedLists)
{
count += a.length;
//取出所有orderedLists的值
for(int i : a)
{
list.add(i);
}
}
//初始化一个数组
int[] array = new int[count];
//将值插入
for(int i = 0;i < array.length;i ++)
{
array[i] = list.get(i);
}
//冒泡排序array
for(int i = 0;i < array.length - 1;i ++)
{
for(int j = 0;j < array.length - i - 1;j ++)
{
//若前一个大于后一个,则交换
if(array[j] > array[j + 1])
{
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
return array;
并不难,构建集合去存放数组值,然后随便搞个排序,我用的冒泡,然后转换成数组返回。
但是对方并不满意,希望我不要去遍历,因为遍历的复杂度太高,于是我开始思考其他解决方案:
version 2:
yList list = new ArrayList();
//去遍历这个二维数组的每个值
for(int[] a : orderedLists)
{
for(int i : a)
{
//插入法排序到list
if(list.size() == 0)
{
//插入第一个元素
list.add(i);
}
else
{
//否则插入到最后
list.add(i);
//依次与前面数字比较
for(int j = list.size() - 2;j >= 0;j --)
{
if(list.get(j) > list.get(j + 1))
{
//交换
int temp = list.get(j);
list.set(j, list.get(j + 1));
list.set(j + 1, temp);
}
}
}
}
}
//将list转换为数组返回
int[] array = new int[list.size()];
for(int i = 0;i < array.length ; i ++)
{
array[i] = list.get(i);
}
return array;
这次我用了一次遍历,然后结合插入排序,每次取出数组的值放到list里,结合插入排序方法,虽然对方评论这两个方法差不多,但我感觉这个效率高点。涉及到算法复杂度的问题,没有严格的推倒很难下结论。经常靠“经验”。因此掌握常用算法的时间与空间复杂度还是十分必要的。
对方希望我不用遍历,还给我提供思路,让我每次从第一个数组里选出一个最小的数字,选出后原数组会改变。我可能到现在都还没完全理解他的意思,但是也不太好意思再这么问下去了,我又开始思考。他的意思用堆排序去维护排序,让我有点无力,堆排序我不太熟悉,立刻去查相关资料,涉及到大堆和小堆排序和树形结构,单独去递归遍历树是没问题,但是堆排序的灵活转换让我有点头痛,做了连续一天的算法,堆排序是空间复杂度降低不少,只需要一个单位的辅助空间吧。考虑到对方的耐心,我暂时放弃了堆排序的解决方案,还是插入排序吧。就按他的方式取数据。
如下:
/*
* 方法3
*/
//有序数组
ArrayList list = new ArrayList();
int count = 0;
int max = getMaxLength(orderedLists);
while(count < max)
{
// for(int[] a : orderedLists)
for(int i = 0;i < orderedLists.length;i ++)
{
if(orderedLists[i].length != 0)
{
// int min = getMinOfArray(orderedLists[i]);
list.add(orderedLists[i][0]);
orderedLists[i] = getNewArray(orderedLists[i]);
}
}
count ++;
}
//插入排序list
list = getOrderList(list);
//转换为数组
int[] array = new int[list.size()];
for(int i = 0;i < list.size();i ++)
{
array[i] = list.get(i);
}
return array;
}
//得到最小的值
private static int getMinOfArray(int[] array)
{
return array[0];
}
//重建数组
private static int[] getNewArray(int[] array)
{
int[] newArr = new int[array.length - 1];
for(int i = 1;i < array.length; i ++)
{
newArr[i - 1] = array[i];
}
return newArr;
}
private static int getMaxLength(int[][] orderedLists)
{
int max = 0;
for(int[] a : orderedLists)
{
max += a.length;
}
return max;
}
//插入排序
private static ArrayList getOrderList(ArrayList list)
{
if(list == null)
{
return null;
}
//定义一个列表,是排好序的列表
ArrayList orderedList = new ArrayList();
//首先将第一个值插入到排序好的列表中
orderedList.add(list.get(0));
for(int i = 1;i < list.size();i ++)
{
//先放到最后的位置
orderedList.add(list.get(i));
for(int j = i - 1;j >= 0;j --)
{
if(orderedList.get(j) > list.get(i))
{
orderedList.set(j + 1,orderedList.get(j));
orderedList.set(j,list.get(i));
}
}
}
return orderedList;
}
尽力了。通过笔试,暂时只能做到这样,但是我会去研究堆排序。