質問タイプ|データ構造とアルゴリズムのリンクリストシリーズ

質問タイプ|データ構造とアルゴリズムのリンクリストシリーズ

前に書く

Xiaoluのように、最初はリンクリストの操作コードについて非常に混乱している場合は、1か月後に、Xiaoluに従ってリンクリストの関連操作と質問タイプを整理して要約し、適切な演習を行うことをお勧めします。浅いものから深いものへ。これらの質問を実際に練習した後、リンクリストに関する心理的な混乱を解消するだけでなく、他のデータ構造を学ぶのに大きな自信と助けになると思います。

記事の長さは8000語なので、公式アカウントは縮小版です。フル版については、記事下部のリンクをご確認ください。

研究の提案

Xiaoluは、この質問と演習を一度にすべて読んで、自分で小さな計画を立てることをお勧めしません。質問を整理するときに、毎日1つの質問を整理し、各質問を徹底的に分析することを計画しました。最高の吸収効果を実現します。

学習パス

この記事は、基本練習段階、上級練習段階、強化練習段階の3つの段階に分かれています。

1.基本的な練習段階

最初の段階の前に、リンクリストの基本的な知識をすでに習得していますが、リンクリストのコードを記述していない場合は、最初の段階の最も基本的なものから始めます。この記事は基本的にJavaScriptコードで実装されていますが、アルゴリズムの考え方は静的です。問題が発生した場合は、BaiduまたはGoogleを自分で使用するか、私に教えてください。以下メッセージを残してください。

2.高度な練習段階

上記のリンクリストの基本コードに精通している場合は、次の段階である上級段階に進むことができます。この段階でさらに困難になるのは、リンクリストの操作によって実際の問題が解決されることです。あなたはあなたの権利を行使することができます問題を分析して解決する能力はまたあなたのコードの包括性と頑健性をテストします。この段階は非常に重要です。私は以下の質問のそれぞれについて詳細な分析を行いました。

3.練習段階を強化する

上記の高度な練習段階のすべての質問を知っている場合は、それを見てみましょう。LeetCodeは多くの面接の質問を集めたので、上記のいくつかの古典的な質問をまとめました。私が並べ替えた関連の質問に答えてみてください。問題のコードと問題を解決するためのアイデアを出します。この段階のトピックは、後の段階で継続的に更新されます。これらのトピックを完全に把握することができ、リンクリストはあなたにとって簡単なものになります。


フェーズ1:リンクリストの基本的な演習

私は最初に、以下のリンクリストにある最も基本的な操作を1つずつ説明し、関連するコードを整理しました(最初にそれらを解決してみてください)

1.単一リンクリストの挿入、削除、検索操作(☛タイトル分析)

2.循環リンクリストの挿入、削除、検索操作(☛タイトル分析)

3.二重リンクリストの挿入、削除、検索操作(☛タイトル分析)

ステージ2:リンクリストの高度な演習

1.単一リンクリストを端から端まで印刷します

タイトル:リンクリストのヘッドノードを入力し、各ノードの値を最後から最初まで順番に出力します。

1.1問題の分析と解決策

▉問題分析

1.質問を見て最初に頭に浮かぶのは、リンクリストを反転する方法である反転リンクリストが印刷されることですが、この方法では元のリンクリストの構造が変更されます。

※デメリット:リンクリストの構成が変更になりました。リンクリストの構造を変更しない場合はどうすればよいですか?

2.リンクリストを端から端まで印刷したいという質問から、通常は最初から最後まで印刷します。最後のデータが最初に印刷され、最初のデータが印刷されると考えます。ラスト「ファーストインラストアウト」「の特徴」のようなものがあり、「スタック」の構造を使って、スタックを使って実現することが考えられます。

※デメリット:コードが簡潔ではありません。

※メリット:堅牢性に優れています(不確実な場合でもプログラムを正しく実行できます)。

3.スタックのデータ構造に関して言えば、「再帰的」実装はスタックのデータ構造で実現されていると考えられます。スタックを実現できるので、再帰も実現できます。

※デメリット:リンクリストが非常に長い場合、再帰の深さが非常に深くなり、スタックオーバーフローが発生します。

※メリット:コードが簡潔でわかりやすいです。

▉アルゴリズムのアイデア

上記の問題分析により、以下の解決策が得られます。

●逆リンクリスト方式

●スタックの実装

●再帰的な実装

1.逆リンクリストの実装

リンクリストの内容を最後から最初まで出力する場合は、リンクリストを逆にして、最初から最後までデータを出力するのが一般的です。

2.スタックの実装

単一リンクリストを最初から最後までトラバースし、データを順番にスタックに格納します。次に、スタック全体をトラバースしてデータを出力します。

3.再帰的な実装

単一リンクリストは、最後から最初まで再帰的に出力できます。再帰プロセスには、「受け渡し」と「再帰的」が含まれます。リンクリストの出力データを反転し、正式には「受け渡し」プロセスを使用します。データはヘッドから出力されます。次に、「再帰」プロセスを再帰的に使用してコンテンツを出力します。現在のノードを出力するには、最初に現在のノードの次のノードを出力する必要があります。

▉テストケース

コードを書く前に、健全で堅牢なコードを書くためのテストケースを考える必要があります。また、プログラム全体の中で最も致命的な部分であることが多い境界条件を考慮する必要があります。包括的に考慮しないと、バグが発生し、プログラムをクラッシュさせます。

1.空のリンクリストを入力します。

2.入力リンクリストにはノードが1つだけあります。

3.入力リンクリストには複数のノードがあります。

▉コードの実装

1.コードの実装:逆リンクリスト方式


 1//定义结点
 2class Node{
 3    constructor(data){
 4        this.data = data;
 5        this.next = null;
 6    }
 7}
 8//定义链表
 9class LinkedList{
10    constructor(){
11        this.head = new Node('head');
12    }
13
14    // 功能:单链表反转
15    // 步骤:
16    // 1、定义三个指针(pre=null/next/current)
17    // 2、判断链表是否可反转(头节点是否为空、是否有第二个结点)
18    // 3、尾指针指向第一个结点的 next
19    // 4、尾指针向前移动
20    // 5、当前指针(current)向后移动
21    // 6、将 head 指向单转好的结点
22    reverseList = () =>{
23        //声明三个指针
24        let current = this.head; //当前指针指向头节点
25        let pre = null;//尾指针
26        let next;//指向当前指针的下一个指针
27
28        //判断单链表是否符合反转的条件(一个结点以上)?
29        if(this.head == null || this.head.next == null) return -1;
30
31        //开始反转
32        while(current !== null){
33            next = current.next;
34            current.next = pre;
35            pre = current;
36            current = next;
37        }
38        this.head = pre;
39    }
40
41    //输出结点
42    print = () =>{
43        let currentNode = this.head
44        //如果结点不为空
45        while(currentNode !== null){
46            console.log(currentNode.data)
47            currentNode = currentNode.next;
48        }
49    }
50}

2.コードの実装:ループスタック


 1//方法三:栈实现
 2const tailToHeadOutput = (currentNode)=>{
 3    let stack = [];
 4    //遍历链表,将数据入栈
 5    while(currentNode !== null){
 6        stack.push(currentNode.data);
 7        currentNode = currentNode.next;
 8    }
 9    //遍历栈,数据出栈
10    while(stack.length !== 0){
11        console.log(stack.pop());
12    }
13}

3.コードの実装:再帰


 1// 步骤:
 2// 1、判断是否为空链表
 3// 2、终止条件(下一结点为空)
 4// 3、递归打印下一结点信息
 5const tailToHeadOutput = (head)=>{
 6    // 判断是否空链表
 7    if(head !== null){
 8        // 判断下一结点是否为空
 9        if(head.next !== null){
10            // 下一结点不为空,先输出下一结点
11            tailToHeadOutput(head.next)
12        }
13        console.log(head.data);
14    }else{
15        console.log("空链表");
16    }
17}

▉パフォーマンス分析

1.逆リンクリストの実装

●時間計算量:O(n)。リンクリスト全体をトラバースする必要があります。時間計算量はO(n)です。

●スペースの複雑さ:O(1)。追加のスタックストレージスペースは必要なく、スペースの複雑さはO(1)です。

2.ループスタックの実装

●時間計算量:O(n)。リンクリスト全体をトラバースする必要があります。時間計算量はO(n)です。

●スペースの複雑さ:O(n)。追加のスタックストレージスペースが必要であり、スペースの複雑さはO(n)です。

3.再帰的な実装

●時間計算量:O(n)。リンクリスト全体をトラバースする必要があります。時間計算量はO(n)です。

●スペースの複雑さ:O(n)。追加のスタックストレージスペースが必要であり、スペースの複雑さはO(n)です。

2.2まとめ

▉調査内容

1.単一リンクリストの基本操作。

2.コードの堅牢性。

3.ループ、再帰、およびスタックの柔軟な使用。

▉拡張思考:ループと再帰

※適用条件:同じ問題を複数回計算する必要がある場合は、ループ法または再帰法を使用します。

※再帰のメリット:コードが簡潔です。

※再帰のデメリット:

1.スタックオーバーフロー:関​​数はそれ自体を呼び出し、関数の一時変数はスタックにプッシュされます。関数が実行されると、スタックはクリアされます。再帰スケールが大きすぎる場合、関数自体の呼び出しは常に内部で実行されます。関数、および一時変数は常にスタックにプッシュされます。、システムスタックまたは仮想マシンスタックメモリが小さいため、スタックオーバーフローが発生します。

2.繰り返し計算:再帰には多くの繰り返し計算があります。繰り返し計算はプログラムのパフォーマンスに大きな影響を与え、時間の消費を指数関数的に増加させますが、ハッシュテーブルで解決できます。

3.高いスペースの複雑さ:再帰の各関数呼び出しには、メモリ内のスペースのオープン、スタックのプッシュ、スタックのポップなどの操作が含まれますが、これらは時間とスペースを消費するため、再帰の効率はそれほど効率的ではありません。ループとして。

拡張:

1.再帰スタック:再帰の本質はスタックであり、スタックループによって解決される問題は通常再帰に適しています。

2.再帰動的計画法:問題を解決するための動的計画法では、問題を分析するために再帰的思考を使用することがよくあります。再帰的反復計算の問題に関しては、通常、ボトムアップソリューション(動的計画法)を使用して、再帰的反復計算の問題を解決します。

▉注意が必要な事項

1.ループ解決の問題に関しては、再帰を使用して解決できるかどうかを考えることができます。

2.再帰を使用して、再帰の欠点によって引き起こされるパフォーマンスの問題を解決します。

3.動的計画法を使用して、再帰的ソリューションの問題を解決し、パフォーマンスを向上させることができますか。

4.スタックデータ構造を使用するときは、再帰を実現できるかどうかを検討してください。

この質問に加えて、元のテキストで表示できるリンクリストのいくつかの古典的な例があり、各質問は詳細に分析されています。

注:記事の長さのため、残りの主要トピックの分析はGithubにあります。残りのトピックの詳細な分析を表示するには、[元のテキストを読む]をクリックしてください。

ステージ3:LeetCodeは練習ステージを強化します

基本的なリンクリストの操作をマスターしていて、リンクリストの習熟度をさらに向上させたい場合は、LeetCodeを練習できます。問題分析、アルゴリズムのアイデア、コードの実装、テストの内容など、各質問の詳細な分析を行いました。リンクリストに関連する関連トピックは常に更新されます...

1.循環リンクリストI(☛質問分析)

2.循環リンクリストII(☛質問分析)

3. K個のソートされたリンクリストをマージします(☛タイトル分析)

リンクリストの概要

リンクリストについて多くの質問タイプを行った後、リンクリストの操作を要約して確認し、リンクリストの全体的な把握と新しい理解を得ます。

1.構造的に

リンクリストを格納するためのメモリ空間は連続的ではなく、断片化されたすべてのメモリ空間はポインタを使用して接続する必要があるため、ポインタを介して操作する必要があります。これが、リンクリストのほとんどがポインタ操作である理由です。

リンクリストの構造には、リンクリストヘッドとリンクリストテールの2つの特別な場所があります。多くの操作では、リンクリストヘッドとリンクリストテールに特別な処理が必要なため、センチネルのアイデアを使用できます(リンクリストの先頭にある歩哨)をリンクリストの先頭に立たせます。問題解決を簡素化できます。

2.操作

再帰:リンクリストの各ノードは同じ構造を持っているため、リンクリスト内の多くの操作は再帰によって解決でき、解決された問題は解決のためにサブ問題に分解できます。したがって、リンクリストでの再帰的なプログラミングスキルは依然として非常に一般的です。例:リンクリストを最後から最初まで印刷する、2つの順序付けられたリンクリストをマージする、逆リンクリストなど。

ダブルポインター:リンクリストのほとんどはポインター操作です。リンクリストは線形テーブル構造に属します(構造は線のようなものです)。多くの問題は、非常に一般的に使用されるダブルポインターを使用して解決できます。例:下からK番目のノードを見つける、リンクリストの中央のノードを見つけるなど。

3.パフォーマンス

リンクリストのストレージスペースは連続的ではなく、CPUキャッシュに対応していないため、いつでも最初からしかアクセスできません。時間の複雑さはO(n)ですが、リンクリストのこの構造も利点があります。事前に申請する必要はなく、動的にメモリスペースを申請することができます。

ポインタの格納には追加のメモリスペースが必要です。格納されたデータがポインタを格納するためのメモリスペースよりもはるかに大きい場合は、無視できます。

質問タイプ|データ構造とアルゴリズムのリンクリストシリーズ

この記事には8,000語以上が含まれています。公開アカウントは短縮版にすぎません。完全版を表示するには、[元のテキストを読む]をクリックしてください。

おすすめ

転載: blog.51cto.com/15064450/2602399