三元組順序表和廣義表


1 稀疏矩陣:

  假設在m*n的矩陣中,有t個元素不為0.令q=t/(m*n),稱q為矩陣的稀疏因子。通常認為q<=0.05的時候就認為稀疏矩陣。

2 三元組順序表

如果對每一個元素都分配存儲空間的話,矩陣含有大量的0則會造成資源浪費。所以一般我們采用壓縮存儲的方式,除了存儲非0元素的值外,還要存儲相應的行和列。因此,稀疏矩陣可以表示成為非0元的三元組及行列數唯一確定 。

  相關定義如下:

typedef int Status
#define MAX_ROW 100
#define MAX_SIZE 101
typedef int elemtype;
typedef struct
{
    int row;   //設定行索引值
    int col;    //設定列索引值
    elemtype value;    //設定元素值
}Triple;
typedef struct
{
    int rn;   //設定行數
    int cn;   //設定列數
    int tn;
    Triple data[MAX_SIZE];  // 設定 數組
}TMatrix;

  轉置的操作(按照一定順序進行的轉置):

  這里我們注意,存儲方式是按行存儲的。

  時間復雜度為rn*tn. 當tn遠遠小於rn*cn的時候使用,否則時間復雜度相比傳統方法大的多。(犧牲時間來優化存儲)

//轉置操作
void TransposeSMatrix(TMatrix M, TMatrix T)
{
    T.rn = M.cn; T.cn = M.rn; T.tn = M.tn;
    if (T.tn) {
        q = 1;
        for (col = 1; col < M.rn; ++col)
            for (p = 1; p < M.tn; ++p)
                if (M.data[p].cn == col) {
                    T.data[q].rn = M.data[p].cn; T.data[q].cn = M.data[p].rn;
                    T.data[q].value = M.data[p].value;
                }
    }
}

  快速轉置:

//快速轉置
void FastTranspodeSMtrix(TMatrix M, TMatrix T)
{
    T.rn = M.cn; T.cn = M.rn; T.tn = M.tn;
    if (T.tn) {
        for (col = 1; col < M.rn; ++col) num[col] = 0;
        for (t = 1; t <= M.tnl++t) ++num[M.data[t].cn];
        cpot[1] = 1;
        //求第col列中的第一個非0元素在M.data中的序號
        for (col = 2; col <= M.tn; ++p) cpot[col] = cpot[col - 1] + num[col - 1];
        for (p=1; p <= M.tn; ++p) {
            col = M.data[p].cn; q = cpot[col];
            T.data[q].rn = M.data[p].cn; T.data[q].cn = M.data[p].rn;
            T.data[q].value = M.data[p].value; ++cpot[col];
        }
    }
}

  這個算法相比前一個多了兩個輔助向量。有兩個單次循環,所以時間復雜度為rn+cn.在M的非0元素個數和rn*cn相同的數量級的時候,時間復雜度與經典算法相同。

3 行邏輯連接的順序表:

   我們把指示“行”信息的輔佐數組cpot固定在稀疏矩陣的存儲結構當中。

下述為兩個矩陣相乘的算法:

typedef struct
{
    Triple data[MAX_SIZE];
    int rpos[MAX_ROW];
    int rn, cn, tn;
}RLSMatrix;


//三元組定義  默認行從1開始
void MultsMatrix(PLSMatrix a, RLSMatrix b, RLSMatrix c)
{
    elemtype ctemp[MAX_SIZE];
    int p, q, arow, ccol, brow, t;
    if (a.cn != b.rn) { cout << "Error" << endl; }
    else
    {
        c.rn = a.rn; c.cn = b.cn; c.tn = 0;
        if (a.tn * b.tn != 0)
        {
            for (arow = 1; arow <= a.rn; ++arow)  //處理a的每一行
            {
                ctemp[arow] = 0;   //當前行各元素累加器清0
                c.rpos[arow] = c.tn + 1; p = a.rpos[arow];
                for (; p < a.rpos[arrow + 1]; ++p)   //對當前行中的每一個非0元
                {
                    brow = a.data[p].col;      //找到對應在b中行號
                    if (brow < b.rn) t = b.rpos[brow + 1];
                    else t = b.tn + 1;
                    for (q = b.rpos[brow]; q < t; ++q)
                    {
                        ccol = b.data[q].col;
                        ctemp[ccol] += a.data[p].value * b.data[q].value;
                    }
                }
                for(ccol=1;ccol<=c.cn;++ccol)   //壓縮存儲在行的非0元
                    if (ctemp[ccol] != 0)
                    {
                        if (++c.tn > MAX_SIZE)
                        {
                            cout << "Error" << endl; exit(0);
                        }
                        else c.data[c.tn] = (arow, ccol, ctemp[ccol]);
                    }
            }
        }
    }
}

4 十字鏈表

  當矩陣的非0元個數和位置在操作過程中變化較大時,就不宜采用順序結構來表示三元組的線性表。例如,將矩陣B加到矩陣A,由於非0元的操作會引起數組中的變化。所以,采用鏈性表更加方便。

  除了行,列和值外,還有兩個指針,用以標識同行中的下一個非0元和同列中的下一個非0元。

 

typedef struct Clnode
{
    int i, j;
    elemtype e;
    struct Clnode* down, * right;
}OLNode;
//下面是整個稀疏矩陣的定義
typedef struct
{
    int mu; int nu; int tu;
    OLNode* rhead;
    OLNode* chead;
}CrossList;
void CreateSMatrix_OL(CressList& M)
{
    int m, n, t;
    //創建稀疏矩陣。采用十字鏈表表示
    if (M) free(M);
    cout << '請輸入矩陣的M的行數,列數 和非0元素個數' << endl;
    cin >> m >> n >> t;
    M.mu = m; M.nu = n; M.tu = t;      //數額uM的行數,列數和非0元素個數
    if (!(M.rhead = (OLNode*)malloc((m + 1) * sizeof(OLNode)))) cout << "輸入數據出現錯誤" << endl;
    if (!(M.chead = (OLNode*)malloc((n + 1) * sizeof(OLNode)))) cout << "輸入數據出現錯誤" << endl;
    M.rhead[] = M.chead[] = NULL;
    for (cin >> i >> j >> e; i != 0; cin >> i >> j >> e;) {
        if (!(p = (OLNode*)malloc(sizeof(OLNode))))cout << "數據分配出現錯誤" << endl;
        p->i = i; p->j = j; p->e = e;       //生成節點
        if (M.rhead[i] == NULL || M.rhead[i]->j > j) { p->right = M.rhead[i]; M.rhead[i] = p; }
        else {        //尋找在行表中插入的位置
            for (q = M.rhead[i]; (q->right) && q->right->j < j; q = q->right);
            p->right = q->right; q->right = p;
        }
        if (M.chead[j] == NULL || M.chead[j]->i > i) { p->down = M.chead[j]; M.chead[j] = p; }
        else {        //尋找在列表中插入的位置
            for (q = M.chead[i]; (q->down) && q->down->j < j; q = q->down);
            p->down = q->down; q->down = p;      //完成列的插入
        }
    }
}

5 廣義表

  廣義表是線性表的推廣。

  廣義表一般記作:

      LS = (a1, a2, a3,..., an)

  每個元素可以是單個元素,也可以是廣義表,分別稱為廣義表的原子和子表。

  當廣義表非空的時候,我們習慣稱第一個元素為表頭,稱其余的元素為表尾(a2, a3, a4,..., an)。

  值得提醒的是列表()和(())不同。前者為空表,長度為0;后者長度為1,可分解得到表頭,表尾均為空表()。

  這種靈活性就需要用到鏈式存儲結構。需要兩種結構的節點,一種是原子的節點,用以表示原子,由標志域,值域組成;另一種是列表,由標值域,標識表頭的指針域和值域組成。

 

1 求廣義表的深度(遞歸):

  廣義表的深度就是 各個子表深度的最大值加一,注意區分長度;

  我們用ATOM(Tag)為0表示原子,反之為子表。

typedef struct GLNode {
    ElemTag tag;   //公共部分,用於區分原子節點和表節點
    int atom;
    GLNode* hp;
    GLNode* tp;
}*GList;
//求解表深度
int GListDepth(GList L)
{
    int dep, max;
    GList pp;
    //采用頭尾鏈表存儲結構,求廣義表L的深度
    if (!L) return 1;    //空表深度為一
    if (L->tag == ATOM) retun 0;   //原子深度為0
    for (max = 0, pp = L; pp; pp = pp->tp)
    {
        dep = GListDepth(pp->hp);  //求以pp->hp為頭指針的子表深度
        if (dep > max)max = dep;
    }
    return max + 1;
}

2 遞歸復制廣義表

  分別遞歸的復制表頭和表尾:

//復制廣義表
void CopyGlist(GList& T, GList L)
{
    if (!L)T = NULL;   //復制空表
    else {
        if(!(T=(GList)malloc(sizeof(GLNode)))cout<<"數據分配出現錯誤"<<endl;   //建表節點
            T->tag=L->tag;
            if(L->tag==0)T->atom=L->atom;    //復制單原子
            else {
                CopyGlist(T->hp,L->hp);
                CopyGlist(T->tp,L->tp);
            }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM