up以前参加的集成电路设计大赛,题目是这样的:
一个单口RAM连续存储着 32*20=640个数的二维数组。单口RAM每个时钟周期只能读取一个数,要求在最短的时间内筛选出五个最大的峰值,并将峰值的坐标输出来。
数组是随机的,当时我用的是Verilog写 的,思路如下:
处理返回坐标时,由于Verilog没有对象的概念,所有是将所有数据进行再加工,比如二维数组第13行21列的数是9,将他转化为91321。即将每个数向左平移四位,再将后四位由该数的坐标填充。
硬件语言并没有java那样灵活,使用9个寄存器来读取数组,每个寄存器存放一个数据再在这组寄存器中筛选出九个数,其中,这九个数有一个是二位数组中间的数,其余八个为它周围的数。如图:
将这九个数据高16位进行比较,最后,比较得出来的二维峰值,筛选出最大的五个二维峰值,顺带将它的位置输出。
现在学习了java,当然也可以用java来实现。
问题分析:
一个二维数组如下
二维峰值是指一个数,比它周围的八个数都要大。比如数组中的244就是一个二维峰值。
最先想到的是将数组遍历,再进行条件判断。
假设峰值为a[x][y],在满足边界条件x>=1,y>=1的那它一定满足
a[x][y]>a[x+1][y]
a[x][y]>a[x-1][y]
a[x][y]>a[x][y+1]
a[x][y]>a[x][y-1]
a[x][y]>a[x+1][y+1]
a[x][y]>a[x+1][y-1]
a[x][y]>a[x-1][y+1]
a[x][y]>a[x-1][y-1]
我们还要考虑x=0,y=0,x=31,y=15时的边界条件。
想到这里,我们就发现这种方式实在太过繁琐。我们需要更懒的方法。
object [][] array ;
array.length 就是行数row
array [0].length 就是列数col
通过观察,可以看到,二维峰值比周围的数大,作为一个二维峰值,他一定属于一维峰值。可以先找出所有的一维峰值,再从一维峰值中找出符合条件的二维峰值。
一维数组的峰值可以用for循环直接查找,简单暴力。
package com.test.demo1;
import java.util.ArrayList;
class Number{
int x;
int y;
int value;
}
class Test{
public ArrayList Find(int [][] array) {
Number number = new Number();
//int a = array.length;//32 hang
//int b = array[0].length;//16 lie
ArrayList<Number> List = new ArrayList<Number>();//存放峰值
for (int i = 0; i < array.length; i++) {
if (array[i][0]>array[i][1]) {//判断每一行的第一个数是否为峰值
number.y = i;
number.x = 0;
number.value = array[i][0];
List.add(number);
}
for (int j = 1; j < array[0].length-1; j++) {//判断每一行的峰值
if ((array[i][j]>array[i][j+1])&&(array[i][j]>array[i][j-1])) {
number.y = i;
number.x = j;
number.value = array[i][j];
List.add(number);
}
}
if (array[i][array[0].length-2]<array[i][array[0].length-1]) {//判断每一行的最后数是否为峰值
number.y = i;
number.x = array[0].length-1;
number.value = array[i][array[0].length-1];
List.add(number);
}
}
for(Number number2 : List ){
System.out.println(number2.x+","+number2.y+","+number2.value);
}
//return List;
return List;
}
public int [][]initArray(int array[][],int x,int y) {
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[0].length; j++) {
array[i][j]=(int)(Math.random()*100);
}
}
return array;
}
public void display(int [][]array){
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[0].length; j++) {
System.out.print(array[i][j]+",");
}
System.out.println();
}
System.out.println("print over");
}
public static void main(String[] args) {
Test s = new Test();
int a[][] = new int[32][16];
s.initArray(a,32, 16);
s.display(a);
s.Find(a);
}
}
发生了一点小bug,检查后得知,我只创建了一个number对象,number对象存放入ArrayLis后,新查询到的数据覆盖前面的数据,再次存放入ArrayList中后,指向的都是同一个对象,所以打印出来的都是相同的数据。
修改如下(其实就是每找出一个值,就new一个Number对象):
package com.test.demo1;
import java.util.ArrayList;
class Number{
int x;
int y;
int value;
}
class Test{
public ArrayList Find(int [][] array) {
Number number;
//int a = array.length;//32 hang
//int b = array[0].length;//16 lie
ArrayList<Number> List = new ArrayList<Number>();//存放峰值
for (int i = 0; i < array.length; i++) {
if (array[i][0]>array[i][1]) {//判断每一行的第一个数是否为峰值
number = new Number();
number.y = i;
number.x = 0;
number.value = array[i][0];
List.add(number);
}
for (int j = 1; j < array[0].length-1; j++) {//判断每一行的峰值
if ((array[i][j]>array[i][j+1])&&(array[i][j]>array[i][j-1])) {
number = new Number();
number.y = i;
number.x = j;
number.value = array[i][j];
List.add(number);
}
}
if (array[i][array[0].length-2]<array[i][array[0].length-1]) {//判断每一行的最后数是否为峰值
number = new Number();
number.y = i;
number.x = array[0].length-1;
number.value = array[i][array[0].length-1];
List.add(number);
}
}
for(Number number2 : List ){
System.out.println(number2.x+","+number2.y+","+number2.value);
}
//return List;
return List;
}
public int [][]initArray(int array[][],int x,int y) {
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[0].length; j++) {
array[i][j]=(int)(Math.random()*100);
}
}
return array;
}
public void display(int [][]array){
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[0].length; j++) {
System.out.print(array[i][j]+",");
}
System.out.println();
}
System.out.println("print over");
}
public static void main(String[] args) {
Test s = new Test();
int a[][] = new int[32][16];
s.initArray(a,32, 16);
s.display(a);
s.Find(a);
}
}
在找到行的峰值后,再在一维峰值中找出那个二维峰值。
思路如下:
先将找出来的峰值进行排序,从大到小进行判断是否满足峰值条件,取到5个峰值后结束。
到这里,需要解决一个问题,如何将存储在ArrayList中的number对象的属性进行排序?
这里我使用了内部匿名类的方法对number对象进行排序。
public ArrayList sort(ArrayList<Number> list){
System.out.println("print over1111111111111111");
Collections.sort(list, new Comparator<Number>() {
@Override
public int compare(Number o1, Number o2) {
//降序
return o2.getValue().compareTo(o1.getValue());
}
});//匿名内部类,重写compare方法,
for (Number number2 : list) {
System.out.println( number2.value );
}
return list;
}
在排序完成后,从ArrayList的第一个number对象开始判断,是否满足二维数组的峰值条件。
public ArrayList top(ArrayList<Number> list,int Array[][]){
Number number3;
int n=0;
ArrayList<Number> list2 = new ArrayList();
for (int i = 0; i < list.size(); i++) {
new Number();
number3.value = list.get(i).value;
int x = number3.x = list.get(i).x;
int y = number3.y = list.get(i).y;
if ( (x==0)&&(number3.value > Array[1][y])){
list2.add(number3);
n++;
if (n==4) {
break;
}
}
if( (number3.value > Array[x][y-1])
&&(number3.value > Array[x][y+1])
&&(number3.value > Array[x-1][y-1])
&&(number3.value > Array[x+1][y+1])
&&(number3.value > Array[x-1][y+1])
&&(number3.value > Array[x+1][y-1])
&&(x>=1)
){
list2.add(number3);
n++;
if (n==4) {
break;
}
}
if ( (x==31)&&(number3.value > Array[31][y])) {
n++;
list2.add(number3);
}
}
for (Number object : list2) {
System.out.println(object.value+","+object.x+","+object.y);
}
return list2;
}
综合在一起,搞定。