مراقبه های LeetCode: جریان آب اقیانوس اطلس آرام

در توضیح این مشکل آمده است:
یک … وجود دارد
m x n
جزیره مستطیلی که هم مرز است اقیانوس آرام و اقیانوس اطلس. را اقیانوس آرام لبه های سمت چپ و بالای جزیره را لمس می کند و اقیانوس اطلس لبه های راست و پایین جزیره را لمس می کند.این جزیره به شبکه ای از سلول های مربع تقسیم شده است. به شما یک
m x n
ماتریس عدد صحیحheights
جایی کهheights[r][c]
نشان دهنده ارتفاع از سطح دریا سلول در مختصات(r, c)
.این جزیره باران زیادی دریافت می کند و اگر ارتفاع سلول همسایه بالا باشد، آب باران می تواند مستقیماً به سمت سلول های همسایه به طور مستقیم در شمال، جنوب، شرق و غرب جریان یابد. کمتر یا مساوی ارتفاع سلول فعلی آب می تواند از هر سلول مجاور اقیانوس به اقیانوس جریان یابد.
برگشت آ لیست دو بعدی مختصات شبکه
result
جایی کهresult[i] = [ri, ci]
نشان می دهد که آب باران می تواند از سلول جاری شود(ri, ci)
به هر دو اقیانوس آرام و اطلس.
مثلا:
Input: heights = [
[1, 2, 2, 3, 5],
[3, 2, 3, 4, 4],
[2, 4, 5, 3, 1],
[6, 7, 1, 4, 5],
[5, 1, 1, 2, 4]
]
Output: [[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]]
Explanation: The following cells can flow to the Pacific and Atlantic oceans, as shown below:
[0, 4]: [0, 4] -> Pacific Ocean
[0, 4] -> Atlantic Ocean
[1, 3]: [1, 3] -> [0, 3] -> Pacific Ocean
[1, 3] -> [1, 4] -> Atlantic Ocean
[1, 4]: [1, 4] -> [1, 3] -> [0, 3] -> Pacific Ocean
[1, 4] -> Atlantic Ocean
[2, 2]: [2, 2] -> [1, 2] -> [0, 2] -> Pacific Ocean
[2, 2] -> [2, 3] -> [2, 4] -> Atlantic Ocean
[3, 0]: [3, 0] -> Pacific Ocean
[3, 0] -> [4, 0] -> Atlantic Ocean
[3, 1]: [3, 1] -> [3, 0] -> Pacific Ocean
[3, 1] -> [4, 1] -> Atlantic Ocean
[4, 0]: [4 ,0] -> Pacific Ocean
[4, 0] -> Atlantic Ocean
Note that there are other possible paths for these cells to flow to the Pacific and Atlantic oceans.
اگرچه توضیح در نگاه اول می تواند به خودی خود چالشی برای درک باشد، کاری که ما باید انجام دهیم اساساً ساده است (حداقل در تئوری). ما سلولی میخواهیم که همسایگانش کمتر یا مساوی با آن باشند، در تمام مسیر شمال، جنوب، شرق و غرب تا زمانی که به هر دو «اقیانوس» برسیم.
ابتدا، میتوانیم دو مجموعه را برای ذخیره سلولهایی که میتوانند به «اقیانوس آرام» و «آتلانتیک» برسند، مقداردهی اولیه کنیم:
const reachableToPacific: Set<string> = new Set();
const reachableToAtlantic: Set<string> = new Set();
توجه داشته باشید |
---|
ما آنها را به صورت مجموعهای از رشتهها مقداردهی اولیه میکنیم، مشابه همان کاری که در راهحل Number of Islands انجام دادیم. ما می خواهیم جفت سطر و ستون را مانند نمایش دهیم `${row},${col}` . |
به جای اینکه سلول به سلول برویم و بررسی کنیم که آیا می تواند به اقیانوس ها برسد یا خیر، ابتدا می توانیم با سلول هایی که در مجاورت اقیانوس ها هستند شروع کنیم و ببینیم به کدام سلول ها می رسند. ما.
از آنجایی که ما سلولهایی را دریافت میکنیم که در مجموعههای مختلف به اقیانوسها دسترسی دارند، میتوانیم آنهایی را که در هر دو مجموعه هستند برگردانیم (زیرا باید آنهایی را که برای هر دو اقیانوس قابل دسترسی هستند، دریافت کنیم).
بنابراین، در پایان، کاری که ما انجام خواهیم داد این است:
for (const cell of reachableToPacific.values()) {
if (reachableToAtlantic.has(cell)) {
const [r, c] = cell.split(',');
result.push([+r, +c]);
}
}
برای بازدید و علامت گذاری سلول ها می توانیم از یک جستجوی عرضی استفاده کنیم.
برای لبههای بالا و پایین شبکه، سلولهایی را که میتوانند به اقیانوس آرام و اقیانوس اطلس برسند، علامتگذاری میکنیم:
for (let col = 0; col < colsLength; col++) {
bfs(0, col, reachableToPacific);
bfs(rowsLength - 1, col, reachableToAtlantic);
}
میتوانیم همین کار را برای لبههای چپ و راست شبکه نیز انجام دهیم:
for (let row = 0; row < rowsLength; row++) {
bfs(row, 0, reachableToPacific);
bfs(row, colsLength - 1, reachableToAtlantic);
}
در حال حاضر، به اجرای bfs
.
هدف ما bfs
عملکرد علامت گذاری سلول هایی است که می توانند به یک “اقیانوس” برسند. بنابراین، سه پارامتر می گیرد: r
برای ردیف، c
برای ستون، و reachableToOcean
برای مجموعه ای که سلول های قابل دسترسی را ذخیره می کند.
طبق معمول BFS، صفی را نگه می داریم که دارای آرایه هایی متشکل از یک ردیف، یک ستون و مقدار مربوطه در شبکه است:
let queue = [[r, c, heights[r][c]]];
همانطور که از عناصر queue
، یک جفت سطر-ستون را به عنوان قابل دسترسی علامت گذاری می کنیم تا زمانی که آن جفت خارج از محدوده نباشد، یا ما قبلاً آن را به عنوان قابل دسترس اضافه نکرده ایم، یا ارزشی که دارد بزرگتر یا مساوی به “ارتفاع” قبلی که به آن نگاه کردیم.
توجه داشته باشید |
---|
از آنجایی که ما از سلول های لبه شروع می کنیم، به مقادیر علاقه مندیم بزرگتر یا مساوی. به عبارت دیگر، ما به «ارتفاعات» کوتاهتر علاقهای نداریم. |
در حالی که queue
خالی نیست، ابتدا سطر فعلی، ستون فعلی و ارتفاع قبلی را از آن باز می کنیم queue
:
const [currentRow, currentCol, prevHeight] = queue.pop() as number[];
توجه داشته باشید |
---|
این “ارتفاع قبلی” است، زیرا همانطور که در زیر خواهیم دید، مقادیر جدیدی که به آنها فشار می دهیم queue مقادیر سطر و ستون به روز می شود (مانند currentRow + rowToGo و currentCol + colToGo ) در حالی که سلول “قبلی” خواهد بود (heights[currentRow][currentCol] ). |
اگر یکی از شرایطی که در بالا ذکر کردیم درست باشد، می خواهیم با عنصر بعدی در صف ادامه دهیم. در غیر این صورت، آن را به مجموعه خود اضافه می کنیم:
if (
isOutOfBounds(currentRow, currentCol) ||
reachableToOcean.has(`${currentRow},${currentCol}`) ||
heights[currentRow][currentCol] < prevHeight
) {
continue;
}
reachableToOcean.add(`${currentRow},${currentCol}`);
سپس، همسایه ها را به صف اضافه می کنیم heights[currentRow][currentCol]
، که قرار است “ارتفاع قبلی” برای عنصر بعدی باشد:
// up, down, left, right
const coords = [[-1, 0], [1, 0], [0, -1], [0, 1]];
for (const [rowToGo, colToGo] of coords) {
queue.push([
currentRow + rowToGo,
currentCol + colToGo,
heights[currentRow][currentCol]
]);
}
و این برای bfs
تابع:
function bfs(r: number, c: number, reachableToOcean: Set<string>) {
let queue = [[r, c, heights[r][c]]];
while (queue.length > 0) {
const [currentRow, currentCol, prevHeight] = queue.pop() as number[];
if (
isOutOfBounds(currentRow, currentCol) ||
reachableToOcean.has(`${currentRow},${currentCol}`) ||
heights[currentRow][currentCol] < prevHeight
) {
continue;
}
reachableToOcean.add(`${currentRow},${currentCol}`);
// up, down, left, right
const coords = [[-1, 0], [1, 0], [0, -1], [0, 1]];
for (const [rowToGo, colToGo] of coords) {
queue.push([
currentRow + rowToGo,
currentCol + colToGo,
heights[currentRow][currentCol]
]);
}
}
}
با کنار هم قرار دادن همه چیز، راه حل نهایی ما در TypeScript چگونه به نظر می رسد:
function pacificAtlantic(heights: number[][]): number[][] {
let result = [];
const rowsLength = heights.length;
const colsLength = heights[0].length;
const reachableToPacific: Set<string> = new Set();
const reachableToAtlantic: Set<string> = new Set();
function isOutOfBounds(r: number, c: number) {
return r < 0 || c < 0 || r >= rowsLength || c >= colsLength;
}
function bfs(r: number, c: number, reachableToOcean: Set<string>) {
let queue = [[r, c, heights[r][c]]];
while (queue.length > 0) {
const [currentRow, currentCol, prevHeight] = queue.pop() as number[];
if (
isOutOfBounds(currentRow, currentCol) ||
reachableToOcean.has(`${currentRow},${currentCol}`) ||
heights[currentRow][currentCol] < prevHeight
) {
continue;
}
reachableToOcean.add(`${currentRow},${currentCol}`);
// up, down, left, right
const coords = [[-1, 0], [1, 0], [0, -1], [0, 1]];
for (const [rowToGo, colToGo] of coords) {
queue.push([
currentRow + rowToGo,
currentCol + colToGo,
heights[currentRow][currentCol]
]);
}
}
}
for (let col = 0; col < colsLength; col++) {
bfs(0, col, reachableToPacific);
bfs(rowsLength - 1, col, reachableToAtlantic);
}
for (let row = 0; row < rowsLength; row++) {
bfs(row, 0, reachableToPacific);
bfs(row, colsLength - 1, reachableToAtlantic);
}
for (const cell of reachableToPacific.values()) {
if (reachableToAtlantic.has(cell)) {
const [r, c] = cell.split(',');
result.push([+r, +c]);
}
}
return result;
}
پیچیدگی زمان و مکان
پیچیدگی زمانی است
– جایی که
تعداد ردیف ها و
تعداد ستونها است، زیرا ما کل شبکه را طی میکنیم، اما از ساختار داده Set برای جلوگیری از بازدید از همان سلول استفاده میکنیم.
پیچیدگی فضا – من فکر می کنم – نیز هست
، دوباره، کجا
تعداد ردیف ها و
تعداد ستون ها است.
اندازه صف ما متناسب با اندازه شبکه رشد می کند، و همچنین دو مجموعه را نگه می داریم، اندازه آنها نیز می تواند متناسب با شبکه ای که به ما داده شده است، رشد کند.
مشکل بعدی و نهایی که در این فصل به بررسی آن خواهیم پرداخت، برنامه دوره است. تا آن زمان، کد نویسی مبارک.