データ構造とアルゴリズム(C) -のリスト
のブログの庭iwehdio:https://www.cnblogs.com/iwehdio/
1、リストの定義
静的および動的:
- 静的および動的な操作:
- 静的:のみ、一般的に変わらないデータ構造と組成の内容を読み取ります。
- 動的:書き込む必要がある、ローカルまたはグローバルデータ構造が変更されます。
- 静的および動的データ要素が格納されていると方法を組織します。
- 静的:作成または破棄、全体的な空間データ、データ要素の同じ物理的なストレージや言葉の彼らの論理的順序。静的の効率的な動作は、動的な動作は、比較的非効率的です。隣人。
- ダイナミック:ああ、そのデータ要素が多すぎる物理的なスペースの割り当てと回復のため。互いの物理アドレスを論理的に隣接する記録要素は、ロジックに一体的に形成されています。動的および静的動作の効率的な動作は、比較的非効率的です。リストとして。
- 静的および動的な操作:
リスト:動的メモリの典型的な構造。
- 各要素と呼ばれるノード(ノード)。
- 各ノードは、論理の線形配列を形成するために、ポインタまたは参照によって互いに結合されています。
- 互いに互いに隣接ノードの前任者と後継者を呼び出します。前任者と後継者が存在する場合は、ユニークな存在でなければなりません。
- いいえ先行ノード、後続ノードの最初のではなく、端部が呼び出されることがないこと。センチネル第一前駆体の存在は頭、尾と呼ばれるセンチネルのその後の存在の終わりと呼ばれることが考えられます。
- ヘッドランクが、最初に考慮されてもよい、テール・ノードの端部は、-1,0である、N-1、N。
- アクセスランクアクセスサイクル、使用サイクルのアクセス位置を使用できない場合。即ちノードを使用して別の記事を参照し、特定のノードを見つけます。
リストADTインタフェースの要素:
ノードのリスト:ListNodeのテンプレートクラス。
#define Posi(T) ListNode<T>* //定义指向列表节点的数据类型(节点位置) template <typename T> struct ListNode{ T data; //数值内容 Posi(T) pred; //前驱 Post(T) succ; //后继 ListNode() {} //针对头尾哨兵的无参构造 ListNode(T e, Posi(T) p=null, Posi(T) s=null) : data(e), pred(p), succ(s) {} //默认构造器 Posi(T) insertAsPred(T const& e); //前插入 Posi(T) insertAsSucc(T const& e); //后插入 }
ADTインタフェースの一覧:
リスト:リストテンプレートクラス:
#include "ListNode.h" //引入列表节点类 template <typename T> class List { private: int _size; Posi(T) header; Posi(T) trailer; //头尾哨兵 protected: /*内部函数*/ public: /*构造函数、析构函数、对外的接口函数等*/ }
初期化関数のリスト:
template <typename T> void List<T>::init() { header = new ListNode<T>; //创建头尾哨兵节点 trailer = new ListNode<T>; header -> succ = trailer; //互联元素 header -> pred = null; trailer -> pred = header; trailer -> succ = null; _size = 0; //记录规模 }
2、操作順不同リスト
ランクフォロー訪問:
模倣順位リストは、(オーバーロードさによって添字演算子)アクセスベクタに従うことができます。
効率は比較的低いが、時間複雑度は、O(N)です。
実装:
template <typename T> T List<T>::operator[](Rank r) const { Posi(T) p = first(); //从首节点出发 while(0 < r++) p = p->succ; //顺数第r个节点 return p ->data; }
操作を探します:
定義:(おそらく尾センチネル)N真前駆指定されたノードの最後に見出されるが、E pに等しいです。
実装:
template <typename T> Posi(T) List<T>::find(T const& e, int n, Posi(T) p) const { while(0 < n--) //从右到左逐个比较,直到命中或越界 if(e == (p = p->pred)->data) return p; return null; }
挿入操作(以前に、例えば挿入):
新しいノードe、Pを作成し、前駆体として挿入。
新しいノードeを作成し、pはオリジナルの前身の前駆体である、後継者がeです。オリジナルセット以降の前駆体がp eは、pは、新たな前駆物質の電子です。
実装:
template <typename T> Posi(T) List<T>::insertBefore(Posi(T) p, T const& e){ _size++; return p->insertAsPred(e); //把e当作p的前驱插入 } template <typename T> Posi(T) ListNode<T>::insertAsPred(T const& e){ Posi(T) x = new ListNode(e, pred, this); //创建新节点e,前驱是p的原前驱,后继是e pred->succ = x; //p的原前驱的后继是e pred = x; //p的新前驱是e return x; //返回新节点的位置 }
レプリケーションの構成に基づいて:
制御リストを作成し、ノードpから渡されたN末端アイテムの開始は非常にノードとして挿入します。
実装:
template <typename T> void List<T>::copyNodes(Post(T) p, int n){ init(); //初始化空列表 while(n--){ //insertAsLast(p->data)实际上就是insertBefore(trailer, p->data) insertAsLast(p->data); p = p->succ; } }
削除操作:
着信におけるPノードを削除します。
Pの新たな後継割り当て前駆体は、Pオリジナル後継、Pのオリジナルの前駆物質P前駆体の新規割り当ての後継です。
実装:
template <typename T> T List<T>::remove(Posi(T) p){ T e = p->data; p->pred->succ = p->succ; //将p的前驱的新后继赋值为p的原后继 p->succ->pred = p->pred; //将p的后继的新前驱赋值为p的原前驱。 delete p; _size--; return e; //返回被删除的值 }
デストラクタ操作:
まず、すべての可視ノードを削除し、その後、頭と尾センチネルを削除します。
達成するために繰り返し0のスケールまでノードによって最初可視ノードを削除する削除します。
実装:
template <typename T> List<T>::~List() //列表析构 { clear(); delete header; delete trailer; } template <typename T> int List<T>::clear(){ int oldSize = _size; while(0 < _size){ remove(header->succ); } return oldSize; }
リストのみの:
重複ノードの順不同のリストを削除します。
最初のノードの開始から、及びその前身で重複ノードがあるかどうかを調べるためにそう。前駆体中の同一のエントリがある場合にのみノードよりも、検索する必要がそうすることを、先行ノードが常に繰り返しなしで検索する、拡張された後方を作ります。
実装:
template <typename T> int List<T>::dedupicate(){ if(_size < 2) return 0; int oldSize = _size; Posi(T) o = first(); //从首节点开始 Rank r = 1; while(trailer != (p = p->succ)){ Posi(T) q = find(p->data, r, p); //在p的r个真前驱中,有没有雷同的 q ? remove(q) : r++; //如果有雷同的就移除前驱中雷同的项,否则将不雷同的前驱向后扩展一项 } return oldSize - _size; //返回被删除的元素数 }
リストの唯一の順序:
削除重複は、ノードのリストを命じました。
順序ので、任意の類似性は、常に近い部分です。
同じ後続の削除と、各セグメントの最初のノードへのP点、。同一でない後継者が後継者に、P点を表示された場合。
実装:
template <typename T> int List<T>::uniquify(){ if(_size < 2) return 0; int oldSize = _size; ListNodePosi(T) p = first(); //p初始化为首节点,p为每个区段的首节点 ListNodePosi(T) q; //q为各个区段中p的后继 while(trailer != (q->succ)){ //考察紧邻的节点对 if(p->data != q->data){ p = q; } else { remove(q); } } return oldSize - _size; }
時間計算量はO(N)です。
アクションの順序付きリストを表示して下さい:
- 全く同じ障害ベクトル探索アルゴリズム、及びリストの決定の循環によってアクセスされる機能である効率のない改善。
3、挿入ソートと選択ソート
並び替えを選択:
最後で最大のリスト内のすべての要素を選択し、最後で最大の要素を残りの場所前駆体を選択し、繰り返し実行。すなわち、毎回残りの選択されたセットの最大値です。
ソートバブルソートオプションであるが、原因位置スワップ隣接する要素のための時間に、低効率をもたらします。
改善:ランダムシーケンスに配列プレフィックスとサフィックス規則正しい配列LがよりまたはUの要素の同じ要素大きいU L、。各反復において、U、Lで最大の要素を選択し、最前線に移動します。
実装:
template <typename T> void List<T>::selectionSort(Posi(T) p, int n){ Posi(T) head = p->pred; Posi(T) tail = p; for(int i=0; i<n; i++) tail = tail->succ; //待排序区间为(head,tail) //从待排序区间内找出最大者,将其从原位置删除并插入tail的位置,然后将tail前移,,待排序区间宽度-1 while(1 < n){ //insertBefore()和remove()虽然可以认为时O(1)时间,但由于使用了动态内存分配,会使效率大大降低。实际上更倾向于进行节点数据域的交换 insertBefore(tail, remove(selectMax(head->succ, n))); tail = tail->pred; n--; } } template <typename T> //获取起始于p的n个元素的最大值,画家算法 Posi(T) List<T>::selectMax(Posi(T) p, int n){ Posi(T) max = p; //初始化p为最大 for(Posi(T) cur=p; 1<n; n--){ //后续节点与max逐一比较,!lt(a,b)比较器表示a不小于(not less than,即>=)b时 if(!lt((cur=cur->succ)->data, max->data){ max = cur; } } return max; }
時間の複雑さは常にΘ(nは^ 2)、ない最高と最悪のケースです。しかし、バブルソート操作の移動要素と比べて大幅に低減されます。
挿入ソート:
接頭辞[0、R)の順序付けられたシーケンスに全配列、および接尾辞配列障害[R、N-)、接頭語の順序付けられたシーケンス内の適切な位置に最初R元素、依然として配列を注文順序こと接頭スケールプラスワン。
;したがって、無秩序部分ソート操作対象の最大値、挿入ソート操作対象を選択するソート選択要素は、常に乱れ部分よりも小さい部分を注文し、そして挿入ソートはそのような制限はありません:選択したソートの間の差不規則部分となっており素子の接合部。
実装:
template <typename T> void List<T>::insertionSort(Posi(T) p, int n){ for(int r=0; r<n; r++){ //r实际上就是有序前缀序列的长度 //每次在search()返回的位置插入,然后删除原位置上的节点 insertAfter(search(p->data, r, p), p->data); p = p->succ; remove(p->pred); } }
時間計算:好ましくは、O(n)は、最悪O(N ^ 2)は、平均的な複雑さはO(N ^ 2)です。
逆の場合:大小の最初の2つの要素。
- 、Bの数の逆の順序が逆の順序(> b)のであれば、我々は対応するBを追加します。その心の右側に比較して逆の順序で小さなロールの数を逆ようすなわち、番号の逆の順序に対応する要素です。
- 挿入ソートの場合は、比較の数の逆のリストを行うことを特徴づけるためにログオンします。最高の状態で、順序、逆の順序番号0のリスト。最悪の場合は、リストが逆数N ^ 2に、逆です。複雑さは、時間と時間の比較に挿入される時には、比較時間は、好ましくは0であり、最悪は、O(N ^ 2)であり、挿入時間はO(n)。何とか複雑さは、好ましくは、O(n)は、最悪O(N ^ 2)です。
- 挿入ソートの時間計算は、入力に敏感であり、逆入力シーケンスの数に依存します。
参考:データ構造とアルゴリズム(清華C ++記述):https://www.bilibili.com/video/av49361421
iwehdioブログの庭:https://www.cnblogs.com/iwehdio/