数据结构之循环链表(三)

循环链表

概念

将单链表中的尾结点的指针由空指针指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表。
【注意】
因为循环链表没有NULL来表示链表的结束,因而遍历循环链表时需要特别小心,否则将会无线遍历循环链表,因为在循环链表中每个结点都有一个后续结点。
【作用】
因为循环链表中没有next指针为NULL的结点。所以,循环链表有时非常有用,比如:当多个进程需要在相同的时间内使用同一个计算机资源(CPU)时,必须确保在所有其他进程使用这些资源前,没有进程访问该资源(轮询算法)。
循环链表示意图如下:
循环链表示意图
循环链表的插入

主要操作如下:
◆统计循环链表的结点个数
循环链表可以通过标记为表头的结点进行访问。为了统计结点个数,只能从标记为表头的结点开始遍历,利用虚拟结点一一计数当前结点,当当前结点再次到达开始结点时,结束计数过程。若链表为空,表头结点为NULL,在这种情况下设结点个数等于0.否则,设置当前结点指向第一个结点(表头结点),然后遍历链表进行计数,直到当前结点达到开始结点。其主要代码如下:

int CircularListLength(CLLNode headNode){
    int length=0;
    CllNode currentNode=headNode;
    while(currentNode!=null){
         length++;
         currentNode=currentNode.getNext();
         if(currentNode==headNode)
               break;
         }
         return length;
}

时间复杂度为0(n),用于扫描长度为n的链表。
空间复杂度为0(1),用于创建一个临时变量。

◆输出循环链表的内容
假设循环链表可以通过表头结点进行访问。由于所有的结点采用循环方式排列,所以链表的表头结点的前驱就是表尾结点。假设要输出从表头结点开始的结点内容,输出结点内容,移动到下一个结点,继续输出直至再次达到表头结点。其主要代码如下:

void PrintCircularData(CLLNode headNode){
      CLLNode CLLNode=headNode;
      while(CLLNode!=null){
         System.out.println(CLLNode.getData()+"->");
         CLLNode=CLLNode.getNext();
         if(CLLNode==headNode)
              break;
     }
     System.out.println("("CLLNode.getData()+")headNode");
}

时间复杂度为0(n),用于扫描长度为n的链表。

◆在循环链表的表尾插入结点

  1. 创建一个新节点,并且初始化其next指针指向该结点自身
    创建新结点
  2. 更新新结点的next指针为表头结点,然后遍历循环链表直至表尾
    更新新结点的next指针为表头结点
  3. 更新表头的前驱结点的next指针指向新结点,得到循环链表
    更新表头的前驱结点的next指针指向新结点
    其主要代码如下:
void InsertAtEndInCLL(CLLNode headNode,CLLNode nodeToInsert){
          CLLNode currentNode=headNode;  //创建新结点
          whlie(currentNode.getNext()!=headNode){
            currentNode.setNext(currentNode.getNext());
            }
          nodeToInsert.setNext(nodeToInsert);
          if(headNode==null)  //判断头结点是否为空
             headNode=nodeToInsert;
          else{
               nodeToInsert.setNext(headNode);
               currentNode.setNext(nodeToInsert);
          }
}

时间复杂度为0(n),用于扫描长度为n的链表。

◆在循环链表的表头插入结点

  1. 创建一个新节点,并且初始化其next指针指向结点自身
  2. 更新新结点的next指针为表头指针,然后遍历循环链表直至表尾
  3. 更新链表中表头的前驱结点的next指针,使其指向新结点
  4. 设置新结点为表头结点

其主要代码如下:

void InsertAtBeginInCLL(CLLNode headNode,CLLNode nodeToInsert){
          CLLNode currentNode=headNode;  //创建新结点
          whlie(currentNode.getNext()!=headNode){
            currentNode.setNext(currentNode.getNext());
            }  //遍历循环链表直至表尾
          nodeToInsert.setNext(nodeToInsert);
          if(headNode==null)  //判断头结点是否为空
             headNode=nodeToInsert;
          else{  //设置新结点为表头结点
               nodeToInsert.setNext(headNode);
               currentNode.setNext(nodeToInsert);
               headNode=nodeToInsert;
          }
}

时间复杂度为0(n),用于扫描长度为n的链表。

循环链表的删除

■删除循环链表中最后一个结点

  1. 遍历循环链表,找到表尾结点及其前驱结点
    搜索表尾结点及其前驱结点示意图
  2. 更新表尾结点的前驱结点的next指针,使其指向表头结点
    更新表尾结点的前驱结点的next指针示意图
  3. 移除表尾结点

其主要代码如下:

void DeleteLastNodeFromCLL(CLLNode headNode){
          CLLNode temp=headNode;
          CLLNode currentNode=headNode;
          if(headNode==null){
              System.out.println("链表为空");
              return;
          }
          whlie(currentNode.getNext()!=headNode){
            temp=currentNode;
            currentNode.setNext(currentNode.getNext());
          }  //遍历循环链表,找到表尾结点及其前驱结点
          temp.setNext(headNode);
          currentNode=null;
          return;
}

时间复杂度为0(n),用于扫描长度为n的链表。

■删除循环链表中的第一个结点

  1. 遍历循环链表找到表尾结点。
  2. 更新表尾结点的next指针,使其指向第一个结点的后继结点
  3. 移除第一个结点

    其主要代码如下:

void DeleteFirstNodeFromCLL(CLLNode headNode){
          CLLNode temp=headNode;
          CLLNode currentNode=headNode;
          if(headNode==null){
              System.out.println("链表为空");
              return;
          }
          whlie(currentNode.getNext()!=headNode){
              currentNode.setNext(currentNode.getNext());
          }  //遍历循环链表,找到表尾结点及其前驱结点
          currentNode.setNext(headNode.getNext());
          headNode=headNode.getNext();
          temp=null;
          return;
}

时间复杂度为0(n),用于扫描长度为n的链表。

猜你喜欢

转载自blog.csdn.net/My_ben/article/details/82349759