17

У меня есть двумерный массив, скажемПолучить смежные элементы в двумерном массиве?

0 0 0 0 0 
0 2 3 4 0 
0 9 1 5 0 
0 8 7 6 0 
0 0 0 0 0 

И мне нужно, чтобы получить все числа, прилегающих к 1 (2, 3, 4, 5, 6, 7, 8, 9)

Существует ли менее уродливое решение, чем:

topLeft = array[x-1][y-1] 
top = array[x][y-1] 
topRight = array[x+1][y-1] 
# etc 

Спасибо!

+0

@Nick D: Почему удалить '2d' из названия? –

+0

@ Ian P: Я предполагаю, потому что это было излишним.«Двумерное» - это то же самое, что «2D». –

ответ

17

Если вы не беспокоитесь о порядке, чистейшее, вероятно, использовать пару петель:

result = new List<int>(8); 
for (dx = -1; dx <= 1; ++dx) { 
    for (dy = -1; dy <= 1; ++dy) { 
     if (dx != 0 || dy != 0) { 
      result.Add(array[x + dx][y + dy]); 
     } 
    } 
} 

Если порядок важен, вы можете составить список всех (Dx, Dy) в том порядке, в котором вы хотите, и вместо этого перебирайте это.

Как указано в комментариях, вы, вероятно, захотите добавить пограничные проверки. Вы можете сделать это, как этот (предполагается, что порядок не имеет значения):

List<int> result = new List<int>(8); 
for (int dx = (x > 0 ? -1 : 0); dx <= (x < max_x ? 1 : 0); ++dx) 
{ 
    for (int dy = (y > 0 ? -1 : 0); dy <= (y < max_y ? 1 : 0); ++dy) 
    { 
     if (dx != 0 || dy != 0) 
     { 
      result.Add(array[x + dx][y + dy]); 
     } 
    } 
} 
+0

Необходимо учитывать краевые случаи. Если спецификация (x, y) равна (0, 0), то индексы вашего массива будут за пределами границ. Так как это выглядит как код C#, это означает, что вы получите исключение. – Eilon

+0

@Eilon: обновлен с помощью пограничных проверок. –

1

В C++ это может выглядеть следующим образом:

vector<int> adj; 
for (int i = 0; i < 9; i++) 
    if (i != 4) adj.push_back(array[x + i/3 - 1][y + i%3 - 1]); 

Это не очень понятно решение, но очень мало.

+0

это решение вообще не обобщается – twolfe18

+0

@ twolfe18 спасибо, исправлено – sergtk

11

я бы, вероятно, пойти на постоянный список Dx, Dy для каждого направления, например, так:

struct { 
    int dx; 
    int dy; 
} directions[] = {{-1,-1,},{-1,0,},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}}; 

Тогда вы итерацию по направлениям с помощью простого цикла:

for (int i = 0; i < 8; i++) { 
    // use x + directions[i].dx; 
    // use y + directions[i].dy; 
} 

Вы можете, конечно, использовать sizeof(directions)/sizeof(directions[1]) вместо 8 выше.

+0

это, вероятно, самая красивая реализация –

+0

Привет, что бы 'x' и' y' были здесь? Благодарю. – Unheilig

7

лично, петли более уродливые, чем оригинальные.

topLeft = array[ x - 1 ][ y - 1 ] 
top  = array[ x  ][ y - 1 ] 
topRight = array[ x + 1 ][ y - 1 ] 

midLeft = array[ x - 1 ][ y  ] 
midRight = array[ x + 1 ][ y  ] 

botLeft = array[ x - 1 ][ y + 1 ] 
bot  = array[ x  ][ y + 1 ] 
botRight = array[ x + 1 ][ y + 1 ] 

Но без указания того, что вы хотите значение - то, что вы делаете в различных направлениях, подразумевает, хотите ли вы значение в отдельных переменных или нет.

Для игры в стиле жизни вы обычно хотите работать на битпаттере в любом случае, а не в массиве отдельных значений, и вы можете сканировать горизонтально, проверяя только три из восьми ячеек за раз с использованием аккумуляторов и временных рядов. Для графических сверток используйте существующую библиотеку с ядром 3x3.

Другим способом борьбы с границами является расширение массива на одну ячейку в каждом направлении. Это позволяет избежать дорогостоящих ветвей в коде свертки.

+0

При поиске чего-то другого я наткнулся на ваш ответ. Очень приятное решение. Интересно ... можно ли применить что-то подобное, если массив был похож на пирамиду Паскаля? Не «пирамида» Паскаля, просто форма: одна запись сверху, две в середине и три внизу? –

0

Вот решение Ruby. Алгоритм должен быть очевидным даже для читателей, которые не знакомы с Ruby.

def adjacent(arr, r, c) 
    last_row, last_col = arr.size-1, arr.first.size-1 
    ([r-1,0].max..[r+1,last_row].min).each_with_object([]) do |i, a| 
    ([c-1,0].max..[c+1,last_col].min).each { |j| a << arr[i][j] unless i==r && j==c } 
    end 
end 

arr = [ 
    [-1, 2, 3, 4], 
    [-2, 9, 1, 5], 
    [-3, 8, 7, 6], 
    [-4, -5, -6, -7] 
] 

(0..2).each do |i| 
    (1..3).each do |j| 
    puts "adjacent to #{arr[i][j]} at r=#{i}, c=#{j} = #{adjacent(arr, i, j)}" 
    end 
end 

печатает

adjacent to 2 at r=0, c=1 = [-1, 3, -2, 9, 1] 
adjacent to 3 at r=0, c=2 = [2, 4, 9, 1, 5] 
adjacent to 4 at r=0, c=3 = [3, 1, 5] 
adjacent to 9 at r=1, c=1 = [-1, 2, 3, -2, 1, -3, 8, 7] 
adjacent to 1 at r=1, c=2 = [2, 3, 4, 9, 5, 8, 7, 6] 
adjacent to 5 at r=1, c=3 = [3, 4, 1, 7, 6] 
adjacent to 8 at r=2, c=1 = [-2, 9, 1, -3, 7, -4, -5, -6] 
adjacent to 7 at r=2, c=2 = [9, 1, 5, 8, 6, -5, -6, -7] 
adjacent to 6 at r=2, c=3 = [1, 5, 7, -6, -7] 
Смежные вопросы