使用單指針實現雙鏈表(C++語言)

本文是Clifford A. Shaffer所著《數據結構與算法分析》(C++版)習題4.4的解答。

鏈表是常見的數據結構,鏈表中的結點通常定義如下。

template <typename E> class Link {
public:
  E element;
  Link<E> *next;

  Link(const E& it, Link<E>* next) { }
};

結點是一個定義爲Link的模板類,其元素element的類型E可以代入不同的具體類型,指針成員nex指向下一個結點。只有一個指針的結點只能用於單鏈表,在使用時需要保存一個頭指針head,搜索鏈表的時候,每次都要從head開始,而且只能向後搜索,不能向前搜索。

雙鏈表中的結點含有兩個指針域,分別爲prev和next,如下所示。

template <typename E> class Link {
public:
  E element;
  Link<E> *prev, *next;

  Link(const E& it, Link<E>* prev, Link<E>* next) { }
};
雙鏈表在使用時可以保存一個頭指針head,一個尾指針next。既可以從head出發向後搜索,也可以從tail出發向前搜索。如果再增加一個當前指針curr,那麼在單鏈表中,只能從curr出發向後搜索,而在雙鏈表中,可以從curr出發向後或者向前搜索。

基於結點類,可以設計鏈表類,下面的抽象數據類型給出了鏈表類所需要支持的基本操作。

template <typename E> class List { // List ADT
private:
  void operator =(const List&) {} 
  List(const List&) {} 
public:
  List() {}
  virtual ~List() {}
  virtual void clear() = 0;
  virtual void insert(const E& item) = 0;
  virtual void append(const E& item) = 0;
  virtual E remove() = 0;
  virtual void moveToStart() = 0;
  virtual void moveToEnd() = 0;
  virtual void prev() = 0;
  virtual void next() = 0;
  virtual int length() const = 0;
  virtual int currPos() = 0;
  virtual void moveToPos(int pos) = 0;
  virtual const E& getValue() const = 0;
};
基於帶有prev和next指針的結點類Link來說,通過在鏈表中增加head、tail或者curr成員變量,很容易寫出一個雙向鏈表的實現,支持上述抽象類List中的操作。但是,也可以在Link類中只使用一個指針pt,而實現雙向鏈表。主要思想是從這一個指針中,既可以獲取prev的內容,也可以獲取next的內容。其實現依賴於異或操作的一個重要性質:

(L^R)^R = L, (L^R)^L = R。

可以定義指針pt的值爲

pt = prev ^ next。

也就是說,pt是其後向指針next和前向指針prev的異或。那麼,當前結點的pt和指向其之前結點的指針在做異或操作,就可以得到指向當前結點之後的結點的指針。當前結點的pt和指向其之後結點的指針做異或操作,就可以得到指向當前結點之前的結點的指針。換言之,在這種形式的雙鏈表中,除了保存head,tail和curr指針指向鏈表中的不同結點之外,還需要使用兩個指針prevp和nextp,分別指向curr結點之前和之後的結點。從當前結點向後移動一步時,可以通過如下語句序列實現。

prevp = curr;
curr = nextp;
nextp = prevp ^ curr->pt;
從當前結點向前一步所對應的語句序列如下。

nextp = curr;
curr = prevp;
prevp = curr->pt  ^ nextp;

當然,鏈表中多了兩個變量prevp和nextp,但是每個結點都節省了一個指針,所以總體而言,這種雙鏈表的實現能夠大幅度節省空間。當然這是以搜索時計算步驟增多、時間變長爲代價的。

雙向鏈表的具體實現代碼如下。

template <typename E> class LList: public List<E> {
private:
  Link<E>* head, *tail, *curr;
  Link<E> *prevp, *nextp; 
  int cnt;

  void init() {
    curr = head = new Link<E>;
    head->pt = tail = new Link<E>(head, NULL);
    prevp = NULL;
    nextp = tail;
    cnt = 0;
  }

  void removeall() {
    prevp = NULL;
    while(head != NULL) {
      curr = head;
      head = xor_op(prevp, head->pt);
      prevp = curr;
      delete curr;
    }
  }

  Link<E>* xor_op(Link<E>* pt1, Link<E>* pt2)
  {
    unsigned long op1 = (unsigned long)pt1;
    unsigned long op2 = (unsigned long)pt2;

    return (Link<E>*)(op1 xor op2);
  }
public:
  LList(int size=defaultSize) { init(); }
  ~LList() { removeall(); } 
  void clear() { removeall(); init(); } 

  void moveToStart() // Place curr at list start
  {
    curr = head;
    prevp = NULL;
    nextp = head->pt;
  }

  void moveToEnd()   // Place curr at list end
  {
    curr = tail->pt;
    nextp = tail;
    prevp = xor_op(tail, curr->pt);
  }

  
  void next()
  {
    if (curr != tail->pt)
    {
      prevp = curr;
      curr = nextp;
      nextp = xor_op(prevp, curr->pt);
    }
  }

  int length() const  { return cnt; }

  // Return the position of the current element
  int currPos() {
    Link<E>* temp = head;
    int i;

    prevp = NULL;
    nextp = head->pt;

    for (i=0; curr != temp; i++){
      prevp = temp;
      temp = nextp;
      nextp = xor_op(prevp, temp->pt);
    }
    return i;
  }

  // Move down list to "pos" position
  void moveToPos(int pos) {
    Assert ((pos>=0)&&(pos<=cnt), "Position out of range");

    curr = head;
    prevp = NULL;
    nextp = head->pt;
    for(int i=0; i<pos; i++)
    {
      prevp = curr;
      curr = nextp;
      nextp = xor_op(prevp, curr->pt);
    }
  }

  const E& getValue() const { // Return current element
    if(nextp == tail)
      return NULL;
    return nextp->element;
  }

  void insert(const E& it) {
    Link<E> *tmp;

    tmp = new Link<E>(it, curr, nextp);
    curr->pt = xor_op(xor_op(curr->pt, nextp), tmp);
    nextp->pt = xor_op(xor_op(nextp->pt, curr), tmp);
    nextp = tmp;

    cnt++;
  }

  void append(const E& it) {
    Link<E> *tmp;

    tmp = new Link<E>(it, tail->pt, tail);
    tail->pt->pt = xor_op(xor_op(tail->pt->pt, tail), tmp);
    tail->pt = tmp;
    if(nextp == tail)
      nextp = tmp;

    cnt++;
  }

  E remove() {
    if(nextp == tail)
      return NULL;

    Link<E> *new_next;
    E it = nextp->element;

    new_next = xor_op(nextp->pt, curr);
    curr->pt = xor_op(prevp, new_next);
    new_next->pt = xor_op(xor_op(nextp, new_next->pt), curr);
    delete nextp;
    nextp = new_next;
    cnt--;
    return it;
  }

  void prev()
  {
    if (curr != head)
    {
      nextp = curr;
      curr = prevp;
      prevp = xor_op(nextp, curr->pt);
    }
  }
};
 













發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章