【面试题】输出数组的问题

本文介绍了一种使用迭代模式解决特定数组输出问题的方法。通过定义迭代器类,实现了按照特定路径输出数字,适用于多种数组输出场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:输出这样的数组:

01    02    06    07    15    
03    05    08    14    16    
04    09    13    17    22    
10    12    18    21    23    

11    19    20    24    25   

当然你的问题不应该是一位数输出 “1”还是“01”。


这是一类型问题,把数字按照一定的顺序(或者图案、路径)输出,我们姑且称之为“PrintArray”问题吧。

--------------------------------------------------------------------------

遇到这个问题的时候,我们的大部分人会想着怎样在print的时候找到对应的某个位置是那个数,这样的方法也不是说就不行,反正我实在是懒得伤脑筋。那天遇到这个问题的时候,我瞬间想到的是Iterator,熟悉Java集合框架或者STL的朋友肯定对此有所了解:迭代模式。

我们把上面的数看成一个集合,这个集合的元素当然也是需要包装一下的,因为我们在知道元素的值的同时,还想知道它究竟对应于输出时的位置(x,y)。从程序设计的角度讲,元素的迭代方式(即顺序或者路径等等)应该由迭代器决定,元素基本上只是一个pojo,不过我这里只做了简单的实现,并没有把二者分开。

让我们回想一下,STL当中定义的迭代器的类型共有四种,分别是random(随机访问,比如数组,访问时间O(1)),bidirect(双向),forward(单向),input。对于本题来说,bidirect是比较容易实现的,算法也是O(1),而random的话,实际上可以通过执行i次bidirect从形式上实现,但复杂度就成为O(n)了,所以我们只考虑bidirect的情况。

对于bidirect来说,forward和backward实际上经常是逆操作,我下面列出的代码只给出forward,backward的代码看大家自己的兴趣了。

class Iter {
	boolean direction = true;
	int cur = 1;
	int x = 0;
	int y = 0;
	int N;
	int sqrN;

	public Iter(int N) {
		this.N = N;
		this.sqrN = (int) Math.sqrt((double) N);
		if (sqrN * sqrN != N) {
			throw new RuntimeException();
		}
	}

	public boolean forward() {
		if (cur == N) {
			return false;
		}

		if (direction) {
			if (x == sqrN - 1) {
				y += 1;
				direction = false;
			} else if (y == 0) {
				x += 1;
				direction = false;
			} else {
				x += 1;
				y -= 1;
			}

		} else {
			if (y == sqrN - 1) {
				x += 1;
				direction = true;
			} else if (x == 0) {
				y += 1;
				direction = true;
			} else {
				x -= 1;
				y += 1;
			}

		}
		cur++;
		return true;
	}
}
这里的N是输出的最大值,我们要求它是一个完全平方数,当N不符合要求时,抛异常。

代码中的direction可并不是指输出的方向,而是数字在输出时,按照数字顺序输出时呈现在路径上的顺序:从左下角到右上角,或是从右上角到左下角。

这时候有人说我们是要输出这个数组啊,没问题,下面给出main的代码:

public class PrintArray {
	public static void main(String[] args) {
		Iter it = new Iter(25);
		int[][] array = new int[it.sqrN][it.sqrN];
		array[0][0] = it.cur;
		while (it.forward()) {
			array[it.y][it.x] = it.cur;
//			System.out.println(it.cur + "--(" + it.x + "," + it.y + ")");
		}

		for (int i = 0; i < it.sqrN; i++) {
			for (int j = 0; j < it.sqrN; j++) {
				System.out.print(String.format("%02d", array[i][j]) + "\t");
			}
			System.out.println();
		}
	}

}
我们先通过迭代把所有需要输出的数存入一个sqrtN阶的数组中,然后依次打印就行了。你可以把N取得很大,比如我取它为64,那么结果就像这样:

01    02    06    07    15    16    28    29    
03    05    08    14    17    27    30    43    
04    09    13    18    26    31    42    44    
10    12    19    25    32    41    45    54    
11    20    24    33    40    46    53    55    
21    23    34    39    47    52    56    61    
22    35    38    48    51    57    60    62    
36    37    49    50    58    59    63    64   
---------------------------------------------------------------------------------------

这个思路实际上可以用于很多数组输出的问题,比如:

01    28    27    26    25    24    23    22    
02    29    48    47    46    45    44    21    
03    30    49    60    59    58    43    20    
04    31    50    61    64    57    42    19    
05    32    51    62    63    56    41    18    
06    33    52    53    54    55    40    17    
07    34    35    36    37    38    39    16    
08    09    10    11    12    13    14    15   
逆时针旋转的形状输出,那么你只需要修改一下Iter即可。

class Iter2 {
	enum Direction {
		UP, DOWN, LEFT, RIGHT
	}

	Direction direction = Direction.DOWN;

	int cur = 1;
	int x = 0;
	int y = 0;
	int N;
	int sqrN;

	int maxX;
	int maxY;
	int minX;
	int minY;

	public Iter2(int N) {
		this.N = N;
		this.sqrN = (int) Math.sqrt((double) N);
		if (sqrN * sqrN != N) {
			throw new RuntimeException();
		}

		maxX = sqrN - 1;
		maxY = sqrN - 1;
		minX = 0;
		minY = 0;
	}

	public boolean forward() {
		if (cur == N) {
			return false;
		}

		switch (direction) {

		case DOWN:
			if (++y == maxY) {
				direction = Direction.RIGHT;
				++minX;
			} else {

			}
			break;
		case LEFT:
			if (--x == minX) {
				direction = Direction.DOWN;
				++minY;
			}
			break;
		case RIGHT:
			if (++x == maxX) {
				direction = Direction.UP;
				--maxY;
			}
			break;
		case UP:
			if (--y == minY) {
				direction = Direction.LEFT;
				--maxX;
			}
			break;
		default:
			break;

		}
		cur++;
		return true;
	}
}
那么实际上,既然知道了iter可以按照要求输出这些数字,就可以给它配备算法。这种做法有点儿类似STL的算法设计模式。

通过简单的修改forward的代码就可以让数字按照各式各样的图案输出。为了增加趣味,我们让上面的逆时针输出点儿别的:




### 前端面试中与数组相关的题目 #### 使用 `Math.max` 和展开运算符找到最大值 在前端开发的面试过程中,经常会被问到如何利用 JavaScript 的内置函数来解决实际问题。例如,“给定一个数字数组,如何找出其中的最大值?”。可以通过组合使用 `Math.max()` 函数以及扩展运算符 (`...`) 来实现这一目标[^1]。 ```javascript const numbers = [3, 5, 7, 2, 8]; const maxNumber = Math.max(...numbers); console.log(maxNumber); // 输出: 8 ``` #### 将类数组对象转换为真实数组 另一个常见的问题是关于如何处理类数组对象(如 `arguments` 对象),并将其转化为真正的数组以便进一步操作。可以采用传统的 `for` 循环方式完成此任务[^2]: ```javascript function convertToArray() { const newArr = []; for (let i = 0; i < arguments.length; i++) { newArr[i] = arguments[i]; } return newArr; } convertToArray(1, 2, 3); // 返回 [1, 2, 3] ``` 此外,在现代浏览器环境下推荐使用更简洁的方法——`Array.from()` 或者扩展运算符(`...`)来进行转化: ```javascript function convertToArrayModern() { return Array.from(arguments); } // 或者 function convertToArraySpread() { return [...arguments]; } ``` #### 数组去重的不同方法 对于数组去重的问题,也是前端工程师常遇到的一个考点之一。以下是几种不同的解决方案及其特点说明[^3]: ##### 方法一:基于 Set 数据结构 这是最简单高效的方式之一,因为 ES6 中新增加的数据类型 `Set` 自动会过滤掉重复项。 ```javascript function uniqueUsingSet(arr) { return [...new Set(arr)]; } uniqueUsingSet([1, 2, 2, 3]); // 返回 [1, 2, 3] ``` ##### 方法二:双重循环检测法 这种方法虽然效率较低 O(n²),但在某些特定情况下仍然适用。 ```javascript function uniqueDoubleLoop(arr) { const result = []; for (let i = 0; i < arr.length; i++) { if (!result.includes(arr[i])) { result.push(arr[i]); } } return result; } uniqueDoubleLoop(['a', 'b', 'a']); // ['a', 'b'] ``` ##### 方法三:借助 sort 排序后再比较相邻元素 该方案首先对原始数据进行排序,之后只需对比前后两个位置上的数值即可决定是否保留下来。 需要注意的是这种做法无法完全去除所有的重复项目,比如当涉及到特殊类型的值像 `NaN`, `{}` 这样的情况时就会出现问题. ```javascript function uniqueSortAndCompare(arr) { if (!Array.isArray(arr)) { console.error('Input is not an array'); return; } arr.sort(); const res = [arr[0]]; for (let i = 1; i < arr.length; i++) { if (arr[i] !== arr[i - 1]) { res.push(arr[i]); } } return res; } uniqueSortAndCompare([1, 2, 2, 3]); // [1, 2, 3] ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值