資料結構學習(C++)——單連結串列(定義與實現) (轉)

gugu99發表於2007-12-05
資料結構學習(C++)——單連結串列(定義與實現) (轉)[@more@]

節點類

#ifndef Node_H:namespace prefix = o ns = "urn:schemas--com::office" />

#define Node_H

 

template class Node  //單鏈節點類

{

public:

  Type data;

  Node *link;

  Node() : data(Type()), link(NULL) {} 

  Node(const Type &item) : data(item), link(NULL) {}

Node(const Type &item, Node *p) : data(item), link(p) {}

};

 

#endif

【說明】因為資料結構裡用到這個結構的地方太多了,如果用原書那種宣告友元的做法,那宣告不知道要比這個類的本身長多少。不如開放成員,事實上,這種結構只是C中的struct,除了為了方便初始化一下,不需要任何的方法,原書那是畫蛇添足。下面可以看到,連結串列的public部分沒有返回Node或者Node*的,所以,別的類不可能用這個開放的介面對連結串列中的節點操作。

【重要修改】原書的預設建構函式是這樣的Node() : data(NULL), link(NULL) {} 。我原來也是照著寫的,結果當我做擴充時發現這樣是不對的。當Type為結構而不是簡單型別(int、……),不能簡單賦NULL值。這樣做使得定義的模板只能用於很少的簡單型別。顯然,這裡應該Type的預設建構函式。 這也要求,用在這裡的類一定要有預設建構函式。在下面可以看到構造連結串列時,使用了這個預設建構函式。當然,這裡是約定帶表頭節點的連結串列,不帶頭節點的情況請大家自己思考。

【閒話】請不要對int *p = new int(1);這種語法有什麼懷疑,實際上int也可以看成一種class。

單連結串列類

#ifndef List_H

#define List_H

 

#ifndef TURE

#define TURE 1

#endif

#ifndef FALSE

#define FALSE 0

#endif

typedef int BOOL;

 

#include "Node.h"

 

template class List  //單連結串列定義

{

//基本上無引數的成員函式操作的都是當前節點,即current指的節點

//認為表中“第1個節點”是第0個節點,請注意,即表長為1時,最後一個節點是第0個節點

public:

  List() { first = current = last = new Node; prior = NULL; }

  ~List() { MakeEmpty(); delete first; }

 

 

  void MakeEmpty()  //置空表

  { 

    Node *q;

  while (first->link != NULL)

  {

  q = first->link;

    first->link = q->link;

    delete q;

  }

    Initialize();

  }

 

  BOOL IsEmpty()

  {

  if (first->link == NULL)

  {

    Initialize();

    return TURE;

  }

  else return FALSE;

  }

 

  int Length() const   //計算帶表頭節點的單連結串列長度 

  {

    Node *p = first->link;

  int count = 0;

  while (p != NULL)

  {

  p = p->link;

    count++;

  }

  return count;

  }

 

  Type *Get()//返回當前節點的資料域的地址

  {

  if (current != NULL) return &current->data;

  else return NULL;

  }

 

  BOOL Put(Type const &value)//改變當前節點的data,使其為value

  {

  if (current != NULL)

  {

    current->data = value;

    return TURE;

  }

  else return FALSE;

  }

 

  Type *GetNext()//返回當前節點的下一個節點的資料域的地址,不改變current

  {

  if (current->link != NULL) return &current->link->data;

  else return NULL;

  }

 

  Type *Next()//移動current到下一個節點,返回節點資料域的地址

  {

  if (current != NULL && current->link != NULL)

  {

    prior = current;

    current = current->link;

    return &current->data;

  }

  else

  {

    return NULL;

  }

  }

 

  void Insert(const Type &value)//在當前節點的後面插入節點,不改變current

  {

    Node *p = new Node(value, current->link);

    current->link = p;

  }

 

  BOOL InsertBefore(const Type &value)//在當前節點的前面插入一節點,不改變current,改變prior

  {

    Node *p = new Node(value);

  if (prior != NULL)

  {

    p->link = current;

    prior->link = p;

    prior = p;

    return TURE;

  }

  else return FALSE;

  } 

 

  BOOL Locate(int i)//移動current到第i個節點

  {

  if (i <= -1) return FALSE;

    current = first->link;

  for (int j = 0; current != NULL && j < i; j++, current = current->link)

    prior = current;

  if (current != NULL) return TURE;

  else return FALSE;

  }

 

  void First()//移動current到表頭

  {

    current = first;

  prior = NULL;

  }

 

  void End()//移動current到表尾

  {

  if (last->link != NULL)

  {

    for ( ;current->link != NULL; current = current->link)

    prior = current;

    last = current;

  }

    current = last; 

  }

 

 

  BOOL Find(const Type &value)//移動current到資料等於value的節點

  {

  if (IsEmpty()) return FALSE;

  for (current = first->link, prior = first; current != NULL && current->data != value;

    current = current->link)

    prior = current;

  if (current != NULL) return TURE;

  else return FALSE;

  }

 

  BOOL Remove()//刪除當前節點,current指向下一個節點,如果current在表尾,後current = NULL

  {

  if (current != NULL && prior != NULL)

  {

    Node *p = current;

    prior->link = p->link;

    current = p->link;

    delete p;

    return TURE;

  }

  else return FALSE;

  }

 

  BOOL RemoveAfter()//刪除當前節點的下一個節點,不改變current

  {

  if (current->link != NULL && current != NULL)

  {

    Node *p = current->link;

    current->link = p->link;

    delete p;

    return TURE;   

  }

  else return FALSE;

  }

 

friend ostream & operator << (ostream & strm, List &l)

{

    l.First();

  while (l.current->link != NULL) st<< *l.Next() << " " ;

  strm << endl;

    l.First();

  return strm;

}

 

protected:

 /*主要是為了高效的入隊演算法所新增的。因為Insert(),Remove(),RemoveAfter()有可能改變last但沒有改變last所以這個演算法如果在public裡除非不使用這些,否則不正確。但是last除了在佇列中非常有用外,其他的時候很少用到,沒有必要為了這個用途而降低Insert(),Remove()的所以把這部分放到protected,實際上主要是為了給佇列繼承*/  void LastInsert(const Type &value)

  {

    Node *p = new Node(value, last->link);

    last->link = p;

  last = p;

  }

 

  void Initialize()//當表為空表時使指標復位

  {

    current = last = first;

  prior = NULL;

  }

 

  //這部分函式返回型別為Node指標,是擴充套件List功能的介面

  Node *pGet()

  {

  return current;

  }

 

  Node *pNext()

  {

  prior = current;

    current = current->link;

  return current;

  }

 

  Node *pGetNext()

  {

  return current->link;

  }

 

  Node *pGetFirst()

  {

  return first;

  }

 

  Node *pGetLast()

  {

  return last;

  }

 

  Node *pGetPrior()

  {

  return prior;

  }

 

  void PutLast(Node *p)

  {

  last = p;

  }

  //這部分插入刪除函式不建立或刪除節點,是原位操作的介面

  void Insert(Node *p)

  {

    p->link = current->link;

    current->link = p;

  }

 

  void InsertBefore(Node *p)

  {

    p->link = current;

    prior->link = p;

  prior = p;

  } 

 

  void LastInsert(Node *p)

  {

    p->link = NULL;

    last->link = p;

  last = p;

  }

 

  Node *pRemove()

  { 

  if (current != NULL && prior != NULL)

  {

    Node *p = current;

    prior->link = current->link;

    current = current->link;

    return p;

  }

  else return NULL;

  }

 

  Node *pRemoveAfter()

  {

  if (current->link != NULL && current != NULL)

  {

    Node *p = current->link;

    current->link = current->link->link;

    return p;  

  }

  else return NULL;

  }

 

 

 

private:

  List(const List &l);

  Node *first, *current, *prior, *last;//儘量不要使用last,如果非要使用先用End()使指標last正確

 

};

 

#endif

【說明】我將原書的遊標類Iterator的功能放在了連結串列類中,遮蔽掉了返回值為Node以及Node*型別的介面,這樣的連結串列簡單、實用,擴充也很好。

【後記】在完成書後作業的時候,我發現了原書做法的好處,也就是我的做法的不足。如果使用原書的定義,在完成一個功能時,只需要寫出對應的函式實現。而在我的定義中,必須先派生一個類,然後把這個功能作為成員或者友元。但是這種比較並不說明書上的定義比我的要合理。首先,使用到原位操作的情況並不多,書後作業只是一種特殊情況;換句話說,書上的定義只是對完成書後作業更實用些。其次,在使用到連結串列的時候,通常只會用到插入、刪除、取資料、搜尋等很少的幾個功能,我的定義足夠用了。而在完成一個時,對連結串列的擴充功能在設計階段就很清晰了,這時可以派生一個新類在整個軟體中使用,對整體的規劃更為有利。而對於單個連結串列的操作,把它作為成員函式更好理解一些。也就是說我的定義靈活性不差。

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-988552/,如需轉載,請註明出處,否則將追究法律責任。

相關文章