二重リンクリスト
このセクションで単独でリンクされたリスト上の詳細は、二重リンクリストのための主要な原理を説明し、並びに各動作の長所と短所。
単一の二重リンクリストは、それが2つの方向をサポートし、各ノードが参照するマップと組み合わせて、複数の次の背面を指す後継ノードポインタだけでなく、先行ノードを指す前駆前のポインタを有します。
図からわかるように、双方向リンクリストは、次のノードおよび先行ノードのアドレスを格納するための追加のスペースが必要です。だから、同じデータを、より多くのスペースを取るよりも片方向リンクリスト、二重リンクリストを格納します。スペースのが、二つのポインタ以上の廃棄物が、双方向のトラバースをサポートすることができ、これはまた、柔軟性、二重リンクリスト操作をもたらします。双方向リンクリストの長所と短所を要約:
利点:
- 与えられたリンクリストのノードは、2つの方向からアクセスすることができます。ノードを削除することができます前に、しかし、片方向リンクリストで、ポインタのみノード前駆体ノードを取得します。
- 二重リンクリストでは、各ノードが先行ノードへのポインタを持っている、あなたは、先行ノードに直接戻ることができます。
短所:
- 各ノードは、余分なポインタを追加する必要があり、そのためより多くのスペースのオーバーヘッドが必要です
- ノードの挿入や削除は、より多くの時間がかかる(複数のポインタ操作)であります
アクションの二重リンクリスト:
まず、二重リンクリストを初期化します。
パッケージcom.xb.other。 / ** *著者郭氏 *は2019年8月11日作成- 12時22分 * / パブリック クラスDLLNode { プライベート int型のデータを、 プライベートDLLNode次。 プライベートDLLNodeのpreivous。 公共 DLLNode(int型データ){ この .DATA = データ。 } 公共 INT のgetData(){ 戻りデータ; } 公共DLLNode getNextを(){ 戻り次。 } 公共DLLNode getPreivous(){ 返すpreivous; } 公共 ボイドのsetData(int型データ){ この .DATA = データ。 } 公共 ボイドsetNext(DLLNode次){ この .next = 次回。 } 公共 ボイドsetPreivous(DLLNode preivous){ この = .preivous preivous。 } }
挿入:
-
- ヘッド挿入ノードリスト
この場合、新しいノードは、ヘッダノードに挿入される前に。ポインタ値を変更する先行および後続ポインタは2つの手順が必要です。
-
-
1、NULLに、現在のノードのヘッダに新しいノードポインタ割り当て前駆体を指すように更新された新たな後継ノードポインタ。
-
図2に示すように、元のヘッダの前駆ノードポインタは、新たなヘッダとして、ノードは、新しいノードに更新されます。
-
-
- リンクリスト挿入ノードの終わりに
この場合は、最後にリストをトラバースし、新しいノードを挿入する必要があります。
-
-
NULL 1、新たな後継ノードポインタは、テーブルの端に先行ノードポインタ。
-
図2に示すように、新たなノードへのテールポインタテーブル元の後続ノード。
- リンクリスト挿入ノードの真ん中で
この場合、ノードトラバーサルテーブルの位置を知っているし、新しいノードを挿入する必要があります。
1、新结点的后继指针指向需要插入新结点的位置结点的后继指针。然后令新结点的前驱指针指向位置结点。
2、位置结点的后继指针指向新结点前驱指针,位置结点的后继结点的前驱指针指向新结点的后继指针
代码实现:
/** * 获取双向链表的长度 */ public int getLength(DLLNode headNode) { int length = 0; DLLNode currentNode = headNode; while (currentNode != null) { length++; currentNode = currentNode.getNext(); } return length; } /** * 链表插入操作 */ public DLLNode DLLInsert(DLLNode headNode, DLLNode nodeToInsert, int position) { // 如果链表为空,则直接插入 if (headNode == null) { return nodeToInsert; } //先获取链表长度 int size = getLength(headNode); // 如果插入位置超出了链表范围,则直接返回头结点,插入范围在[1,size+1] if (position > size + 1 || position < 1) { System.out.println("Posititon of nodeToInsert is invalid." + "The valid inputs are1 to " + (size + 1)); return headNode; } // 如果是在头结点插入 if (position == 1) { nodeToInsert.setNext(headNode); headNode.setPreivous(nodeToInsert); return nodeToInsert; } else { // 如果在链表的中间或尾部插入 DLLNode previousNode = headNode; int count = 1; while (count < position - 1) { previousNode = previousNode.next; count++; } DLLNode currentNode = previousNode.getNext(); nodeToInsert.setNext(currentNode); if (currentNode != null) { currentNode.setPreivous(nodeToInsert); } previousNode.setNext(currentNode); currentNode.setPreivous(previousNode); } return headNode; }
复杂度分析:
时间复杂度为O(n)。在最坏情况下,需要在链表的尾部插入结点。
空间复杂度为O(1)。用于创建临时变量。
删除:
-
- 删除链表的表头结点
这种情况下,需要从双向链表中删除第一个结点,需要通过两步来实现:
1、创建一个临时结点,让它与表头指向同一个结点
2、修改表头结点指针,使其指向下一个结点(headnode=headnode.next),将表头结点的前驱指针更改为NULL(head.setpreivous(NULL)),然后移除临时结点。
-
- 删除链表的表尾结点
该种情况下处理,比删除双向链表的第一个结点要复杂一些,因为要找到尾节点的前驱结点,需要通过三步来实现:
1、遍历链表,在遍历的时候,还要保持前驱(前一次经过的)结点的地址。当遍历到表尾时,有两个指针分别指向表尾结点的tail指针和指向表尾结点的前驱结点指针。
2、更新表尾的前驱结点的next指针为NULL
3、移除表尾结点
-
- 删除链表的中间结点
这种情况下,删除的结点总是位于两个结点之间,因此表头和表尾的值不需要更新。该删除操作可以分为两步来完成:
1、遍历链表,遍历链表的时候保存前驱(前一次经过的)结点。一旦找到要删除的结点。更改前驱结点的next指针使其指向被删除结点的下一个结点,更该被删除结点的后继结点的previous指针指向被删除结点的前驱结点。
2、移除被删除的当前结点
代码实现
/** * 链表的删除操作 */ public DLLNode DLLDelete(DLLNode headNode, int position) { //获取链表的长度 int size = getLength(headNode); //如果删除的位置不在链表的范围内,则直接返回 if (position > size || position < 1) { System.out.println("Posititon of node to delete is invalid." + "The valid inputs are1 to " + size); return headNode; } if (position == 1) { DLLNode currentNode = headNode.getNext(); headNode = null; currentNode.setPreivous(null); return currentNode; } else { DLLNode previousNode = headNode; int count = 1; while (count < position -1) { previousNode = preivous.next; count++; } //要删除的当前结点 DLLNode currentNode = previousNode.getNext(); DLLNode laterNext = currentNode.getNext(); previousNode.setNext(laterNext); if (laterNext != null){ //如果被删除的结点不是NULL结点,那么设置其前驱结点指针指向被删除结点的前驱指针 laterNext.setPreivous(previousNode); currentNode = null; } } return headNode; }
时间复杂度分析:
时间复杂度为:O(n),在最差的情况下,可能需要删除链表尾部结点
空间复杂度为:O(1),仅用于创建一个临时变量。