データ構造 - リスト(2)

二重リンクリスト

このセクションで単独でリンクされたリスト上の詳細は、二重リンクリストのための主要な原理を説明し、並びに各動作の長所と短所。

 

単一の二重リンクリストは、それが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. 1、NULLに、現在のノードのヘッダに新しいノードポインタ割り当て前駆体を指すように更新された新たな後継ノードポインタ。
    2.  


      図2に示すように、元のヘッダの前駆ノードポインタは、新たなヘッダとして、ノードは、新しいノードに更新されます。

        

    • リンクリスト挿入ノードの終わりに

       この場合は、最後にリストをトラバースし、新しいノードを挿入する必要があります。

    1. NULL 1、新たな後継ノードポインタは、テーブルの端に先行ノードポインタ。

    1. 図2に示すように、新たなノードへのテールポインタテーブル元の後続ノード。 
    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),仅用于创建一个临时变量。

おすすめ

転載: www.cnblogs.com/Gxiaobai/p/11343479.html