LeetCodeの Mendix: 二つの塔 | Mendix

メインコンテンツへスキップ

LeetCodeの Mendix: 二つの塔

中程度の難易度のチャレンジに挑戦

前回の投稿では、leetcode.com の問題を解くというチャレンジに挑戦し、ローコードが単なる 1 つの形式のソリューションではなく、より難しい技術的問題にも取り組むために必要な柔軟性を備えていることを証明しました。私が選んだのは、このサイトの最初のチャレンジであり、「簡単な」チャレンジの 1 つである Two Sum です (詳細はこちらでご覧いただけます).

今回は少し難しいことに取り組みます。チャレンジ11 – 「最も水が入った容器” – 中程度の難易度のチャレンジです。この問題では、列の高さの配列を取り、どの列のペアが最も多くの水を保持できる容器を形成するか、または列間の長方形の面積が最も大きいかを計算します。

「最も水が入った容器」 – どこから始めるか

前回のチャレンジでは、チャレンジを額面通りに受け止め、できるだけ基本的なアプローチを使用して公平な立場でアプローチし、その後、ローコードでどのように実行すべきかを検討するところから始めました。今回はそれをスキップして、ローコードでこの問題にすぐに取り組みます。

まず、データが必要です。結果を保存するエンティティと、「タワー」データを作成する子エンティティを作成します。

結果を格納するエンティティと、「タワー」データを作成する子エンティティを作成する方法を示す画像

これにより、一連の値をランダムに生成して一定の順序で配置するスペースと、試行の結果を保存するためにグループ化する場所が確保されます。ただし、データセットをランダムに生成する前に、例に基づいて値のリストを作成することをお勧めします。これをローコードで行うのは特にエレガントではありませんが、簡単です。各項目を 1 つずつ作成し、試行にリンクして、「試行」エンティティを返すだけです。

各項目を 1 つずつ作成し、それを試行にリンクして、「試行」エンティティを返す方法を示す画像。

見た目は良くありませんが、これで目的の場所にたどり着くことができます。右側のグラフは、先ほど作成したデータからレンダリングされたものです。

先ほど作成したデータからレンダリングされたグラフを示す画像。

最初の試み – 力ずく

まずは、総当たり方式で基本的なことを試してみましょう。これが最善の解決策ではないことは確かですが、より効率的なバージョンに進むときに答えを確認できるように、特定の配列の正しい答えを見つけるためには常に実行しておく価値があります。これを行うには、ネストされたループを使用します。最初のループはすべてのタワーを通過し、2 番目のループは外側のループで現在のイテレータに続くすべてのタワーを通過します。

ネストされたループを示す画像。

Microflow からわかるように、効率は O(n2) で、それほど高くありませんが、正しい答えが得られるか確認してみましょう。

効率テストの結果を示す画像。

結果表からわかるように、正しい結論に到達することができました。素晴らしいですね。次のステップでは、より効率的な方法で実行できるかどうかを確認しましょう。

次の試み – 効率性を追求しよう

そのためには、そのコンテナを見つけるための最善かつ最も速い方法を見つける必要がありますが、それは前回の例と同様に、ポインタを使用することです。
両端から始めて、ペアのうち最も高いタワーを維持しながら、もう一方のポインターを移動しながら内側に移動します。各ボリュームをチェックしてログを保持し、それを並べ替えて最大のボリュームを見つける必要があります。この方法で反復することで、最大幅のチェックから最大高さのチェックに移行し、ボリュームを計算するためのすべての最適な組み合わせをチェックします。

これを実行するには、前回の演習での欠点に対処する必要があります。 Mendixデフォルトでは、メモリ内リストの要素をインデックスで取得することはできません。データベースから取得するときには簡単に実行できますが、あまり効率的ではありません。これを修正するには、簡単な Java アクションを作成します。

単純な Java アクションの作成を示す画像。

機能拡張 Mendix Javaアクションで

本当のプラスポイントの一つは Mendix プラットフォームによってツールボックス内のツールだけに制限されないことです。マーケットプレイスから何かを取得するか、独自のカスタム Java アクションを作成することで、機能を迅速かつ簡単に拡張できます。私たちの場合、この関数を作成し、リストを渡し、次にインデックスを渡して、適切なオブジェクトを返すという非常に単純な要求です。

この関数を作成し、リストを渡し、次にインデックスを渡し、適切なオブジェクトを返す方法を示す画像。

これで、リストにアクセスするときに、ほとんどのプログラミング言語と同じようにポインターを使用できるようになりました。効率的なパスのために、両端からのチェックを追跡するための 2 つの整数ポインターと、結果を格納するリストが必要になります。

両端からのチェックを追跡するための 2 つの整数ポインターと、結果を入れるリストの作成を示す画像。

次に、While ループを開始し、両方のポインターが 1 つのタワーで出会うまで繰り返し処理を続けます。その時点で停止することが分かります。このループ内では、各ポインターのタワーを取得し、タワー間に含まれる可能性のあるボリュームを計算します。ループ処理中は、Attempt エンティティで以前に追加した属性で見つかった最大ボリュームも追跡します。こうすることで、最大ボリュームが何であるか、どの 2 つのタワーがそのボリュームを作成するかが分かります。

ループの繰り返しを終了して次の繰り返しに移る前に、比較した 2 つのタワーのどちらが高いかを確認します。タワー 1 の方が高い場合は、ポインター 2 を減らし、タワー 2 の方が高い場合は、ポインター 1 を増やします。これにより、ポインターが同じタワーで出会うまで、ポインターが取り込まれ、While ループのエスケープ句が満たされます。すべて次のようになります...

先ほど比較した 2 つのタワーのうち、どちらが高いかを確認している画像。

効率化の試みの結果

最後に、試行を追跡するためのコミットをいくつか実行すれば完了です。その後、新しいプロセスを実行して結果を確認できます。

総当たり方式では 36 回試行する必要がありましたが、わずか 6 回で正しい結論に到達したことを示す画像。
ここでは、総当たり方式では 36 回必要だった試行回数ではなく、たった 6 回で正しい結論に到達したことがわかります。これは、作業を少し楽にするために少量のコードを追加しただけで、ローコードでもう 1 つの課題が成功したことを意味します。

言語を選択してください