リンクリストに関連するアルゴリズム

最近駅で高品質のコースを見つけた、以下はコースノートです!

基本的な操作

ノード構造

typedef struct{
    int val;
    Node* next;
}Node;

検索操作

Node* search(int val){
    Node* pNode = root; // 根结点
    while(pNode != nullptr)
         if(pNode->val == val){
             return pNode;
          }
         pNode = pNode->next;
        }
     return nullptr;
 }

削除操作–削除するノードの前のノードを指定します

void delNode(Node* prev){
	Node* curr = prev->next;
	prev->next = curr->next;
	delete curr;
}

削除操作-削除するノードを指定

void delNode(Node* curr){
	if(curr->next != nullptr){
		Node* temp = curr->next;
		curr->val = temp->val;
		curr->next = temp->next;
		delete temp;
	}else{
		Node* pNode = root; // 头节点
		while(pNode->next != curr){
			pNode = pNode->next;
		}
		pNode->next = nullptr;
		delete curr;
	}
}

関連アルゴリズム

リンクされたリスト要素の重複排除

並べ替え
られたリストからの重複の削除並べ替えられたリンクリストが与えられた場合、すべての重複を削除して、各要素が1回だけ表示されるようにします。 3-> 3、1-> 2-> 3を返す

Node* deleteDuplicates(Node* head){
	if(head == nullptr){
		return nullptr;
	}
	Node* node = head;
	while(node->next != nullptr){
		if(node->val == node->next->val){
			Node* temp = node->next;
			node->next = temp->next;
			delete temp;
		}else{
			node = node->next;
		}
	}
	return head; 
}

ダミーノードのヒント
考慮事項:
a。どのノードの次のポインターが影響を受けるか、ポインターを修正する必要があります
b。削除するノードが動的に開かれるメモリスペースである場合、
ダミーノードを使用してメモリのこの部分(c / c ++)解放する必要があります。非常に便利なトリック:長いヘッドノード、ヘッドノードの操作の動作に関わる不確実性などのように、ノードのダミー作成することがあります:
ListNode
ダミー=新新ListNode(0);
dummy-> =次の頭を;
*

ソートされたリストから重複を削除||
ソートされたリンクリストを前提として、重複する番号を持つすべてのノードを削除し、元のリストとは異なる番号のみを残します。
たとえば、
1-> 2-> 3-> 3-> 4-> 4-> 5の場合、1-> 2-> 5を返します。
1-> 1-> 1-> 2-> 3の場合、2-> 3を返します

Node* deleteDuplicates(Node* head){
	if(head == nullptr){
		return nullptr;
	}
	Node* dummy = new Node(0);
	dummy->next = head;
	Node* node = dummy;
	while(node->next != nullptr && node->next->next != nullptr){
		if(node->next->val == node->next->next->val){
			int val_pre = node->next->val;
			while(node->next != nullptr && val_pre == node->next->val){
				Node* temp = node->next;
				node->next = temp->next;
				delete temp;
			}
		}else{
			node = node->next;
		}
	}
	return dummy->next;
}

リンクリストと値を指定して、小さい比率がすべて左になり、大きい比率が右になるように、リンクリストを並べ替える並べ替えを記述します。

Node* partition(Node* head, int x){
	if(head == nullptr){
		return nullptr;
	}
	Node* dummyleft = new Node(0);
	Node* dummyright = new Node(0);
	Node* left = dummyleft;
	Node* right = dummyright;
	Node* node = head;
	while(node->next != nullptr){
		if(node->val < x){
			left->next = node;
			left = left->next;
		}else{
			right->next = node;
			right = right->next;
		}
		node = node->next;
	}
	left->next = dummyright->next;
	right->next = nullptr;
	return dummyleft->next;
}

ポインタースキルをキャッチ

リストの特定の位置を見つける問題については、2つのチェイサーとランナーを使用して、リストを異なる速度でトラバースし、ターゲット位置を見つけることができます。ノードチェイサー=ヘッド、ランナー=ヘッド。簡単な小さなテストケースを使用して、

リンクされたリストの中点を見つける

Node* midpoint(Node* head){
	if(head == nullptr){
		return nullptr;
	}
	Node* chaser = head, runner = head;
	while(runner && runner->next){
		runner = runner->next->next;
		chaser = chaser->next;
	}
	return chaser;
}

リンクリストの最後から2番目のkノードを見つける

Node* findkthtoLast(Node* head, int k){
	if(head == nullptr || k < 0){
		return nullptr;
	}
	Node* runner = head;
	Node* chaser = head;
	while(k){
		runner = runner->next;
	}
	if(runner == nullptr){
		return nullptr;
	}
	while(runner->next != nullptr){
		runner = runner->next;
		chaser = chaser->next;
	}
	return chaser;
}

リング付きのリンクリストを指定して、リングが開始するノードを見つけます

分析:特定の場所を見つけるには、ランナー手法を使用します。ランナーがループがあると仮定すると、ランナーは2倍の速度でトラバースします。その場合、ランナーとチェイサーは特定のポイントで出会う必要があります。遭遇後、チェイスを頭からトリガーして再度ランナーに追いつくと、2回目の遭遇のノードがループの開始位置になります

2つの単一リンクリストに共通部分があるかどうかを判断する方法

リンクリストのk番目の要素の後のノードを前に移動します

例:
リスト= 10-> 20-> 30-> 40-> 50-> 60 k = 4
50-> 60-> 10-> 20-> 30-> 40に変更

void rotate(Node** root, int k){
	if(k == 0){
		return;
	}
	Node* current = root;
	for(int count = 1; count < k && current != nullptr;count++){
		current = current->next;
	}
	if(current == nullptr){
		return;
	}
	Node *kthNode = current;
	while(current->next != nullptr){
		current = current->next;
	}
	current->next = root;
	root = kthNode->next;
	kthNode->next = nullptr;
}


パターン認識
1.リンクリストをたどる場合、ループごとに1つまたはペアのノードのみが処理されることに注意してください。コアノードは現在のノードのみを処理します。そうしないと、処理の問題が繰り返し発生しやすくなります。

リバースリンクリスト

リンクリストの反転リンクリストを
反転して、新しいヘッドを返します。

—递归版本—

Node* reverseList(Node* head){
	if(head == nullptr){
		return nullptr;
	}
	Node* prev = nullptr;
	while(head != nullptr){
		Node* curr = head;
		head = head->next;
		curr->next = prev;
		prev = curr;
	}
	return prev;
}

—非再帰バージョン—

Node* reverseList(Node* head){
	if(head == nullptr){
		return head;
	}
	if(head->next == nullptr){
		return head;
	}
	Node* newHead = reverseList(head->next);
	head->next->next = head;
	head->next = nullptr;
	return newhead;
}


パターン認識
2.スワップノードの問題
3. 2つのノードがスワップされ、削除がない場合、2つのノードの前のノードの次のポインターとこれら2つのノードの次のポインターが影響を受けます。常に可能です
a。最初に2つの前のノード
の次のポインターの値を交換しますb。次に
2つのノードの次のポインターの値を交換します2つのノードの相対位置と絶対位置に関係なく、上記の処理方法は常に保持されます

隣接する2つのノードを交換する

隣接するノードを
入れ替えるリンクされたリストが与えられた場合、隣接する2つのノードごとに入れ替え、その先頭を返します。

Node* swapPairs(Node* head){
	if(head == nullptr){
		return head;
	}
	Node* dummy = new Node(0);
	dummy->next = head;
	Node* prev = dummy;
	Node* node1 = head;
	Node* node2 = head->next;
	while(node1 && node1->next != nullptr){
		node2 = node1->next;
		prev->next = node1->next;
		
		node1->next = node2->next;
		node2->next = node1;

		prev = node1;
		node1 = prev->next;
	}
	return dummy->next;
}


パターン認識
3.工程2つのリンクされたリストの問題は、循環条件は、一般に、一方(L1 && L2)を使用することができ、そしてこの場合、非NULLのリストの残りの部分を処理する、境界ケース特別な処理、従来の場合、従来の治療法。

リンクリスト追加

リストの合計を追加する
2つのリンクされたリストがある場合、リストの各要素は整数です。新しいリストを返す関数を記述します。これは、指定された2つのリストの「合計」です。
パートa。入力(7-> 1-> 6)+(5-> 9-> 2)が与えられた場合、出力2-> 1-> 9。
パートb 入力(6-> 1-> 7)+(2-> 9-> 5)の場合、出力9-> 1-> 2になります。

パートa–

Node* addTwoNumbers(Node* l1, Node* l2){
	Node dummy(0);
	Node* p = &dummy;
	int cn = 0;
	while(l1 || l2){
		int val = cn + (l1 ? l1->val : 0) + (l2 ? l2->val : 0);
		cn = val / 10;
		val = val % 10;
		p->next = new Node(val);
		p = p->next;
		if(l1){
			l1 = l1->next;
		}
		if(l2){
			l2 = l2->next;
		}
	}
	if(cn != 0){
		p->next = new Node(cn);
		p = p->next;
	}
	return dummy->next;
}

Partb-
問題分析:aの場合、フロントノードのソリューションはリアノードに依存しないため、ソリューションを順番にたどることで十分です。bの場合、フロントノードのソリューションはバックノード(キャリー)に依存するため、再帰またはスタックで処理する必要があります。また、サブ問題によって返される結果は、カスタム構造(キャリー+サブリスト)になる場合があります。もちろん、逆リストを解いてからaの解を使うこともできます。

2つ以上のソートされたリンクリストを組み合わせる

2つのソート・リストのマージ
マージ2をリンクされたリストをソートして、新しいリストとしてそれを返します。

Node* mergeTwoLists(Node* l1, Node* l2){
	Node* dummy = new Node(0);
	Node* curr = dummy;
	while(l1 && l2){
		if(l1->val <= l2->val){
			curr->next = l1;
			l1 = l1->next;
		}{
			curr->next = l2;
			l2 = l2->next;
		}
		curr = curr->next;
	}
	curr->next = (l1 != nullptr) ? l1 : l2;
	return dummy->next;
}

Kソート済みリストのマージ

Node* mergeKLists(vector<Node*>&lists){
	if(lists.size() == 0)return nullptr;
	Node* p = lists[0];
	for(int i = 0; i < l1sts.size(); i++){
		p = mergeTwoLists(p, lists[i]);
	}
	return p;
} 

終わってない!

21件のオリジナル記事を掲載 いいね!0 訪問数163

おすすめ

転載: blog.csdn.net/qq_45227330/article/details/105060058