数据结构之双向链表(四)

双向链表

概念

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。
双向链表示意图如下:
双向链表示意图
双向链表的优缺点:
★优点:对于链表中一个给定的结点,可以从两个方向进行操作。
☆缺点:每个结点添加一个额外的指针,因此需要更多的空间开销;结点的插入或删除更加费时(需要更多的指针操作)
双向链表的类型声明,代码如下:

class DLLNode
{
   private int data;
   private DLLNode next;
   private DLLNode previous;
   public DLLNode(int data){
       this.data=data;
   }
   public void setData(int data){
      this.data=data;
   }
   public int getData(){
      return data;
   }
   public void setNext(DLLNode next){
      this.next=next;
   }
   public DLLNode getNext(){
      return this.next;
   }
   public void setPrevious(DLLNode previous){
      this.previous=previous;
   }
   public DLLNode getPreviouds(){
      return this.previous;
   }
}


双向链表的插入

◇在双向链表的开始插入一个结点

  1. 将新结点的后继指针更新为指向当前的表头结点,将新结点的前驱指针赋值为NULL
    将新结点的后继指针更新为指向当前的表头结点
  2. 将表头结点前驱指针更新为指向新结点,然后将新结点作为表头结点
    将表头结点前驱指针更新为指向新结点

◇在双向链表的末尾插入一个结点

  1. 新结点的后续指针指向NULL,其前驱指针指向尾结点
    新结点的后续指针指向NULL
  2. 更新最后一个结点的后续指针,使其指向新结点
    最后一个结点的后续指针指向新结点

◇在双向链表的中间插入一个结点

  1. s的后续指针指向需要插入s的后续指针,然后令s的前驱指针指向p
  2. p的后续结点的前驱指针指向s,p的后续指针指向s
    双向链表中间插入一个结点

    双向链表插入的主要代码如下:

DLLNode DLLInsert(DLLNode headNode,DLLNode nodeToInsert,int position){
    if(headNode==null){
        return nodeToInsert;
    }
    int size=getDLLLength(headNode);
    if(position>size+1 || position<1){
         System.out.println("插入位置无效");
         retrun headNode;
    }
    if(position==1){ //在链表开头插入
        nodeToInsert.setNext(headNode);
        headNode.setPrevious(nodeToInsert);
        return nodeToInsert;
    }else{   //在链表中间或末尾插入
        DLLNode previousNode=headNode;
        int count=1;
        while(count<position-1){
           previousNode=previousNode.getNext();
           count++;
        }  
        DLLNode currentNode=previousNode.getNext();
        nodeToInsert.setNext(currentNode);
        if(currentNode!=null){
            currentNode.setPrevious(nodeToInsert);
        }
        previousNode.setNext(nodeToInsert);
        nodeToInsert.setPrevious(previousNode);
    }
    return headNode;
}

时间复杂度为0(n)
空间复杂度为0(1),用于创建临时变量

双向链表的删除

◆删除双向链表的第一个结点

  1. 修改表头结点的next指针为NULL
    修改表头结点的后趋指针为NULL
  2. 修改表头结点的后续结点的前驱指针为NULL
    修改表头结点的后续结点的前驱指针为NULL

◆删除双向链表的最后一个结点

扫描二维码关注公众号,回复: 3128531 查看本文章
  1. 遍历链表,在遍历时还要保存前驱结点的地址。当遍历到表尾时,有两个指针分别是指向表尾结点的tail指针和指向表尾结点的前驱结点的指针。
    遍历链表寻找表尾结点
  2. 更新表尾的前驱结点的next指针为NULL
    更新表尾的前驱结点的next指针为NULL

◆删除双向链表的中间一个结点

  1. 在遍历链表时保存前驱结点。一旦找到要删除的结点,更新前驱结点的next指针使其指向要被删除结点的下一个结点,更改被删除结点的后续结点的previous指针指向被删除结点的前驱结点
  2. 移除被删除的结点
    删除双向链表中间结点

    双向链表删除的主要代码如下:

DLLNode DLLDelete(DLLNode headNode,int position){
    int size=getDLLLength(headNode);
    if(position>size || position<1){
         System.out.println("删除位置无效");
         retrun headNode;
    }
    if(position==1){ //删除链表的第一个结点
        DLLNode currentNode=headNode.getNext();
        headNode=null;
        currentNode.setPrevious(null);
        return currentNode;
    }else{   //删除中间或表尾结点
        DLLNode previousNode=headNode;
        int count=1;
        while(count<position-1){
           previousNode=previousNode.getNext();
           count++;
        }  
        DLLNode currentNode=previousNode.getNext();
        DLLNode laterNode=currentNode.getNext();
        previousNode.setNext(laterNode);
        if(laterNode!=null){
            laterNode.setPrevious(previousNode);
        }
       currentNode=null;
    }
    return headNode;
}

时间复杂度为0(n)

【结论】
双向链表相对于单链表来说,要更复杂一些,多了前驱指针,所以对于插入和删除来说,需要格外小心。另外由于它的每个结点都需要记录两份指针,所以要占用更多的空间。不过,由于它有良好的对称性,使得对某个结点的前后结点的操作,带来了方便,可以有效的提高算法的时间性能。这也是采用空间换时间性能的一种方法。

猜你喜欢

转载自blog.csdn.net/My_ben/article/details/82384475
今日推荐