برنامه نویسی

مراقبه های 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;
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پیچیدگی زمان و مکان

پیچیدگی زمانی است


O(nمتر)O(n * m)

– جایی که

nn

تعداد ردیف ها و

مترمتر

تعداد ستون‌ها است، زیرا ما کل شبکه را طی می‌کنیم، اما از ساختار داده Set برای جلوگیری از بازدید از همان سلول استفاده می‌کنیم.

پیچیدگی فضا – من فکر می کنم – نیز هست

O(nمتر)O(n * m)

، دوباره، کجا

nn

تعداد ردیف ها و

مترمتر

تعداد ستون ها است.
اندازه صف ما متناسب با اندازه شبکه رشد می کند، و همچنین دو مجموعه را نگه می داریم، اندازه آنها نیز می تواند متناسب با شبکه ای که به ما داده شده است، رشد کند.


مشکل بعدی و نهایی که در این فصل به بررسی آن خواهیم پرداخت، برنامه دوره است. تا آن زمان، کد نویسی مبارک.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا