データ構造_5:リンクリストと再帰

リンクリストと再帰

リンクリスト要素の削除の問題への答え

問題の説明:リンクリスト[1、2、6、3、4、5、6]の値6の要素を削除します

  • ListNode.java 構造の説明

    public class ListNode {
          
          
        int val;
        ListNode next;
    
        ListNode(int x) {
          
          val = x;}
    
        /**
        * 链表节点构造函数,自定义
        * @param arr
        */
        ListNode(int[] arr) {
          
          
            if (arr == null || arr.length == 0) {
          
          
                throw new IllegalArgumentException("arr can not be empty");
            }
            this.val = arr[0];
            ListNode cur = this;
            for (int i = 1; i < arr.length; i++) {
          
          
                cur.next = new ListNode(arr[i]);
                cur = cur.next;
            }
        }
    
        @Override
        public String toString() {
          
          
            StringBuilder sb = new StringBuilder();
            ListNode cur = this;
            while (cur != null) {
          
          
                sb.append(cur.val).append(" -> ");
                cur = cur.next;
            }
            sb.append("NULL");
            return sb.toString();
        }
    }
    
  • 従来の方法では、リンクリストの3つの部分[ヘッド、ミドル、テール]は別々に処理されます

    public ListNode removeElement(ListNode head, int val) {
          
          
            // 链表头部节点删除
            while (head != null && head.val == val)
                head = head.next;
    
            // 链表尾部节点删除
            if (head == null) {
          
          
                return null;
            }
    
            // 链表中间部分节点删除
            ListNode prev = head;
            while (prev.next != null) {
          
          
                if (prev.next.val == val)
                    prev.next = prev.next.next;
                else
                    prev = prev.next;
            }
    
            return head;
        }
    }
    
  • 仮想ヘッドノード方式では、各リンクリストノードにプレノードが含まれるようになり、コードが改善されます。

     public ListNode removeElement(ListNode head, int val) {
          
          
        // 建立虚拟头结点,保证链表中每一个节点前面均有节点
        ListNode dummyHead = new ListNode(-1);
        dummyHead.next = head;
    
        // 链表节点删除
        ListNode prev = dummyHead;
        while (prev.next != null) {
          
          
            if (prev.next.val == val)
                prev.next = prev.next.next;
            else
                prev = prev.next;
        }
    
        return dummyHead.next;
    }
    
  • テストしてください!

    public static void main(String[] args) {
          
          
        int[] arr = {
          
          1, 2, 6, 3, 4, 5, 6};
        ListNode res = new ListNode(arr);
        System.out.println(res);
        new Solution2().removeElement(res, 6);
        System.out.println(res);
    }
    ------------------------------------------
    1 -> 2 -> 6 -> 3 -> 4 -> 5 -> 6 -> NULL
    1 -> 2 -> 3 -> 4 -> 5 -> NULL
    

再帰:コンピューターの非常に重要なコンポーネントロジックメカニズム

  • 基本的に、元の問題は、配列の合計など、より小さな同じ問題に変換されます。

    SUM(arr[0...n-1]) = arr[0] + SUM(arr[1...n-1])

    SUM(arr[1...n-1]) = arr[1] + SUM(arr[2...n-1])

    SUM(arr[n-1...n-1]) = arr[n-1] + SUM(arr[]) = arr[n-1] + 0

  • 再帰的アルゴリズムの構成

    • 求解最基本的问题
    • 把原问题转化成更小的问题
  • リンクリストは再帰的です

    • リンクリストは、複数のノードの接続本体として理解でき、接続本体と見なすこともでき一个节点和一个链表ます。
    • これはNULL、最も基本的なリンクリストでもあります。
    • わかりやすくするために絵を描きました!
      ここに画像の説明を挿入
    • 写真によると、コードを書き直すことができます!
    public ListNode removeElementNew(ListNode head, int val) {
          
          
        // 基础问题 one
        if (head == null) {
          
          
            return null;
        }
        // 处理子链表,分解问题 two
        head.next = removeElementNew(head.next, val);
        // 处理结果,若当前返回子链表满足条件,便跳过节点 three
        return head.val == val ? head.next : head;
    }
    
    • たとえば、リンクリスト1, 2, 3があり、要素を削除したいのです2が、上記の方法はどのように実行されますか?
      • step1ヘッドノードとして1を使用して[1、2、3]リンクリストを入力します
        • one ヘッド!= null
        • two head.next =?、最初の再帰を入力し、step2
      • step2ヘッドノードとして2を使用して[2、3]リンクリストを入力します
        • one ヘッド!= null
        • two head.next =?、 2番目の再帰を入力します。step3
      • step3エントリ[3]ヘッドノードとして3を使用したリンクリスト
        • one ヘッド!= null
        • two head.next =?、 3番目の再帰を入力します。step4
      • step4入力パラメータ[NULL]、NULLリンクリスト
        • one head == null、return null基本的な問題が発生しました!
      • step5 戻るstep3 two
        • two head.next =【null】
        • three head.val == 2?head.next:頭
        • ヘッドを返す、リンクリストは[3]になり、リターンstep2 two
      • step6 戻るstep2 two
        • two head.next =【3】
        • three head.val == 2?head.next:head此时条件满足,为true
        • head.nextを返します。リンクリストは[3]になり、returnstep1 two
      • step7 戻るstep1 two
        • two head.next =【3】
        • three head.val == 2?head.next:頭
        • ヘッド戻り、リスト[1、3]であり、この時間は、バックstep1、引き継がれたone、、 twothreeメソッド戻り、終了します。
  • 再帰呼び出しにはコストがかかります:関数呼び出し+システムスタックスペース(現在の実行位置、変数ステータス、および時間消費を記録します)。基本的な問題が処理されない場合、つまり再帰出口がない場合、メソッドの実行は次のようになります。メモリがいっぱいになるかオーバーフローするまでメモリを占有し、システムをオーバーさせます。アルゴリズムは常に限られた回数の実行後に終了する必要があり、各ステップは限られた時間で完了することができます。

おすすめ

転載: blog.csdn.net/Nerver_77/article/details/102997165