[バックルブラシの質問を強制する](2)リンクリストのトピック

リンクリスト

1、リンクリスト

リンクリストはノードに接続されたデータ構造であり、一般的なノード構造には、データを格納するデータフィールドと、実行のために次のノードを格納するポインタフィールドが含まれます。二重リンクリストは、ポインタフィールドを先行ノードpreと後続ノードnextに絞り込みます。循環リンクリストは、テールノードをヘッドノードに接続します。

したがって、リンクリストの分類には、実装に応じて、単一リンクリスト、二重リンクリスト、および循環リンクリストが含まれます。

リンクリストの設計原則は、追加と削除が速く、クエリが遅いということです。リンクリストの知識については、以下を参照してください。

Javaのリンクリスト:

Javaでのリンクリストの実装は次のとおりです。

2.ボタンの質問タイプを強制します

2.1、203リンクリスト要素を削除する

リンクリストのヘッドノードheadと整数valを指定して、をNode.val == val満足、新しいヘッドノードを返します。

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

リンク:https ://leetcode-cn.com/problems/remove-linked-list-elements/

  • 再帰的書き込み(ヘッドノードを設定する必要はありません)
class Solution {
    
    
    public ListNode removeElements(ListNode head, int val) {
    
    
        if(head == null) return null;
        //移动到尾部,返回值在压栈的时候表示下一结点,弹栈时表示前一结点
        head.next = removeElements(head.next,val);//传的是啥返回的就是啥
        //弹栈时head作为当前结点,匹配则返回前一节点;跳过该节点就相当于删除了。
        if(head.val == val) return head.next;
        //否则,返回前一节点继续
        else{
    
    return head;}
    }
}

一般的な方法は、削除するノードの前のノードを、削除するノードの後のノードにポイントすることです。

削除するノードがヘッドノードの場合は操作できません。したがって、ソリューションを統合するために仮想ヘッドノードが追加されます。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    
    
    public ListNode removeElements(ListNode head, int val) {
    
    
        ListNode dummy = new ListNode(-1); //新建一个节点做头节点
        dummy.next = head;
        ListNode cur = dummy;
        while(cur.next != null){
    
    
            if(cur.next.val == val){
    
    
                cur.next = cur.next.next;
            }else{
    
    
                cur = cur.next; //否则移动到下一个节点
            }
        }
         //为什么返回的头结点是这个而非cur,因为cur一直在移动
         //为什么是dummy.next而非dummy,因为dummy是个虚拟的头节点,要返回自己的
        return dummy.next;
    }
}

2.2、206逆リンクリスト

単一リンクリストのヘッドノードを指定して、リンクリストheadを逆にして、逆リンクリストを返します。

1-> 2-> 3-> 4-> null

null <-1 <-2 <-3 <-4

リンク:https ://leetcode-cn.com/problems/reverse-linked-list/

反復法:

class Solution {
    
    
    public ListNode reverseList(ListNode head) {
    
    
        ListNode prev = null; //前一个结点
        ListNode next = null; //下一个结点(临时存储)
        ListNode curr = head; //当前结点
        while(curr != null){
    
    
            next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }
        return prev;
    }
}

再帰的方法:再帰的ユニットの実行は、ヘッドノードを除く残りのノードを反転することです。

class Solution{
    
    
	publiC ListNode reverseList(ListNode head){
    
    
        //递归的停止条件
        //我们通常使用head.next判断就可以了,head为null是为了防止链表本身为null的情况
        if(head == null || head.next == null)return head;
        //压栈,直到移动到最后一个结点
        ListNode prev = reverseList(head.next);
        head.next.next = head; //弹栈的过程就是把当前结点指向前一个结点的过程
        head.next = null; //把前一结点指向null
        return prev;
    }
}

ステートメント分析:1-> 2-> 3-> 4-> null

reverseList(3.next)= reverseList(4)、ここで4.nextはnullなので、スタックからポップアウトします。返されるprevは4です。この時点で、headは3です。

スタックをポップした後に実行します:3.next.next = 3; 3.next=null。すなわち4->3->null

次のステップ:3-> 2-null; 2->1->null。スタックを3回押すだけで、スタックを4回プレイしました。

テストデモ:

public class ReverseListTest {
     
     
 static int ya = 0;
 static int tan = 0;
 public static void main(String[] args) {
     
     
     ListNode head = new ListNode(1);
     ListNode node2 = new ListNode(2);
     ListNode node3 = new ListNode(3);
     ListNode node4 = new ListNode(4);
     head.next = node2;
     node2.next = node3;
     node3.next = node4;
     node4.next = null;
     reverseList(head);
 }
 public static ListNode reverseList(ListNode head){
     
     
     ya++;
     System.out.println("压栈第"+ya+"次");

     if(head == null || head.next == null)return head;
     //压栈,直到移动到最后一个结点
     ListNode prev = reverseList(head.next);

     tan++;
     System.out.println("弹栈第"+tan+"次");

     head.next.next = head; //弹栈的过程就是把当前结点指向前一个结点的过程
     head.next = null; //把前一结点指向null
     return prev;
 }
}
class ListNode{
     
     
 int val;
 ListNode next;
 ListNode(int x){
     
     
     val = x;
 }
}
压栈第1次
压栈第2次
压栈第3次
压栈第4次
弹栈第1次
弹栈第2次
弹栈第3次

2.3、24リンクリスト内のノードをペアで交換します

リンクリストを指定して、隣接するノードを2つずつスワップし、スワップされたリンクリストを返します。

ノード内の値を変更するだけでなく、実際にノードスワップを行う必要があります。

输入:head = [1,2,3,4]
输出:[2,1,4,3]

リンク:https ://leetcode-cn.com/problems/swap-nodes-in-pairs/

アイデア:

画像-20210723110511558

反復法:更新はプロセスシミュレーションです。

class Solution {
    
    
    public ListNode swapPairs(ListNode head) {
    
    
        ListNode dummy = new ListNode(0);//虚拟头结点
        dummy.next = head; //加入结点
        ListNode temp = dummy;
        while(temp.next !=null && temp.next.next !=null){
    
    
            ListNode node1 = temp.next;
            ListNode node2 = temp.next.next;
            temp.next = node2; //temp复位node2
            node1.next = node2.next; //node1指向node3
            node2.next = node1; //node2指向node1

            temp = node1; //前进一步
        }
        return dummy.next;
    }
}

再帰的な方法:より簡単で、毎回前の方法を指すだけです

class Solution {
    
    
    public ListNode swapPairs(ListNode head) {
    
    
        // 1,结束条件
       if(head == null || head.next == null) return head;
    //    2,移动
       ListNode next = head.next;
    //    注意要间隔一个
       head.next = swapPairs(next.next);
    //    弹栈执行
       next.next = head;
       return next;
    }
}

2.4、19リンクリストの最後のN番目のノードを削除します

リンクリストを指定したら、リンクリストの最後からn番目のノードを削除し、リンクリストのヘッドノードを返します。

入力:ヘッド= [1,2,3,4,5]、n = 2
出力:[1,2,3,5]

入力:ヘッド= [1]、n = 1
出力:[]

リンク:https ://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/

アイデア:この問題は、ダブルポインターの古典的なアプリケーションです。まず、高速ポインターを最初にn移動させ、次に低速ポインターを同時に移動し始めます。高速ポインターが終了すると、低速ポインターは前のポインターに移動します。指定した位置の位置で、直接削除できます。(ここでの高速ポインターの本質は、リンクリストの長さを見つけることと同じです)

移動停止状態:(双方向)

fIndex !=null:高速ポインタは最後のNULLに移動します。このとき、同期を維持するために、低速ポインタは事前に仮想ノードのソースに移動する必要があります。仮想ノードを使用すると、ヘッドノードでの処理の問題が統合されます。

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        // 双指针
        // 因为无法直接获取链表长度,从头往后遍历
        ListNode dummy = new ListNode(0,head);
        ListNode fIndex = head;
        ListNode sIndex = dummy;
        if(head == null || head.next == null) return null;
        // 先移动n个长度,
        for(int i = 0;i<n;i++){
            fIndex = fIndex.next;
        }
        while(fIndex!=null){
             fIndex = fIndex.next;
             sIndex = sIndex.next;
        }
        sIndex.next = sIndex.next.next;
        return dummy.next;
    }
}

fIndex.next != null、このとき、高速ポインタは1つ少なく移動し、最後の位置に移動します。その後、低速ポインタはヘッドポインタから開始して、同期を維持できます。

class Solution {
    
    
    public ListNode removeNthFromEnd(ListNode head, int n) {
    
    
        // 双指针
        // 因为无法直接获取链表长度,从头往后遍历
        ListNode fIndex = head;
        ListNode sIndex = head;
        // 先做判空处理
        if(head == null || head.next == null) return null;
        // 先移动n个长度,
        for(int i = 0;i<n;i++){
    
    
            fIndex = fIndex.next;
        }
        
        // 注意:这里就需要考虑删除节点为头结点的情况
        // 即删除倒数第n个,移动了n个
        if(fIndex == null) return head.next;
        
        while(fIndex.next!=null){
    
    
             fIndex = fIndex.next;
             sIndex = sIndex.next;
        }
        sIndex.next = sIndex.next.next;
        return head;
    }
}

(実際、公式のソリューションでも最も独創的なアイデアが得られました。つまり、最初にリンクリストの長さを計算してから、最初からトラバースします。ソリューションを参照してください

2.5、インタビューの質問02.07。リンクリストの交差点

2つの単一リンクリストのヘッドノードheadAとheadBを前提として、2つの単一リンクリストが交差する開始ノードを見つけて返します。2つのリンクリストが交差しない場合はnullを返します。

この図は、2つのリンクリストがノードc1で交差していることを示しています。

img
リンク:https://leetcode-cn.com/problems/intersection-of-two-linked-lists-lcci

ここでの共通部分は、ノードではなく、最初のノードの文字列であることに注意してください。したがって、2つのリンクリストの最後のノードは等しくなければなりません。したがって、2つのリンクリストは右揃えである必要があります。右揃えの直後にトラバースできます。

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    
    
        // 此种方法的缺点是要考虑到所有输出为null的情况
        if (headA == null || headB == null) return null;
        int sa=0,sb = 0;
        // 算出各自的长度
        ListNode tailA = headA,tailB = headB;
        while(tailA != null){
    
    
            tailA = tailA.next;
            sa++;
        }
        while(tailB != null){
    
    
            tailB = tailB.next;
            sb++;
        }
    	//最后一个不一样直接退出
        if(tailA!=tailB) return null;
        int diff = Math.abs(sa-sb);
        if(sa>sb){
    
      //A比B长,A先移动diff位
            while(diff-- > 0) headA=headA.next;
        }
        if(sa<sb){
    
    
            while(diff-- > 0) headB=headB.next;
        }
        // 此时就可以直接遍历处理了
        while(headA != headB){
    
    
            headA = headA.next;
            headB = headB.next;
        }
        return headA;
    }

2.6、循環リンクリスト||

リンクリストを指定して、リンクリストがリングに入り始める最初のノードを返します。リンクリストにサイクルがない場合に戻りますnull

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

リンク:https ://leetcode-cn.com/problems/linked-list-cycle-ii/submissions/

方法1、高速および低速ポインター:

なぜ会わなければならないのですか?ヘッドノードから同時に開始し、高速ポインターが毎回2ステップかかる場合、低速ポインターは毎回1ステップかかります。リングがある場合は、高速ポインターと低速ポインターがリング内で出会う必要があります。 。スローに追いついた後、この時点でヘッドノードとスローが同時に歩き始め、それらが出会うとリングポイントの位置になります。高速ポインタによる微分式は、低速ポインタの2倍の速度で移動します。

public class Solution {
    
    
    public ListNode detectCycle(ListNode head) {
    
    
        //同时从头结点出发
        ListNode fast = head;
        ListNode slow = head;
        while(fast!=null){
    
    
            slow = slow.next;
            if(fast.next!=null){
    
    
                fast = fast.next.next;//每次走两步
            }else{
    
    
                return null;
            }
            if(fast == slow){
    
    
                //此时prev指针走slow多走的就可以走到环点
                ListNode prev = head;
                while(prev != slow){
    
    
                    prev = prev.next;
                    slow = slow.next;
                } 
                return prev; 
            }   
        }
        return null;
    }
}

HashSetの非反復機能を使用することもできます。

public class Solution {
    
    
    public ListNode detectCycle(ListNode head) {
    
    
        // 利用hashset的不重复特性
        HashSet<ListNode> hashSet = new HashSet<ListNode>();
        ListNode curr = head;
        while(curr!=null){
    
    
             if(!hashSet.contains(curr)){
    
    
                hashSet.add(curr);
                curr = curr.next;
            }else{
    
    
                return curr;
            }
        } 
        return null;
    }
}

3.まとめ

再帰

繰り返す

ダブルポインタ

おすすめ

転載: blog.csdn.net/qq_40589204/article/details/119859006