データ構造:1~3双方向接続リスト
今回の投稿では、まずすべてのソースコードを公開し、機能単位で説明します.
今日必要な関数は以下の通りです.()関数 を挿入()関数の削除
->挿入と削除は場所によって異なります. ナビゲーション()関数
->この探索を導入しなければならない.
挿入方式は、最初の挿入、一番前の挿入、最後の挿入、中間の挿入に分けられます.すなわち,4つの論理に対応できる論理を記述する.
-初期挿入時
初期にはheadとtailはnull値に設定されていたはずです.また、接続リストのsizeも0です.新しいノードが作成されると、headとtailは新しいノードのアドレス値を格納することができます.
-一番前に挿入する場合
既存のノードのアドレスを、新しく作成したノードの右側の領域に格納します.次に、新しく作成したノードのアドレスを既存のノードの左側の領域に格納すればよい.最後にheadを新しいノードのアドレス値に更新すればよい.
-最後の挿入時
既存のノードのアドレスを、新しく作成したノードの左側の領域に格納します.次に、新しく作成したノードのアドレスを既存のノードの右側の領域に格納します.最後にtailを新しく生成したノードのアドレス値に更新すればよい.
-挿入時
新しいノードの左と右は、両方のノードのアドレス値で入力されます.また,左側ノードの右側は新しいノードのアドレス値,右側ノードの左側は新しいノードのアドレス値である.
実装コードは以下の通りです.
ノードの削除にも4つの方法があります.
ノードが1つ残っている場合は、削除方法、一番前のノードの削除、一番後ろのノードの削除、真ん中のノードの削除に分けます.1つの関数では,各方式に対応する論理を記述すべきである.
-ノードが1つしか残っていない場合は削除
最も単純です.headとtailをnullに初期化するだけです.削除するノードはゴミ収集器で収集されます.
-一番前のノードを削除
削除するノードの次のノードの左側の領域をnullに初期化します.次に、headを削除するノードの次のノードのアドレスに更新します.次に、削除するノードの左側、data、右側の領域をnullに初期化します.
-最後のノードを削除
削除するノードの前のノードの右側の領域をnullに初期化します.次にtailを削除するノードの前のノードのアドレス値に更新します.最後に、削除するノードの左、データ、右の領域をすべてnullに初期化します.
-中間ノードを削除
削除するノードの前のノードの右側の領域を、削除するノードの次のノードのアドレス値に置き換えます.同様に、削除するノードの後続ノードの左側の領域は、削除するノードの前のノードのアドレス値に置き換えられます.ノードを削除するデータ、左、右の領域をnullで置き換えます.
以上の4つの方式は、多様な条件の制御ゲートを用いて処理しなければならない.
いくつかの例を挙げて説明します
最初のノードを削除するときは である. scope 2、3動作 要素が1つしか残っていない場合は、 を削除します. scope 3、4動作 中間ノードを削除する場合は である. scope 1,2動作 最後のノードを削除すると となる. scope 1,4動作
双方向接続リストにtailという新しい友達が表示されます.検索するインデックスが中間値より小さい場合はheadから検索を開始し、中間値より大きい場合はtailから検索を開始します.すなわち,この関数は二分探索だけで簡単に実現できる.
入力条件文の値は、ポインタに割り当てられた値に依存します.ヘッドから出発して、右に1格移動して、私たちが探している要素を持ってきてくれればいいのに.tailから出発して、左に1つ移動して、私たちが探している要素を持ってきてください.
こうして双方向接続リストの実装を終了するノードがどのように変化し,左−右領域の値が何であるかを理解すれば,双方向を遠慮なく理解できる.次の章はStackです.
public class DoubleLinkedList {
DNode head = null;
DNode tail = null;
int size = 0;
DNode findNode(int index) {
//1. 인덱스 음수고, 사이즈보다 크거나 같으면 예외 발생
if (index < 0 || index >= size) {
throw new ArrayIndexOutOfBoundsException();
}
DNode pointer;
int flag = 0;
if (size/2 > index) {
//2. 앞에서 찾기
pointer = head;
flag = 0;
while (flag != index) {
pointer = pointer.right;
++flag;
}
} else {
//3. 뒤에서 찾기
pointer = tail;
flag = size-1;
while (flag != index) {
pointer = pointer.left;
--flag;
}
}
return pointer;
}
Object getData(int index) {
return this.findNode(index).data;
}
boolean isEmpty() {
return size == 0;
}
int size() {
return size;
}
void addLast(Object data) {
this.add(size, data);
}
void addFirst(Object data) {
this.add(0, data);
}
void removeFirst() {
this.remove(0);
}
void removeLast() {
this.remove(size-1);
}
void add(int index, Object data) {
DNode newNode = new DNode();
newNode.data = data;
if (index == 0 || size == index) {
//1. 최초 노드 삽입 or 맨앞 or 맨 뒤에 노드 삽입 시
// 최초 -> index == 0, size == index
// 맨앞 -> index == 0
// 맨뒤 -> size==index
if (this.head == null && this.tail == null) {
this.head = newNode;
this.tail = newNode;
} else if (index == 0) {
newNode.right = this.head;
this.head.left = newNode;
this.head = newNode;
} else {
newNode.left = this.tail;
this.tail.right = newNode;
this.tail = newNode;
}
} else {
//2. 특정 인덱스에 데이터를 삽입하는 경우
DNode foundNode = this.findNode(index);
DNode leftNode = foundNode.left;
//새로운 노드와 찾은 노드의 연결
newNode.right = foundNode;
foundNode.left = newNode;
//새로운 노드와 왼쪽 노드의 연결
newNode.left = leftNode;
leftNode.right = newNode;
}
++size;
}
void remove(int index) {
DNode foundNode = this.findNode(index);
DNode leftNode = foundNode.left;
DNode rightNode = foundNode.right;
if (leftNode != null) {
leftNode.right = rightNode;
}
if (rightNode != null) {
rightNode.left = leftNode;
}
if (index == 0) {
this.head = rightNode;
}
if (index == size-1) {
this.tail = leftNode;
}
foundNode.left = null;
foundNode.right = null;
foundNode.data = null;
--size;
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
DNode pointer = head;
stringBuilder.append("head").append(" -> ");
while (null != pointer) {
stringBuilder.append(pointer.data).append(" -> ");
pointer = pointer.right;
}
stringBuilder.append("null ");
if (null != tail) {
stringBuilder.append(", tail(").append(tail.data).append("), ");
}
pointer = tail;
stringBuilder.append("tail").append(" -> ");
while (null != pointer) {
stringBuilder.append(pointer.data).append(" -> ");
pointer = pointer.left;
}
stringBuilder.append("null");
if (null != head) {
stringBuilder.append(", head(").append(head.data).append(")");
}
return stringBuilder.toString();
}
}
実装コードは、一方向接続リストよりも複雑である可能性があります.しかし,ノード配置の変化を考慮し,原理を理解すれば実現は難しくないはずである.今日必要な関数は以下の通りです.
->挿入と削除は場所によって異なります.
->この探索を導入しなければならない.
1.関数の挿入()
挿入方式は、最初の挿入、一番前の挿入、最後の挿入、中間の挿入に分けられます.すなわち,4つの論理に対応できる論理を記述する.
-初期挿入時
初期にはheadとtailはnull値に設定されていたはずです.また、接続リストのsizeも0です.新しいノードが作成されると、headとtailは新しいノードのアドレス値を格納することができます.
-一番前に挿入する場合
既存のノードのアドレスを、新しく作成したノードの右側の領域に格納します.次に、新しく作成したノードのアドレスを既存のノードの左側の領域に格納すればよい.最後にheadを新しいノードのアドレス値に更新すればよい.
-最後の挿入時
既存のノードのアドレスを、新しく作成したノードの左側の領域に格納します.次に、新しく作成したノードのアドレスを既存のノードの右側の領域に格納します.最後にtailを新しく生成したノードのアドレス値に更新すればよい.
-挿入時
新しいノードの左と右は、両方のノードのアドレス値で入力されます.また,左側ノードの右側は新しいノードのアドレス値,右側ノードの左側は新しいノードのアドレス値である.
実装コードは以下の通りです.
void add(int index, Object data) {
DNode newNode = new DNode();
newNode.data = data;
if (index == 0 || size == index) {
//1. 최초 노드 삽입 or 맨앞 or 맨 뒤에 노드 삽입 시
// 최초 -> index == 0, size == index
// 맨앞 -> index == 0
// 맨뒤 -> size==index
if (this.head == null && this.tail == null) {
this.head = newNode;
this.tail = newNode;
} else if (index == 0) {
newNode.right = this.head;
this.head.left = newNode;
this.head = newNode;
} else {
newNode.left = this.tail;
this.tail.right = newNode;
this.tail = newNode;
}
} else {
//2. (중간삽입)특정 인덱스에 데이터를 삽입하는 경우
DNode foundNode = this.findNode(index);
DNode leftNode = foundNode.left;
//새로운 노드와 찾은 노드의 연결
newNode.right = foundNode;
foundNode.left = newNode;
//새로운 노드와 왼쪽 노드의 연결
newNode.left = leftNode;
leftNode.right = newNode;
}
++size;
}
2.()関数の削除
ノードの削除にも4つの方法があります.
ノードが1つ残っている場合は、削除方法、一番前のノードの削除、一番後ろのノードの削除、真ん中のノードの削除に分けます.1つの関数では,各方式に対応する論理を記述すべきである.
-ノードが1つしか残っていない場合は削除
最も単純です.headとtailをnullに初期化するだけです.削除するノードはゴミ収集器で収集されます.
-一番前のノードを削除
削除するノードの次のノードの左側の領域をnullに初期化します.次に、headを削除するノードの次のノードのアドレスに更新します.次に、削除するノードの左側、data、右側の領域をnullに初期化します.
-最後のノードを削除
削除するノードの前のノードの右側の領域をnullに初期化します.次にtailを削除するノードの前のノードのアドレス値に更新します.最後に、削除するノードの左、データ、右の領域をすべてnullに初期化します.
-中間ノードを削除
削除するノードの前のノードの右側の領域を、削除するノードの次のノードのアドレス値に置き換えます.同様に、削除するノードの後続ノードの左側の領域は、削除するノードの前のノードのアドレス値に置き換えられます.ノードを削除するデータ、左、右の領域をnullで置き換えます.
以上の4つの方式は、多様な条件の制御ゲートを用いて処理しなければならない.
void remove(int index) {
DNode foundNode = this.findNode(index);
DNode leftNode = foundNode.left;
DNode rightNode = foundNode.right;
if (leftNode != null) {
//scope 1. 좌측 노드가 존재할 경우
leftNode.right = rightNode;
}
if (rightNode != null) {
//scope 2. 우측 노드가 존재할 경우
rightNode.left = leftNode;
}
if (index == 0) {
//scope 3. 첫번째 노드를 삭제할 경우
this.head = rightNode;
}
if (index == size-1) {
//scope 4. 마지막 노드를 삭제할 경우
this.tail = leftNode;
}
foundNode.left = null;
foundNode.right = null;
foundNode.data = null;
--size;
}
あ….これはいったい何ですか.🐶声かどうか聞いてもいいですいくつかの例を挙げて説明します
最初のノード
3.ナビゲーション()関数
双方向接続リストにtailという新しい友達が表示されます.検索するインデックスが中間値より小さい場合はheadから検索を開始し、中間値より大きい場合はtailから検索を開始します.すなわち,この関数は二分探索だけで簡単に実現できる.
入力条件文の値は、ポインタに割り当てられた値に依存します.ヘッドから出発して、右に1格移動して、私たちが探している要素を持ってきてくれればいいのに.tailから出発して、左に1つ移動して、私たちが探している要素を持ってきてください.
DNode findNode(int index) {
//1. 인덱스 음수고, 사이즈보다 크거나 같으면 예외 발생
if (index < 0 || index >= size) {
throw new ArrayIndexOutOfBoundsException();
}
DNode pointer;
int flag = 0;
if (size/2 > index) {
//2. 앞에서 찾기
pointer = head;
flag = 0;
while (flag != index) {
pointer = pointer.right;
++flag;
}
} else {
//3. 뒤에서 찾기
pointer = tail;
flag = size-1;
while (flag != index) {
pointer = pointer.left;
--flag;
}
}
return pointer;
}
4.整理
こうして双方向接続リストの実装を終了するノードがどのように変化し,左−右領域の値が何であるかを理解すれば,双方向を遠慮なく理解できる.次の章はStackです.
Reference
この問題について(データ構造:1~3双方向接続リスト), 我々は、より多くの情報をここで見つけました https://velog.io/@cef5787/자료구조-1-3-양방향-연결리스트テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol