1. 程式人生 > >數據結構與算法(四)-線性表之循環鏈表

數據結構與算法(四)-線性表之循環鏈表

log ddc 兩個 方向 http return close 單向 throw

前言:前面幾篇介紹了線性表的順序和鏈式存儲結構,其中鏈式存儲結構為單向鏈表(即一個方向的有限長度、不循環的鏈表),對於單鏈表,由於每個節點只存儲了向後的指針,到了尾部標識就停止了向後鏈的操作。也就是說只能向後走,如果走過了,就回不去了,還得重頭開始遍歷,所以就衍生出了循環鏈表

一、簡介

  定義:將單鏈表中中斷結點的指針端有空指針改為指向頭結點,就使整個單鏈表形成一個環,這種頭尾詳解的單鏈表稱為單循環鏈表,簡稱循環鏈表; 技術分享圖片

技術分享圖片   特性:  
  • 若鏈表為空,則頭結點的next結點還是指向其本身,即head.next=head;
  • 尾節點的next指針指向head結點,即頭尾相連;
  • 判斷是否遍歷了完,直接判斷next==head即可;
  • 由單鏈表變化的循環也成為單向循環鏈表;
  • 循環鏈表的特點是無須增加存儲量,僅對表的鏈接方式稍作改變,即可使得表處理更加方便靈活;

二、單向循環鏈表的實現

  循環鏈表是在單鏈表(線性單鏈表)的基礎上,將尾節點的next指向了head,所以基本的結構是類似的,所以下面,直接貼代碼了;    技術分享圖片
public class LoopChain<T> {

    //尾結點直接引用
    private Node<T> tail;

    //鏈長度
    private Integer size;

    
//初始化 LoopChain() { tail = new Node<T>(); tail.setNext(tail); } public Node<T> remove(Integer index) throws Exception { //獲取該位置的上一個節點 Node<T> s = getNode(index - 1); //獲取該位置節點的下一個節點 Node<T> next = getNode(index).getNext();
//將本節點的next節點放在本節點的前一個節點的next節點位置 s.setNext(next.getNext()); return next; } public void add(T t,Integer index) throws Exception { //獲取該位置的上一個節點 Node<T> s = getNode(index - 1); //創建新節點 Node<T> p = new Node<>(); p.setObject(t); //將本節點的next節點放入新節點的next節點 p.setNext(s.getNext()); //將新節點放入本節點的next節點位置 s.setNext(p); } public T get(Integer index) throws Exception { return (T)getNode(index).getObject(); } private Node<T> getNode(Integer index) throws Exception { if (index > size || index < 0) throw new Exception("index outof length"); //取頭節點 Node<T> p = tail.next; for (int i = 0; i < index; i++) p = p.getNext(); return p; } class Node<T> { private Object object; private Node next; public Object getObject() { return object; } public void setObject(Object object) { this.object = object; } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } } }
LoopChain.java

  獲取元素:

    public T get(Integer index) throws Exception {
        return (T)getNode(index).getObject();
    }

    private Node<T> getNode(Integer index) throws Exception {
        if (index > size || index < 0)
            throw new Exception("index outof length");
        //取頭節點
        Node<T> p = tail.next;
        for (int i = 0; i < index; i++)
            p = p.getNext();
        return p;
    }

  插入元素:

    public void add(T t,Integer index) throws Exception {
        //獲取該位置的上一個節點
        Node<T> s = getNode(index - 1);
        //創建新節點
        Node<T> p = new Node<>();
        //將本節點的next節點放入新節點的next節點
        p.setNext(s.getNext());
        //將新節點放入本節點的next節點位置
        s.setNext(p);
    }

  移除元素:

    public Node<T> remove(Integer index) throws Exception {
        //獲取該位置的上一個節點
        Node<T> s = getNode(index - 1);
        //獲取該位置節點的下一個節點
        Node<T> next = getNode(index).getNext();
        //將本節點的next節點放在本節點的前一個節點的next節點位置
        s.setNext(next.getNext());
        return next;
    }
在之前學習單鏈表的時候,我們使用頭結點來代表一個鏈表,可以用O(1)的時間訪問第一個節點,但是在訪問尾節點時,需要使用O(n)的時間,而循環鏈表則不同,完全可以使用O(1)的時間來訪問第一個節點和尾節點。

三、問題

  判斷單鏈表中是否有環?   思考:有環的定義是,鏈表的尾節點指向了鏈表中的某個節點;   那麽怎麽判斷是否有環的存在呢?   1、使用p、q兩個指針,p總是向前走,但q每次都從頭開始走,對於每個節點,p走的步數是否與q走的步數一致,若不一致則說明存在環;   若鏈表長度為3,當p走完一圈後,會出現p的步數為4,而q的步數為1,不相等,則存在環。   2、使用p、q兩個指針,p每次向前走一步,q每次向前走兩步,若在某個時候p==q,則存在環;   使用了快慢原理來進行判斷是否存在環。      註意   ①循環鏈表中沒有NULL指針。涉及遍歷操作時,其終止條件就不再是像非循環鏈表那樣判別p或p->next是否為空,而是判別它們是否等於某一指定指針,如頭指針或尾指針等。   ②在單鏈表中,從一已知結點出發,只能訪問到該結點及其後續結點,無法找到該結點之前的其它結點。而在單循環鏈表中,從任一結點出發都可訪問到表中所有結點,這一優點使某些運算在單循環鏈表上易於實現。

本系列參考書籍:

  《寫給大家看的算法書》

  《圖靈程序設計叢書 算法 第4版》

數據結構與算法(四)-線性表之循環鏈表