コンテンツ
古代には誰もいません。孤独な印のリン・ユンダオは友人であり、剣は私の人生であり、それは狂気で騎士道的で並外れたものです。
1.この章の焦点
問題1:それ自体以外の配列の製品
質問2:ランダムなポインタを使用してリンクリストをコピーする
2.それ自体以外のアレイの製品
難易度:中程度
整数numsの配列が与えられた場合、配列answerを返します。ここで、answer[i]はnums[i]を除くnumsの要素の積に等しくなります。
タイトルデータは、配列nums内のすべてのプレフィックス要素と任意の要素のサフィックスの積が32ビット整数の範囲内にあることを保証します。
除算は使用せず、O(n)時間計算量で実行してください。
ソース:LeetCode
例1:
入力:nums =[1,2,3,4]
出力:[24,12,8,6]
例2:
入力:nums = [-1,1,0、-3,3]
出力:[0,0,9,0,0]
アイデア:
これは除算、つまりすべての数値を乗算して合計の積を求めることで解決できます。最初の新しい配列の値は、合計の積を元の配列の値で割ったものです。ただし、これは元の配列に0が存在することを考慮に入れています。
タイトルは除算を使わなくてもいいので、考え方を変えるしかありません。
左の積に右の積を掛けます。
それ自体以外の配列の積、つまり、要素の左側の積に右側の積を掛けたもの。各要素の左側の積を最初に作成された新しい配列に入れ、次に各要素の右側の積に新しい配列の値を掛けて、結果を新しい配列に入れます。新しい配列の要素は、元の配列左の積と右の積の積、つまり、それ自体以外の配列の積。
最初の要素の左の積は1に等しい
2番目の要素の左の積は、最初の要素の左の積に最初の要素を掛けたものに等しくなります。
3番目の要素の左の積は、2番目の要素の左の積に2番目の要素を掛けたものに等しくなります。
等々。。。。。。
したがって、各要素の左側の製品メソッドを見つけることができます。
int left=1; for(i=0;i<numsSize;i++) { if(i>0) left=left*nums[i-1]; answer[i]=left; }
最後の要素の正しい積は1に等しい
最後から2番目の要素の右積は、最初から最後の要素の右積に最初から最後の要素を掛けたものに等しくなります。
最後から3番目の要素の右積は、最後から2番目の要素の右積に最後から2番目の要素を掛けたものに等しくなります。
等々。。。。。。
したがって、各要素に適切な製品メソッドを取得できます。
int right=1; for(i=numsSize-1;i>=0;i--) { if(i<numsSize-1) right=right*nums[i+1]; answer[i]*=right; }
参照コード:
int* productExceptSelf(int* nums, int numsSize, int* returnSize) { int* answer=(int*)malloc(sizeof(int)*numsSize); int i=0; int left=1; int right=1; for(i=0;i<numsSize;i++) { if(i>0) left=left*nums[i-1]; answer[i]=left; } for(i=numsSize-1;i>=0;i--) { if(i<numsSize-1) right=right*nums[i+1]; answer[i]*=right; } *returnSize=numsSize; return answer; }
アイデアの要約:
2つのステップ:
最初の一歩:
新しい配列を開き、元の配列の各要素の左側の積を見つけます(最初の配列要素の場合、左側の積は1つです)
そして、それに応じて左側の製品を新しい配列に配置します。
ステップ2:
元の配列の各要素の正しい積を見つけ、それを新しい配列に対応する要素で乗算し、乗算された結果を新しい配列の対応する要素に入れます。
3.ランダムポインタを使用してリンクリストをコピーします
難易度:中程度
質問:長さnのリンクリストを提供します。各ノードには、リンクリスト内の任意のノードまたは空のノードを指すことができる追加のランダムポインターランダムが含まれています。
このリンクリストのディープコピーを作成します。ディープコピーは、正確にn個の新しいノードで構成する必要があります。ここで、各新しいノードの値は、対応する元のノードの値に設定されます。新しいノードの次のポインターとランダムポインターも、コピーされたリンクリストの新しいノードを指す必要があります。これにより、元のリンクリストとコピーされたリンクリストのこれらのポインターは、同じリンクリストの状態を表すことができます。コピーされたリンクリスト内のどのポインタも、元のリンクリスト内のノードを指してはなりません。
たとえば、元のリンクリストに2つのノードXとYがある場合、X.random->Yです。次に、レプリケーションリンクリストの対応する2つのノードxとyにもx.random->yがあります。複製されたリンクリストのヘッドノードを返します。
入力/出力のリンクリストは、n個のノードのリンクリストで表されます。各ノードは[val、random_index]で表されます。
val:Node.valを表す整数。
random_index:ランダムポインタが指すノードのインデックス(0からn-1の範囲)。ノードを指していない場合はnull。
コードは、元のリンクリストのヘッドノードのみを着信パラメーターとして受け入れます。
ソース:LeetCode
タイトル:
リンクリストを例にとると、C ++のディープコピーと同様に、リンクリストの正確なコピーをコピーする必要があります。
率直に言って、元のノードは元のノードを指しているので、コピーノードのランダムは元のノードのコピーノードを指している必要があります。
元の7の次は元の13を指します。つまり、コピーされた7はコピーされた13を指し、元の7のランダムはNULLを指します。つまり、コピーされた7のランダムはNULLを指します。オリジナル13のランダムはオリジナル7を指します。つまり、コピー13のランダムはコピー7を指します。
7、13、11、10、1、およびNULLをコピーして、次への接続を容易にします。しかし、どのようにランダムに指すのでしょうか?
図:
新しいリンクリストをコピーしたリンクリストに接続します。
元のリンクリストのヘッドポインタしかありません。元の7のランダムはNULLを指していることがわかっているので、7のランダムをコピーしてNULLを指すのは簡単です。元の13のランダムは7を指します。そして、ランダムが指すアドレス空間しか知りません。
つまり、0x11 31 24 31しかわからず、このスペースは新しくコピーされたスペースとは無関係であり、コピーリストのアドレスが指す13をコピーするのに役立ちません。
すべてのより良い方法は、元のリンクリストを新しいコピーリンクリストにリンクすることです。
図:
参照コード:
Node* cur=head;
//在原节点后拷贝节点并连接起来。
while(cur)
{
Node* newnode=(Node*)malloc(sizeof(Node));
newnode->val=cur->val;
newnode->next=cur->next;
cur->next=newnode;
cur=newnode->next;
}
割り当てられたコピーリンクリストのランダムは、次を指します。
元の7のランダムがNULLを指していることがわかっている場合、コピー7もNULLを指します。
元の13のランダムが元の7を指していることを知っている場合、コピー13のランダムはコピー7、つまり元の7の次を指している必要があります。
参照コード:
//给拷贝节点random指针分配指向。
while(cur)
{
copynode=cur->next;
if(cur->random==NULL)
{
copynode->random=NULL;
}
else
{
copynode->random=cur->random->next;
}
cur=cur->next->next;
}
元のリンクリストをアンラップし、リンクリストをコピーします。
コピーリンクリストの先頭を挿入することにより、コピーリンクリストの先頭を別のリンクリストに挿入します。
参照コード:
cur=head;
Node* newhead=NULL;
Node* newtail=NULL;
while(cur)
{
copynode=cur->next;
if(newhead==NULL)
{
newhead=newtail=copynode;
}
else
{
newtail->next=copynode;
newtail=copynode;
}
cur->next=copynode->next;
cur=copynode->next;
}
完全なコードリファレンス:
typedef struct Node Node;
struct Node* copyRandomList(struct Node* head)
{
Node* cur=head;
//在原节点后拷贝节点并连接起来。
while(cur)
{
Node* newnode=(Node*)malloc(sizeof(Node));
newnode->val=cur->val;
newnode->next=cur->next;
cur->next=newnode;
cur=newnode->next;
}
cur=head;
Node* copynode=NULL;
//给拷贝节点random指针分配指向。
while(cur)
{
copynode=cur->next;
if(cur->random==NULL)
{
copynode->random=NULL;
}
else
{
copynode->random=cur->random->next;
}
cur=cur->next->next;
}
//断开连接
cur=head;
Node* newhead=NULL;
Node* newtail=NULL;
while(cur)
{
copynode=cur->next;
if(newhead==NULL)
{
newhead=newtail=copynode;
}
else
{
newtail->next=copynode;
newtail=copynode;
}
cur->next=copynode->next;
cur=copynode->next;
}
return newhead;
}
アイデアの要約:
この問題は、次の3つのステップで解決されます。
接続するための最初のステップ:
元のリンクリストとコピーされたリンクリストを順番に接続し、各コピーノードを対応する元のノードの背面に接続します。
2番目のステップでは、コピーされたリンクリストノードのランダムポインタを割り当てます。
元のリンクリストノードのランダムポインタがNULLの場合、対応するコピーノードのランダムポインタも空です。元のリンクリストノードのランダムポインタが元のリンクリストの別の空でないノードである場合、コピー対応するコピーのランダムなノードは、別の元のリンクリストを指します。リンクリスト内の空でないノードのコピーノード、つまり、元のリンクリスト内の別の空でないノード->次へ。
3番目のステップは、元のリンクリストとコピーされたリンクリストを解凍することです。
元のリンクリストを復元し、コピーノードを接続します。
第四に、最後
この記事がお役に立てば幸いです。ご不明な点がございましたら、コメントまたはプライベートメッセージをお送りください。時間内に返信いたします。ありがとうございます。
踊らない毎日は人生に失望です!