稀疏矩陣三元組表快速轉置(C語言實現)


本來准備昨天下午寫的,但是因為去參加360眾測靶場的考核耽擱了,靶場的題目還是挺基礎的。

 

繼續學習吧。

使用黑色墨水在白紙上簽名就像由像素點構成的稀疏矩陣。如圖4所示。

 

 

4 手寫體簽名

【問題】請將以下稀疏點陣信息用三元組表進行存儲,並:

 

*

*

*

*

*

*

*

 

 

*

 

 

 

 

 

 

 

*

 

*

 

 

 

 

 

 

 

*

 

 

 

 

 

 

 

 

*

 

 

 

 

 

 

 

 

*

 

 

 

 

 

 

 

 

*

 

 

 

 

 

 

 

 

*

 

 

 

 

 

 

 

 

*

 

 

 

 

 

 

 

 

*

 

 

 

 

 

 

 

 

*

 

 

 

 

 

 

 

 

*

*

*

*

*

*

*

*

*

*

(1)用稀疏矩陣快速轉置法對該矩陣進行轉置。轉置前后的三元組表均以行序為主序。

(2) 以陣列形式輸出轉置前后的稀疏矩陣,如圖5所示。

                                        

           5  (a)轉置前                   (b)轉置后 

 

先普及一下稀疏矩陣的概念:

 

 

 簡單理解稀疏矩陣就是元素大部分為零的矩陣,在實際生活中我們遇到的大型稀疏矩陣,如果按照常規的儲存方法,就會造成大量空間的浪費,而且在訪問和操作的時候也會造成大量時間上的浪費。三元組表就是為了解決這一問題而產生的解決方案之一。

稀疏矩陣由於其自身的稀疏特性,通過壓縮可以大大節省稀疏矩陣的內存代價。具體操作是:將非零元素所在的行、列以及它的值構成一個三元組(i,j,v),然后再按某種規律存儲這些三元組,這種方法可以節約存儲空間,而這些三元組的集合,就是三元組表。

 

我們一步步來,將問題分解為一個個小模塊,先將稀疏矩陣存儲在三元組表中

因為C語言中沒有三元組這種數據類型,所以我們先使用typedef定義三元組表:

typedef struct{
	int i,j,val;
}NODE;

  i,j,val分別表示三元組表的行,列以及非零元素的值。

這里的需要儲存的稀疏矩陣也一起定義了

int nums[11][10]={
	{0,1,1,1,1,1,1,1,0,0},
	{1,0,0,0,0,0,0,0,1,0},
	{1,0,0,0,0,0,0,0,1,0},
	{0,0,0,0,0,0,0,1,0,0},
	{0,0,0,0,0,0,1,0,0,0},
	{0,0,0,0,0,1,0,0,0,0},
	{0,0,0,0,1,0,0,0,0,0},
	{0,0,0,1,0,0,0,0,0,0},
	{0,0,1,0,0,0,0,0,0,0},
	{0,1,0,0,0,0,0,0,0,0},
	{1,1,1,1,1,1,1,1,1,1}
	};

  這里有一個問題是:

    將稀疏矩陣存儲到三元組表中時,需要記錄稀疏矩陣的行列值嗎?

 

答案是肯定的,如果不存儲稀疏矩陣的行列值,當遇到稀疏矩陣最后一行全部是0的情況,由稀疏矩陣得到的三元組表,是無法還原成原來的稀疏矩陣的。

在上面的稀疏矩陣中,一共有28個非零元素,行值為11,列值為10,所以我們需要申請29個三元組的儲存空間,多余的那一個儲存空間用來存儲稀疏矩陣的行,列值,以及稀疏矩陣中非零元素的個數

#define T 28
NODE matrix[T+1];
matrix[0].i=11;
matrix[0].j=10;
matrix[0].val=0;

  用三元組表中第0個元素存儲稀疏矩陣的基礎信息

然后就到了將稀疏矩陣nums存儲進三元組表的操作,遍歷稀疏矩陣,當有元素為1的時候,將三元組表非零元素數量matrix[0].val++;然后依次將稀疏矩陣非零元素的信息存儲進三元組表中

void init_matrix(NODE* matrix){
	int i,j;
	for(i=0;i<matrix[0].i;i++){
		for(j=0;j<matrix[0].j;j++){
			if(nums[i][j]==1){
				matrix[0].val++;
				matrix[matrix[0].val].i=i;
				matrix[matrix[0].val].j=j;
				matrix[matrix[0].val].val=nums[i][j];
			}
		}
	}
}

  

存儲了之后我們將三元組表輸出還原成稀疏矩陣,看是否能正確還原

void put_matrix(NODE* matrix){
	int i,j;
	int count=1;
	for(i=0;i<matrix[0].i;i++){
		for(j=0;j<matrix[0].j;j++){
			if(i==matrix[count].i&&j==matrix[count].j){
				printf("%d",matrix[count].val);
				count++;
			}else{
				printf("0");
			}
		}
		printf("\n");
	}
}

  程序運行的結果是

 

 可以看到顯示與原稀疏矩陣相同,說明存儲入三元組表是正確的

接下來我們進行三元組表的快速轉置,先貼一張上課時候的PPT

 

 

當然如果直接看PPT的話很有可能還是一頭霧水,所以舉一個形象的例子:

一個年級有四個班的同學,一班有20人,二班有30人,三班有40人,四班有50人,全年級的同學一起到電影院看電影,進了電影院之后,告訴我們要按照班級的順序坐座位,座位只有一排,大家進電影院的時候都是跟自己熟悉的朋友一起,自然是亂序的,那我們應該怎么才能快速依次按照班級的順序坐座位呢?

有一個辦法是,大家隨便坐座位,到了位置上之后,再進行班級順序的比較,讓一班的同學坐一班的位置,二班的同學坐二班的位置等等等,但是這樣兩兩進行比較,效率未免過於低下。

另外的一個辦法是,坐座位的時候,先把人群里面的一班同學篩選出來,依次放在一班同學的位置,然后再將人群里面的二班同學篩選出來,放在一班同學的后面位置,然后。。。

思考了之后,好像沒有能夠只篩選(遍歷)一次人群就坐好座位的辦法。

當然是!有的

所以就引入快速轉置三元組表的辦法,即如果我們提前知道每個班有多少人,在遍歷人群的時候,只需要將其放在每個班開始的位置就行了。

如人群的開始班級排列是這樣的 2,3,1,4,2,2,3.。。。。第一個同學是2班的,我們將其安排在第21號座位上(假設座位號是從1開始),因為我們知道前面會有20個一班的同學要坐座位,接着二號同學是三班的,安排在51號座位,三號同學是1班的,安排在1號座位,四號同學是四班的,安排在91號座位,五號同學!又是二班的,但是原來二班的同學已經坐了21號,所以我們理所當然地坐在22號座位,后面的也是這樣,只需要遍歷一次原人群,就能將同學們按照順序坐在相應的位置上了。

但是在上面的描述中,我們需要知道每個班有多少人,以及每個班的第一個位置是在哪里(比如最開始二班同學的第一個位置是21號座位,有同學坐了21號座位之后,第一個位置自然就后移成22號座位了,等待下一個同學來坐,坐了之后再繼續后移),所以需要兩個輔助數組來存儲這兩個信息

num數組用來存儲每個班中的人數,cpot數組用來存儲每個班的第一個位置。

在三元組表快速轉置中,num[i]表示原三元組表中第i列中非零元的個數,cpot[i]表示原三元組表中第i列中第一個非零元素的在新的三元組表中的位置(cpot這一段可能有點繞,再解釋一下,因為在題目中,我們需要轉置之后的三元組表按照行主序排列,由於是轉置之后的,說明我們的順序在轉置之前,是按照列序在新的三元組表中放置,即如果新的三元組表中有(2,1),(1,2),那么(1,2)會在(2,1)的下面,因為2>1,找到其在三元組表中對應的位置后,再進行行列轉置)

接下來將新三元組表中第0元素設置好

new_matrix[0].i=matrix[0].j;
new_matrix[0].j=matrix[0].i;
new_matrix[0].val=matrix[0].val;

  然后初始化num數組和cpot數組,這里需要提到的是兩個PPT里面也顯示了的關系,再貼一次:

 

 cpot[i]表示原三元組表中第i列中第一個非零元素在新的三元組表中的位置,可以理解為同學們去看電影的時候,每個班在電影院座位上的最開始那個座位,比如二班的同學最開始是21,三班的同學是51,但是有一個顯而易見的事實是(別告訴我你沒看出來:-)當然沒看出來也沒事),一班的同學在座位中最開始的座位是1,這個是不需要遍歷人群,也不需要由班級人數可以確定的,而其他每個班開始的位置,是上一個班的開始位置+上一個班的人數,如21=1+20,1是一班開始的座位,20是1班的人數,三班的同學由二班的位置又可以得到51=21+30,依次類推初始化cpot

for(i=0;i<matrix[0].j;i++){
		num[i]=0;
	}
	for(i=1;i<=matrix[0].val;i++){
		num[matrix[i].j]++;
	}
	cpot[0]=1;
	for(i=1;i<matrix[0].j;i++){
		cpot[i]=cpot[i-1]+num[i-1];
	}

  最后就是最關鍵的轉置的時候了,當然經過前面這么多的鋪墊,最后這一步已經顯得很簡單了,和大家去看電影這個例子一樣,按照步驟放位置就行了。

使用tmp變量存儲該元素屬於哪一列(哪一個班級),first變量存儲其列在新的三元組表中存儲的位置(該班級同學坐的第一個位置),然后將原三元組表中的數據轉置之后放進去就行了,最后讓這個列在新的三元組表中存儲位置后移一位(后來的該班同學只好坐在這個同學后面啦),代碼已經不是很重要了

for(i=1;i<=matrix[0].val;i++){
		tmp=matrix[i].j;
		first=cpot[tmp];
		new_matrix[first].i=matrix[i].j;
		new_matrix[first].j=matrix[i].i;
		new_matrix[first].val=matrix[i].val;	
		cpot[tmp]++;
	}

  分析完了之后是不是感覺也沒那么難了呢,其實關鍵的代碼只有那么幾處,慢慢分析是可以縷清思路的。

 轉置后輸出結果為:

 

 

完整代碼如下:

#include<stdio.h>
#include<stdlib.h>
#define MAX 111

int nums[11][10]={
	{0,1,1,1,1,1,1,1,0,0},
	{1,0,0,0,0,0,0,0,1,0},
	{1,0,0,0,0,0,0,0,1,0},
	{0,0,0,0,0,0,0,1,0,0},
	{0,0,0,0,0,0,1,0,0,0},
	{0,0,0,0,0,1,0,0,0,0},
	{0,0,0,0,1,0,0,0,0,0},
	{0,0,0,1,0,0,0,0,0,0},
	{0,0,1,0,0,0,0,0,0,0},
	{0,1,0,0,0,0,0,0,0,0},
	{1,1,1,1,1,1,1,1,1,1}
	};
typedef struct{
	int i,j,val;
}NODE;
#define T 28

void put_matrix(NODE* matrix){
	int i,j;
	int count=1;
	for(i=0;i<matrix[0].i;i++){
		for(j=0;j<matrix[0].j;j++){
			if(i==matrix[count].i&&j==matrix[count].j){
				printf("%d",matrix[count].val);
				count++;
			}else{
				printf("0");
			}
		}
		printf("\n");
	}
}
void init_matrix(NODE* matrix){
	int i,j;
	for(i=0;i<matrix[0].i;i++){
		for(j=0;j<matrix[0].j;j++){
			if(nums[i][j]==1){
				matrix[0].val++;
				matrix[matrix[0].val].i=i;
				matrix[matrix[0].val].j=j;
				matrix[matrix[0].val].val=nums[i][j];
			}
		}
	}
}
void put_nums(){
	int i,j;
	for(i=0;i<11;i++){
		for(j=0;j<10;j++){
			printf("%d",nums[i][j]);
		}
		printf("\n");
	}
}

void reverse_matrix(NODE* matrix,NODE* new_matrix){
	int num[matrix[0].j],cpot[matrix[0].j];
	int tmp,first,i;
	new_matrix[0].i=matrix[0].j;
	new_matrix[0].j=matrix[0].i;
	new_matrix[0].val=matrix[0].val;
	for(i=0;i<matrix[0].j;i++){
		num[i]=0;
	}
	for(i=1;i<=matrix[0].val;i++){
		num[matrix[i].j]++;
	}
	cpot[0]=1;
	for(i=1;i<matrix[0].j;i++){
		cpot[i]=cpot[i-1]+num[i-1];
	}
	//轉置存儲 
	for(i=1;i<=matrix[0].val;i++){
		tmp=matrix[i].j;
		first=cpot[tmp];
		new_matrix[first].i=matrix[i].j;
		new_matrix[first].j=matrix[i].i;
		new_matrix[first].val=matrix[i].val;	
		cpot[tmp]++;
	}	
}

int main(){
	int i,j;
	NODE matrix[T+1],new_matrix[T+1];
	matrix[0].i=11;
	matrix[0].j=10;
	matrix[0].val=0;
	printf("儲存入三元組表前:\n");
	put_nums();	
	printf("初始化三元組表...");
	init_matrix(matrix);
	printf("\n使用三元組表按行主序進行儲存后:\n");
	put_matrix(matrix);
	printf("正在轉置...");
	reverse_matrix(matrix,new_matrix);
	printf("\n轉置后的結果為:\n");
	put_matrix(new_matrix);
	return 0;
}	

  

以上

 

 

matrix[


免責聲明!

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



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