資料結構學習(C++)——稀疏矩陣(十字連結串列【2】) (轉)

worldblog發表於2008-01-21
資料結構學習(C++)——稀疏矩陣(十字連結串列【2】) (轉)[@more@]

如果你細想想,就會發現,非零元節點如果沒有指示位置的域,那麼做加法和乘法時,為了確定節點的位置,每次都要遍歷行和列的連結串列。因此,為了運算,這個域是必須的。為了看出十字連結串列和單連結串列的差異,我從單連結串列派生出十字連結串列,這需要先定義一種新的結構,如下:

class MatNode:namespace prefix = o ns = "urn:schemas--com::office" />

{

public:

  int data;

  int row, col;

  union { Node *down; List *downrow; };

};

另外,由於這樣的十字連結串列是由多條單連結串列拼起來的,為了訪問每條單連結串列的保護成員,要宣告十字連結串列類為單連結串列類的友元。即在class List的宣告中新增friend class Matrix;

稀疏矩陣的定義和實現

#ifndef Matrix_H

#define Matrix_H

 

#include "List.h"

 

class MatNode

{

public:

  int data;

  int row, col;

  union { Node *down; List *downrow; };

  MatNode(int value = 0, Node *p = NULL, int i = 0, int j = 0)

  : data(value), down(p), row(i), col(j) {}

friend ostream & operator << (ostream & strm, MatNode &mtn)

  {

  st<< '(' << mtn.row << ',' << mtn.col << ')' << mtn.data;

  return strm;

  }

};

 

class Matrix : List

{

public:

  Matrix() : row(0), col(0), num(0) {}

  Matrix(int row, int col, int num) : row(row), col(col), num(num) {}

  ~Matrix() { MakeEmpty(); }

 

  void MakeEmpty()

  {

    List *q;

  while (first->data.downrow != NULL)

  {

  q = first->data.downrow;

    first->data.downrow = q->first->data.downrow;

    delete q;

  }

    List::MakeEmpty();

  row = col = num = 0;

  }

 

  void Input()

  {

  if (!row) { cout << "輸入矩陣行數:"; cin >> row; }

  if (!col) {  cout << "輸入矩陣列數:"; cin >> col; }

  if (!num) { cout << "輸入非零個數:"; cin >> num; }

  if (!row || !col || !num) return;

  cout << endl << "請按順序輸入各個非零元素,以列序為主,輸入0表示本列結束" << endl;

  int i, j, k, v;//i行數 j列數 k個非零元 v非零值

    Node *p = first, *t;

    List *q;

  for (j = 1; j <= col; j++) LastInsert(MatNode(0, NULL, 0, j));

  for (i = 1; i <= row; i++)

  {

  q = new List;

    q->first->data.row = i;

    p->data.downrow = q;

  p = q->first;

  }

  j = 1; q = first->data.downrow; First(); t = pNext();

  for (k = 0; k < num; k++)

  {

  if (j > col) break;

    cout << endl << "輸入第" << j << "列非零元素" << endl;

    cout << "行數:"; cin >> i;

  if (i < 1 || i > row) { j++; k--; q = first->data.downrow; t = pNext(); continue; }

    cout << "非零元素值"; cin >> v;

  if (!v)  { k--; continue; }

    MatNode matnode(v, NULL, i, j);

  p = new Node(matnode);

    t->data.down = p; t = p;

    while (q->first->data.row != i) q = q->first->data.downrow;

    q->LastInsert(t);

  }

  }

 

  void Print()

  {

    List *q = first->data.downrow;

  cout << endl;

  while (q != NULL)

  {

    cout << *q;

  q = q->first->data.downrow;

  }

  }

 

Matrix & Add(Matrix &matB)

{

  //初始化賦值輔助變數

  if (row != matB.row || col != matB.col || matB.num == 0) return *this;

  Node *pA, *pB;

  Node **pAT = new Node*[col + 1];

  Node **p= new Node*[matB.col + 1];

  List *qA = pGetFirst()->data.downrow, *qB = matB.pGetFirst()->data.downrow;

  First(); matB.First();

  for (int j = 1; j <= col; j++)

  {

  pAT[j] = pNext();

  pBT[j] = matB.pNext();

  }

 

  //開始

  for (int i = 1; i <= row; i++)

  {

    qA->First(); qB->First();

  pA = qA->pNext(); pB = qB->pNext();

  while (pA != NULL && pB !=NULL)

  {

  if (pA->data.col == pB->data.col)

  {

    pA->data.data += pB->data.data;

    pBT[pB->data.col]->data.down = pB->data.down; qB->Remove();

    if (!pA->data.data)

    {

      pAT[pA->data.col]->data.down = pA->data.down;

      qA->Remove();

    }

    else

    {

      pAT[pA->data.col] = pA;

      qA->pNext();

    }

  }

 

    else

  {

    if (pA->data.col > pB->data.col)

    {

      pBT[pB->data.col]->data.down = pB->data.down;

      qB->pRemove();

      pB->data.down = pAT[pB->data.col]->data.down;

      pAT[pB->data.col]->data.down = pB;

      pAT[pB->data.col] = pB;

      qA->InsertBefore(pB);

    }

 

    else if (pA->data.col < pB->data.col)

    {

      pAT[pA->data.col] = pA;

      qA->pNext();

    }

  }

  pA = qA->pGet();pB = qB->pGet();

  }

 

  if (pA == NULL && pB != NULL)

  {

    qA->pGetPrior()->link = pB;

    qB->pGetPrior()->link = NULL;

    while (pB != NULL)

  {

    pBT[pB->data.col]->data.down = pB->data.down;

    pB->data.down = pAT[pB->data.col]->data.down;

    pAT[pB->data.col]->data.down = pB;

    pAT[pB->data.col] = pB;

    pB = pB->link;

  }

  }

 

  if (pA !=NULL)

  {

    while (qA->pGet() != NULL)

  {

      pAT[pA->data.col] = pA;

    qA->pNext();

  }

  }

 

  qA = qA->first->data.downrow; qB = qB->first->data.downrow;

  }

   delete []pAT; delete []pBT;
return *this;

}

private:

  int row, col, num;

};

 

#endif

【說明】對於十字連結串列來說,只要記住對每個節點的操作,要同時考慮它的兩個指標域,那麼,各種演算法的理解都不是很難。比如說矩陣加法,“兩個矩陣相加和兩個一元多項式相加極為相似,所不同的是一元多項式只有一個變元(指數項),而矩陣中每個非零元有兩個變元(行值和列值),每個節點既在行表中又在列表中,致使插入和刪除節點時指標的修改稍為複雜,故需要更多的輔助指標。”(《資料結構(C語言版)》)其實private的row等可以放在首行的頭節點裡的,但為了清晰一點(本來就夠亂了),我把他們單立出來了。另外,很多地方考慮不是很周全,要是不按照註明的要求使用,很容易就會出錯。

【後記】按理說,十字連結串列應該不算是線性鏈式結構,按照原書的安排,放在連結串列這章不是很合適;《資料結構(C語言版)》將它和廣義表放在一章還是合理的。其實十字連結串列不是很難,就是很煩人;並且,如果不是數值運算,基本很少用到矩陣,就算是用到矩陣運算,在矩陣規模不大的時候,可以用二維陣列代替十字連結串列。從歷屆考研題來看,這部分幾乎沒有題,原因就是麻煩(你寫起來麻煩,他批起來也麻煩)、不常用、演算法固定沒新意。所以,你要是鬧心,這部分跳過去也可以。


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

相關文章