02数字を2つ足す2
質問の説明
2 つの非負の整数を表す 2 つの空でないリンク リストが与えられると、それぞれの
数字は逆の順序で格納され、それらの各ノードは 1 つの数字しか格納できません。
これら 2 つの数字を加算すると、新しいリンク リストは次のようになります。数値 0を除いて
、どちらの数値も 0 で始まることはないと仮定できます。
1. 質問の意味を理解する
質問の意味を理解する
- リンクされたリストは整数を表します
- 各ノードには数値が格納されます
- それぞれの数字は逆順に格納されます。
- 連結リストの加算、つまり連結リストで表される整数の加算
- リンクされたリストで表される 2 つの数値の合計を返します。
詳細:
- 両方のリンク リストが空ではない
- 2 つの非負の整数
- どちらの数字も 0 から始まることはありません
- 数値は逆順に配置され、最初のノードは整数の最下位ビットを表します
- 【ポイント】 整数の範囲を指定していない設問ですが、見落とされやすい点です。
問題解決のアイデア
解決策 -: リンクされたリストをそれぞれ数値に変換して加算する
解決策 2: 対応する位置の数値を直接加算する
2.データ構造とアルゴリズムの考え方の選択肢を選択する
リンクされたリスト
-
物理的に非連続かつ非順次のストレージ構造
-
実行時に動的に生成できる一連のノードで構成されます。
-
各ノードには、データ フィールドとポインタ フィールドの 2 つの部分が含まれます。
リンクリストの特徴
- データストレージには継続的なスペースは必要なく、容量も制限されません。
- データの論理順序は、ポインター リンクの順序によって実装されます。
- リンクリストの先頭から順に後続のノードにアクセスします
- リンクされたリストの先頭にデータを挿入する時間計算量は O(1) です。
class ListNode {
int val;
ListNode next;
ListNode() {
};
ListNode(int x) {
val = x;
}
}
データ構造の選択
リンクされたリスト
アルゴリズム的思考の選択
解決策 1: リンクされたリストをそれぞれ数値に変換し、それらを加算して
トラバースする解決策
2: 対応する位置に数値を直接加算する
3.コーディングの基本的な解決策とコーディングの実装
解決策 1: 暴力的な解決策
- 2 つのリンクされたリストを調べ、数学的思考を使用してそれらを整数に変換します。
- 2 つの整数を合計して合計を取得します
- 数学的思考に従って合計を連結リストに変換する
解決策 1: 境界と詳細の問題に対する強力な解決策
国境問題
- リンクリストを整数に変換する場合、next==null で終わる
- 最上位ビットを処理した後、整数をリンク リストに変換します: value==0
詳細
- 異なるノードは異なるビットを表します
- 質問では整数の範囲が指定されていません。
int がオーバーフローした場合は、long を使用できますが、long オーバーフローの場合はどうすればよいでしょうか?
3. 基本的なソリューションとコードのコーディング実装
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
long number1 = 0;
int digit = 0;
//将链表转成数
while (l1 != null) {
int value = l1.val;
number1 = number1 + (int) (value * Math.pow(10, digit));
l1 = l1.next;
digit++;
}
long number2 = 0;
digit = 0;
//将链表转成数
while (l2 != null) {
int value = l2.val;
number2 = number2 + (int) (value * Math.pow(10, digit));
l2 = l2.next;
digit++;
}
//求和
long sum = number1 + number2;
ListNode resultHead = new ListNode(-1);
ListNode currentNode = resultHead;
//如果等于0,则构造List(0)
if (sum == 0) {
currentNode.next = new ListNode(0);
}
while (sum != 0) {
int val = (int) (sum % 10);
sum = sum / 10;
currentNode.next = new ListNode(val);
currentNode = currentNode.next;
}
return resultHead.next;
}
}
時間の複雑さと空間の複雑さ
時間の複雑さ
-
2 つのリンクされたリストをそれぞれ走査します: O(m + n)
-
2 つの数値の合計をリンク リストに変換し、リンク リストの最後にノードを挿入します。
O(k)=O(max(m, n))
PS: k=max(m, n) または k=max(m, n)+1
-
総複雑さ: O(m+n+max(m,n)) 約 O(m+n)
空間の複雑さ
- 2 つの数値の合計の桁数に応じて長さが異なる新しいリンク リストを作成します: O(max(m, n))
4. より良い解決策を考えることを検討する
- 無効なコードを削除するか、スペース消費を最適化します。
- ループのレベルと時間を削減する
- コードを最適化する
- より優れたアルゴリズム的思考を求めて
- リンクリストを整数に変換するプロセスをスキップできますか?
- リンクされたリストの対応する位置に数値を直接追加することはできますか?
- 他のアルゴリズムから学ぶ
5.コーディングの最適解のアイデアとコーディングの実装
最適解:数学的思考による解決策
- 2 つのリンクされたリストを横断する
- 対応する位置にノード値を追加します
- 末尾が 10 より大きい場合は結果を新しいリンク リストに挿入し、キャリーを実行して、キャリーを次のノードに追加します。
最適な解決策: 境界と詳細の問題
国境問題
- 2 つのリンク リスト境界: next==null
詳細
- 2 つのリンク リストの長さは矛盾しており、短いリンク リストの上位ビットは 0 とみなされます。
- リンクリストの最上位ビットではキャリーが発生するため、キャリー番号を格納するにはリンクリストにノードを追加する必要があります。
コーディングの最適なソリューションのアイデアとコーディングの実装
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode resultHead = new ListNode(-1);
ListNode currentNode = resultHead;
ListNode number01 = l1;
ListNode number02 = l2;
int carry = 0;
while (number01 != null || number02 != null) {
//如果当前为尾节点,则为0
int num1 = number01 != null ? number01.val : 0;
int num2 = number02 != null ? number02.val : 0;
int sum = num1 + num2 + carry;
//得到进位
carry = sum / 10;
//得到当前位
sum = sum % 10;
//创建新节点,
currentNode.next = new ListNode(sum);
currentNode = currentNode.next;
//如果当前指针为null,则不移动,指针不为null,则移动到下一个节点
number01 = number01 != null ? number01.next : number01;
number02 = number02 != null ? number02.next : number02;
}
//如果有进位
if (carry > 0) {
currentNode.next = new ListNode(carry);
}
return resultHead.next;
}
}
時間の複雑さと空間の複雑さ
時間計算量: O(max(m,n))
- 2 つのリンク リストの追加: O(max(m,n))
- 総当たり解の O(m+n) と大きな違いはありませんが、大量の演算とループ数の点で、総当たり解の方が優れています。
空間複雑さ: O(max(m,n))
- 作成するには、新しいリンク リストを作成する必要があります: O(max(m, n))
6.変形延長の変更
- 6Cの問題解決法
- リンクリストの特徴
- データストレージには継続的なスペースは必要なく、容量も制限されません。
- データの論理順序は、ポインター リンクの順序によって実装されます。
- リンクリストの先頭から順に後続のノードにアクセスします
- リンクされたリストの先頭にデータを挿入する時間計算量は O(1) です。
- 数学的思考
- 数学垂直