[再帰、検索、バックトラッキングアルゴリズムの演習] 1日目


1. 面接の質問 08.06. ハノイ塔問題

1. トピックの紹介

面接の質問 08.06. ハノイの塔問題
古典的なハノイの塔問題では、3 本の柱と異なるサイズの N 枚の穴あきディスクがあり、プレートはどの柱にも滑り込むことができます。最初に、すべてのプレートが最初の柱に上から下の昇順で配置されます (つまり、各プレートはより大きなプレートにのみ配置できます)。ディスクを移動するときは、次の制限が適用されます:
(1) 一度に移動できるディスクは 1 つだけです;
(2) ディスクは列の上部からのみスライドして次の列に移動できます;
(3) ディスクは次の列に移動できますより大きなディスク上にのみスタックできます。
スタックを使用して最初の柱から最後の柱まですべてのプレートを移動するプログラムを作成してください。
スタックを適切に変更する必要があります。
ここに画像の説明を挿入します

2. 問題解決のアイデア

まず、分析を通じて、この質問には 3 つの状況が考えられることがわかります。

  1. A の要素数 n が 1 の場合、A の要素を直接 C に転送します。
  2. A の要素数 n が 2 の場合、まず A の先頭の要素を B に転送し、次に A の残り 1 要素を C に転送し、最後に B の要素を A に転送します。
  3. A の要素の数 n が 2 より大きい場合、最初に A の上位 n - 1 要素を B に転送し、次に A の残りの 1 要素を C に転送し、最後に B の n - 1 要素を転送します。要素は次のようになります。 Aに転送されました。
    A の残りの 1 要素を C に転送すると、B の要素を A/C に配置できます。これは、n 要素の問題を n-1 要素の問題に単純化するのと同じです (A とこれのみが達成可能)ピラーBの位置を交換することにより)。

したがって、この問題を、最初に A の n-1 個の要素を B に転送し、次に A の残りの 1 個の要素を C に転送し、最後に B の n-1 個の要素を C に転送するというように変換できます。

3.コード

class Solution {
    
    
public:
    void hanota(vector<int>& A, vector<int>& B, vector<int>& C) {
    
    
      dfs(A, B, C, A.size());//这个函数的逻辑就是将A上的元素转移到C上
    }
    void dfs(vector<int>& A, vector<int>& B, vector<int>& C, int n)
    {
    
    
      if(n == 1)//A上面只有一个元素的话,将A的元素直接放到C位置
      {
    
    
        C.push_back(A.back());
        A.pop_back();
        return;
      }
      dfs(A, C, B, n - 1);//将A上的n-1个元素先转移给B
      //将A上最后一个元素直接移到C上
      C.push_back(A.back());
      A.pop_back();
      dfs(B, A, C, n - 1);//将B上的n-1个元素移动到C
    }
};

4. 走行結果

ここに画像の説明を挿入します

2. 21. 2 つの順序付きリンク リストを結合する

1. トピックの紹介

21. 2 つの順序付きリンク リストをマージする
2 つの昇順リンク リストを新しい昇順リンク リストにマージし、戻ります。新しいリンク リストは、指定された 2 つのリンク リストのすべてのノードを連結することによって形成されます。
ここに画像の説明を挿入します

ここに画像の説明を挿入します

2. 問題解決のアイデア

再帰的なアイデア:
2 つのリンク リストのヘッド ノードのうち小さい方を最終ヘッド ノードとして返し、残りのノードを再帰に渡します。(選択したヘッド ノードを除外した後、2 つの新しいリンク リストをマージするのと同じです)。
反復的なアイデア:

  1. まず、新しいリンク リストのヘッド ノード list3 を設定し、l3 を使用して list3 を走査します。
  2. ポインタ l1 と l2 を使用して、それぞれ list1 と list2 をスキャンします。
  3. l1->val < l2->val の場合、l3->next=l1;l1=l1->next;l3=l3->next; そうでない場合は、
    l3->next=l2;l2=l2->next;とします。 l3=l3->次へ;
  4. 2 つのリンク リストの一方が通過するまで、もう一方のリンク リストの残りのノードはすべて l3 の次のノードに接続されます。
  5. 最後に list3->next に戻ります。

3.コード

再帰:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
    
    
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
    
    
        if(list1 == nullptr) return list2;
        if(list2 == nullptr) return list1;
        if(list1 -> val < list2 -> val) //当l1的当前节点小于l2时,我们就返回l1的这个节点并遍历l1(可能会修改它的next节点)
        {
    
    
            list1 -> next = mergeTwoLists(list1 -> next, list2);
            return list1;

        }
        else//同理
        {
    
    
            list2 -> next = mergeTwoLists(list1, list2 -> next);
            return list2;
        }
    }
};

反復:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
    
    
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
    
    
        ListNode* list3 = new ListNode;
        ListNode* l1 = list1, *l2 = list2, *l3 = list3;
        while(l1 && l2)
        {
    
    
            if(l1 -> val < l2 -> val)
            {
    
    
                l3 -> next = l1;
                l1 = l1 -> next;
                l3 = l3 -> next;
            }
            else
            {
    
    
                l3 -> next = l2;
                l2 = l2 -> next;
                l3 = l3 -> next;
            }
        }
        if(l1)
        {
    
    
            l3 -> next = l1;
        }
        else
        {
    
    
            l3 -> next = l2;
        }
        return list3 -> next;
    }
};

4. 走行結果

再帰:
ここに画像の説明を挿入します
反復:
ここに画像の説明を挿入します

3. 206. 逆リンクリスト

1. トピックの紹介

206. 連結リストを反転します。

単連結リストの先頭ノードを渡しますので、連結リストを反転して、反転した連結リストを返してください。
ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します

2. 問題解決のアイデア

再帰的アイデア:
現在のノードの後に​​リンク リスト ノードを反転し、反転したリンク リストの後に現在のノードを配置します (つまり、head->next -> next = head。head->next は反転後の反転リンク リストであるため) ) 最後のノードなので、head -> next -> next の位置に直接 head を配置できます)

反復的なアイデア:

  1. 2 ポインタ方式 (正確には 3 ポインタ方式、2 つのポインタが反転を実行し、1 つのポインタが走査を実行します)
  2. 元のリンク リスト内の cur の前のノードを指すには prev を使用し、元のリンク リスト内の cur の次のノードを指すには next を使用します。
  3. prev と cur を使用してリンク リストを反転し、next を使用してリンク リストを横断して境界を越えないようにします (反転するたびに、prev を cur にポイントし、cur を next にポイントし、next から next -> next をポイントします。 1 つのノードを逆方向にトラバースします)
  4. リンクされたリストの走査が終了するまで、つまり次は nullptr です。

: 各反転はループ本体内で実行されるため、next が空の場合、cur にはまだ完了していない最後の反転が残っているため、これを埋める必要があります。

3.コード

再帰:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
    
    
public:
    ListNode* reverseList(ListNode* head) {
    
    
        if(head == nullptr || head -> next == nullptr)
        {
    
    
            return head;
        }
        ListNode* newhead = reverseList(head -> next);
        head -> next -> next = head;
        head -> next = nullptr;
        return newhead;
    }
};

反復:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
    
    
public:
    ListNode* reverseList(ListNode* head) {
    
    
        if(head == nullptr || head -> next == nullptr) return head;
        ListNode* prev = nullptr;
        ListNode* cur = head;
        ListNode* next = head -> next;
        while(next)
        {
    
    
            cur -> next = prev;
            prev = cur;
            cur = next;
            next = next -> next;
        }
        cur -> next = prev;
        return cur;
    }
};

4. 走行結果

再帰:
ここに画像の説明を挿入します
反復:
ここに画像の説明を挿入します


要約する

今日は、再帰、検索、バックトラッキングのアルゴリズムを練習する初日です。
良いスタートが切れれば戦いは半分です、さあ。
この記事にある問題はすべて Leetcode からのもので、友達は問題の紹介にあるリンクをクリックして練習することができます。
この記事があなたにインスピレーションを与えたなら、著者をもっとサポートしていただければ幸いです。皆さん、ありがとう!

おすすめ

転載: blog.csdn.net/xjjxjy_2021/article/details/132012200