本篇文章的練習(xí)題主要是對天勤19數(shù)組那章書后題的補充洋幻,因為書上沒有運行結(jié)果,自己重寫了一遍翅娶。書上的代碼有些部分可讀性比較差==沒買天勤那本書的看這篇就行了文留。為了更好的閱讀體驗?zāi)憧梢圆榭次业?a target="_blank">github原文
矩陣的壓縮存儲
矩陣
在求矩陣行數(shù)和列數(shù)時遇到一個坑,就是當(dāng)你把二維數(shù)組直接傳到函數(shù)中故觅,再從函數(shù)中求該二維數(shù)組的行數(shù)和列數(shù)會發(fā)生越界訪問厂庇,求的值可能不對,這是因為你傳遞的是數(shù)組首元素的地址输吏,應(yīng)當(dāng)在傳參之前計算好行數(shù)和列數(shù)再傳入函數(shù)中。
矩陣轉(zhuǎn)置
void display(int A[][maxSize],int c,int r){
for(int i=0;i<r;i++){
for(int j=0;j<c;j++){
cout<<setw(2)<<A[i][j];
}
cout<<endl;
}
}
// 矩陣轉(zhuǎn)置
void matrixReserve(int A[][maxSize],int c,int r){
int B[c][r]={};
cout<<"逆轉(zhuǎn)之前的數(shù)組"<<endl;
display(A,c,r);
cout<<"逆轉(zhuǎn)之后的數(shù)組"<<endl;
for(int i=0;i<r;i++){
for(int j=0;j<c;j++){
B[j][i]=A[i][j];
}
}
display(B,c,r);
}
int main(){
int A[][3]={1,2,3,4,5,6,7,8,9};
int c=sizeof(A[0])/sizeof(int);
int r=(sizeof(A)/sizeof(int))/c;
matrixReserve(A,c,r);
}
矩陣相加
C[i][j]=A[i][j]+B[i][j]
// 矩陣相加
void matrixAdd(int A[][2],int B[][2],int m,int n) {
int C[m][2]={};
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
C[i][j]=A[i][j]+B[i][j]; }
}
display(C,m,n);
}
int main(){
int A[2][2]={1,2,3,4};
int B[2][2]={5,6,7,8};
int c=sizeof(A[0])/sizeof(int);
int r=(sizeof(A)/sizeof(int))/c;
matrixAdd(A,B,c,r);
}
矩陣相乘
C[i][j]=A上第i行每個元素與B上第j列每個元素相乘并求和的結(jié)果
;兩矩陣可以相乘的必要條件是:a的列數(shù)必須等于b的行數(shù)替蛉。
// 矩陣相乘
void matrixMuti(int A[][2],int B[][2],int m,int n) {
int C[m][2]={};
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
C[i][j]=0;
/* 注意這里h的區(qū)間并不一定是m贯溅,應(yīng)為a的列數(shù)或b的行數(shù)拄氯,這里因為m和n相等所以為m */
for(int h=0;h<m;h++){
C[i][j]+=A[i][h]*B[h][j];
}
}
}
display(C,m,n);
}
int main(){
int A[2][2]={1,2,3,4};
int B[2][2]={5,6,7,8};
int c=sizeof(A[0])/sizeof(int);
int r=(sizeof(A)/sizeof(int))/c;
matrixMuti(A,B,c,r);
// 19 22
// 43 50
}
習(xí)題
1.數(shù)組的n個元素中有多個零元素,設(shè)計一個算法將數(shù)組中所有的非零元素依次移動到a數(shù)組的前端它浅。
算法是遍歷時用一個指針j記錄最后一次出現(xiàn)的非零元素下標(biāo)译柏,遇見0跳過,出現(xiàn)下一個非0元素時將j+1與該位置的0值交換姐霍,j+1鄙麦。
void moveZeroToBottom(int A[maxSize],int length){
int j=-1; // 記錄最后一個非零元素出現(xiàn)的位置
for(int i=0;i<length;i++){
if(A[i]==0){
continue;
}else{
j++;
int temp=A[i];
A[i]=A[j];
A[j]=temp;
}
}
displayArr(A,length);
}
int main(){
int A[maxSize]={0,2,1,3,0,0,3,5,7};
int length=sizeof(A)/sizeof(A[0]);
moveZeroToBottom(A,length); // 2 1 3 3 5 7 0 0 0 0
}
2.關(guān)于浮點型數(shù)組A[0,...,n-1]分別用遞歸實現(xiàn)求最大值以及數(shù)組中所有數(shù)的和、平均值镊折。
很基礎(chǔ)的算法胯府,不過多解釋了。就是在求平均值時偷懶了恨胚,應(yīng)該s是按照avg=((n-1)*avg+arr[i])/n
這樣遞歸骂因。。自己直接將上一步求的和除了總長度==赃泡。
float findMax(float arr[],float max,int i){
if(i==maxSize){
return max;
}
if(arr[i]>max){
max=arr[i];
}
return findMax(arr,max,i+1);
}
float getsum(float arr[],float sum,int i){
if(i==maxSize){
return sum;
}
return getsum(arr,sum+arr[i],i+1);
}
float getAverage(float arr[],float sum,int i){
if(i==maxSize){
return sum/maxSize;
}
return getAverage(arr,sum+arr[i],i+1);
}
int main(){
float A[maxSize]={0,2,1,3,8,0,3,5,7};
float max=findMax(A,0,0);
float sum=getsum(A,0,0);
float avg=getAverage(A,0,0);
cout<<max<<endl;; // 8
cout<<sum<<endl; // 29
cout<<avg<<endl; // 3.222222
}
3.試設(shè)計一個算法寒波,將數(shù)組中所有的奇數(shù)移到偶數(shù)之前,要求不另增加存儲空間升熊。寫空間復(fù)雜度為O(n)俄烁。
一頭一尾兩個指針,當(dāng)前面的指針i停在偶數(shù)時级野,后面的指針j停在奇數(shù)時猴娩,兩數(shù)值交換,繼續(xù)遍歷直至兩指針重合勺阐。
void bubbleOdd(int arr[]){
int i=0,j=maxSize-1;
while(i<j){
while(arr[i]%2==1&&i<j) i++;
while(arr[j]%2==2&&i<j) j--;
if(i<j){
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
i++;
j--;
}
}
display(arr);
}
int main(){
int A[maxSize]={0,2,1,3,8,0,3,5,7};
bubbleOdd(A); // 7 5 1 3 3 0 8 2 0
}
4.設(shè)有一元素為整數(shù)的線性表l,放在一維數(shù)組中卷中,設(shè)計一個算法,以A[n-1]為參考量渊抽,將該數(shù)組分為左右兩個部分蟆豫,其中左半部分的元素值均小于等于A[n-1]。右半部分的元素指針大于A[n-1]懒闷,A[n-1]位于這兩部分之間十减。要求結(jié)果仍存放在數(shù)組a中。
類似于快排愤估,不過是先將首尾元素互換之后帮辟,將首元素作為參考值。一頭一尾兩指針i和j玩焰,j先動先從后面找比參考值小的由驹,與i指向的元素交換;再從前面找比參考值大的昔园,放到后面蔓榄;直至量指針相遇并炮,將該位置設(shè)為參考值。
void quickSort(int arr[]){
int i=0;
int j=maxSize-1;
int temp=arr[j];
arr[j]=arr[i];
arr[i]=temp;
while(i!=j){
while(i<j&&arr[j]>temp) j--; //找到一個小于目標(biāo)值的數(shù)甥郑,放在前面
if(i<j){
arr[i]=arr[j];
i++;
}
while(i<j&&arr[i]<=temp) i++; //找到一個大于目標(biāo)值的數(shù)逃魄,放在前面
if(i<j){
arr[j]=arr[i];
j--;
}
}
arr[i]=temp;
}
5.設(shè)計一個算法對給定的一個整形矩陣a統(tǒng)計這個矩陣中具有下列特征的元素個數(shù),并輸出它們的坐標(biāo)以及數(shù)值他們技術(shù)所在行中的最小折又是所在列中的最小值澜搅,或者他們技術(shù)所在行中的最大制又是所在列中的最大值伍俘。
先求出一行中最小值,記錄當(dāng)前列勉躺,再看是否為該列中最小值癌瘾。
void getMin(int A[][3],int c,int r){
int min=A[0][0];
int minj=0;
int isFind=1;
for(int i=0;i<r;i++){
for(int j=0;j<c;j++){
if(A[i][j]<min){
min=A[i][j];
minj=j;
}
}
for(int k=0;k<r;k++){
if(min>A[k][minj]){
isFind=0;
break;
}
}
if(isFind==1){
cout<<"找到最小值"<<A[i][minj]<<",坐標(biāo)為"<<i<<","<<minj<<endl;
isFind=0;
}
}
}
// 輸出
找到最小值1,坐標(biāo)為0,0
5.怎樣介紹稀疏矩陣的三元組存儲結(jié)構(gòu)特點,并實現(xiàn)稀疏矩陣的基本操作赂蕴。
1.給定稀疏矩陣a柳弄。創(chuàng)建其三元組存儲結(jié)構(gòu)b。
三元組存儲結(jié)構(gòu)是一種順序結(jié)構(gòu)概说,因此也是一種順序表表中的每個節(jié)點對應(yīng)稀疏矩陣的一個非零元素碧注,其中包括三個字段,分別為該元素的值糖赔、行下標(biāo)和列下標(biāo)萍丐。另外,用第零行的第一個元素放典,存儲矩陣中非零元素的個數(shù)逝变,第零行的第二個元素存儲矩陣的行數(shù),第零行的第三個元素存儲矩陣的列數(shù)奋构。
void create(int A[][3],int c, int r,int B[][3]){
int k=1;
for(int i=0;i<r;i++){
for(int j=0;j<c;j++){
if(A[i][j]!=0){
B[k][0]=A[i][j];
B[k][1]=i;
B[k][2]=j;
k++;
}
}
}
B[0][0]=k-1;
B[0][1]=r;
B[0][2]=c;
display(B,k,3);
}
int main(){
int A[3][3]={1,2,3,4,5,6,7,8,9};
int B[maxSize][3];
create(A,3,3,B);
}
輸出
9 3 3
1 0 0
2 0 1
3 0 2
4 1 0
5 1 1
6 1 2
7 2 0
8 2 1
9 2 2
2.查找給定元素x是否在矩陣中壳影。
遍歷上面求到的三元組進行查找。
int search(int trimat[][3],int target,int r){
int isFind=0;
for(int i=1;i<r;i++){
if(B[i][0]==target){
cout<<"坐標(biāo)為"<<B[i][1]<<","<<B[i][2]<<endl;
isFind=1;
}
}
return isFind;
}
int main(){
int A[][3]={1,2,3,4,5,6,7,8,9};
int B[9][3];
create(A,3,3,B);
search(B,5,9); // 坐標(biāo)為1,1
}
6.假設(shè)稀疏矩陣a采用三元組表示弥臼。編寫一個函數(shù)宴咧,計算其轉(zhuǎn)置矩陣b要求B也采用三元組表示。
矩陣的轉(zhuǎn)置歸根結(jié)底就是A[m][n]=B[n][m]
這個公式径缅。但三元組不僅僅那么簡單掺栅,三元組內(nèi)的元素是原矩陣按照行優(yōu)先存儲的結(jié)果,所以下面的程序是錯的D芍怼Q跷浴!
void reserve(int B[][3],int C[][3],int r){
for(int i=1;i<=r;i++){
C[i][0]=B[i][0];
C[i][1]=B[i][2];
C[i][2]=B[i][1];
}
C[0][0]=B[0][0];
C[0][1]=B[0][2];
C[0][2]=B[0][1];
cout<<"轉(zhuǎn)置后"<<endl;
display(C,r,3);
}
輸出
// 原三元組
9 3 3
1 0 0
2 0 1
3 0 2
4 1 0
5 1 1
6 1 2
7 2 0
8 2 1
9 2 2
----逆置后為-----
9 3 3
1 0 0
2 1 0
3 2 0
4 0 1
5 1 1
6 2 1
7 0 2
8 1 2
9 2 2
正確的寫法應(yīng)該是氏堤,找到A中第i列的元素沙绝,依次放在B中的第i行中,并將元素中的坐標(biāo)對調(diào)。
void reserve(int B[][3],int C[][3],int r){
int k=1;
for(int i=0;i<3;i++){
for(int j=1;j<r;j++){
if(B[j][2]==i){
C[k][0]=B[j][0];
C[k][1]=B[j][2];
C[k][2]=B[j][1];
k++;
}
}
}
C[0][0]=B[0][0];
C[0][1]=B[0][2];
C[0][2]=B[0][1];
cout<<"轉(zhuǎn)置后"<<endl;
display(C,10,3);
}
結(jié)果
----逆置后為-----
9 3 3
1 0 0
4 0 1
7 0 2
2 1 0
5 1 1
8 1 2
3 2 0
6 2 1
9 2 2
7.假設(shè)稀疏矩陣宿饱。A和b都采用三元組表示熏瞄,編寫一個函數(shù)計算C=A+B,C也用三元組表示脚祟。
想矩陣相加規(guī)則為對應(yīng)位置上的元素相加谬以,對于三元組存儲結(jié)構(gòu)下的矩陣a和b,假如當(dāng)前需要將位置上的元素由桌。a[i][j]和b[i][j]相加需要考慮的情況为黎,即兩個元素的相同位置上是否非零。
(王道中算法邏輯比較繁瑣行您,個人建議先將三元組轉(zhuǎn)為矩陣再加铭乾,再轉(zhuǎn)為三元組==)
void add(int A[][3],int B[][3],int C[][3],int r,int c){
int k=1;
int i=1,j=1;
while(i<=A[0][0]&&j<=B[0][0]){
if(A[i][1]==B[j][1]){ // 拿同一行比 ,較小列的先放入c中
if(A[i][2]<B[j][2]){
C[k][0]=A[i][0];
C[k][1]=A[i][1];
C[k][2]=A[i][2];
++k;
++i;
}else if(A[i][2]>B[j][2]){
C[k][0]=B[j][0];
C[k][1]=B[j][1];
C[k][2]=B[j][2];
++k;
++j;
}else{// A娃循、B兩矩陣中位置相同 炕檩,則相加存入C中
int temp=A[i][0]+B[j][0];
if(temp!=0){// 注意要是非0數(shù)
C[k][0]=temp;
C[k][1]=A[j][1];
C[k][2]=A[j][2];
++k;
}
++i;
++j;
}
}else if(A[i][1]<B[j][1]){ // 行數(shù)小的先存入C中
C[k][0]=A[i][0];
C[k][1]=A[i][1];
C[k][2]=A[i][2];
++k;
++i;
}else{ // A的行數(shù)大于B的行數(shù)
C[k][0]=B[j][0];
C[k][1]=B[j][1];
C[k][2]=B[j][2];
++k;
++j;
}
}
// 如果 A、B中仍有剩余元素
while(i<=A[0][0]){
C[k][0]=A[i][0];
C[k][1]=A[i][1];
C[k][2]=A[i][2];
++k;
++i;
}
while(j<=B[0][0]){
C[k][0]=B[j][0];
C[k][1]=B[j][1];
C[k][2]=B[j][2];
++k;
++j;
}
C[0][0]=k-1;
C[0][1]=A[0][1];
C[0][2]=A[0][2];
cout<<"相加后的三元組"<<endl;
display(C,k,3);
}
兩個矩陣都是{1,2,3,4,5,6,7,8,9}
時相加的結(jié)果輸出如下:
第一個矩陣的三元組為
9 3 3
1 0 0
2 0 1
3 0 2
4 1 0
5 1 1
6 1 2
7 2 0
8 2 1
9 2 2
第二個矩陣的三元組為
9 3 3
1 0 0
2 0 1
3 0 2
4 1 0
5 1 1
6 1 2
7 2 0
8 2 1
9 2 2
相加后的三元組
9 3 3
2 0 0
4 0 1
6 0 2
8 1 0
10 1 1
12 1 2
14 2 0
16 2 1
18 2 2
8.假設(shè)稀疏矩陣a和b捌斧。采用三元組表示編寫一個函數(shù)笛质,計算C=A*B,要求c也是采用三元組表示的稀疏矩陣。
矩陣的乘法是一種常用的矩陣運算捞蚂。假設(shè)兩矩陣a與b相乘妇押,結(jié)果為c,c中的第i行和第j列上的元素為a中第i行的元素與b中第j列的元素對應(yīng)相乘姓迅,并且求和的結(jié)果敲霍。及兩向量的點乘。由上述介紹可知丁存,a和b量舉證可以相乘的條件是a的列數(shù)必須等于b的行數(shù)肩杈,看下列公式:
兩個向量a = [a1, a2,…, an]和b = [b1, b2,…, bn]的點積定義為:
a·b=a1b1+a2b2+……+anbn。
還使用剛才的兩個(1~9)的矩陣解寝,他們相乘的過程為
// 原矩陣
1 2 3 1 2 3
4 5 6 x 4 5 6
7 8 9 7 8 9
所求矩陣中0,0位置的數(shù)為:1*1+2*4+3*7=30
0,1位置的數(shù)扩然,為:1*2+2*5+3*8=36
。编丘。与学。博主懶,不一一算了嘉抓。索守。口算出兩個數(shù)用于驗證下面三元組算法的結(jié)果
放在三元組中抑片,這里我們需要定義getValueOfTrimat
函數(shù)卵佛,根據(jù)在矩陣中的下標(biāo)得出在三元組的值。一定要注意驗證sum
不能為0,因為三元組中不統(tǒng)計0元素截汪。
int getValueOfTrimat(int trimat[][3],int r,int c){
for(int i=1;i<=trimat[0][0];i++){
if(trimat[i][1]==r&&trimat[i][2]==c){
return trimat[i][0];
}
}
return 0;
}
void multi(int A[][3],int B[][3],int C[][3]){
if(A[0][1]!=B[0][2]){
cout<<"兩矩陣不能相乘"<<endl;
return;
}
int i,j,k=1;
for(i=0;i<A[0][1];i++){
for(j=0;j<B[0][2];j++){
int sum=0;
for(int t=0;t<A[0][1];t++){
sum+=getValueOfTrimat(A,i,t)*getValueOfTrimat(B,t,j);
}
if(sum!=0){
C[k][0]=sum;
C[k][1]=i;
C[k][2]=j;
k++;
}
}
}
C[0][0]=k-1;
C[0][1]=i;
C[0][2]=j;
display(C,k,3);
}
結(jié)果為:
第一個矩陣的三元組為
9 3 3
1 0 0
2 0 1
3 0 2
4 1 0
5 1 1
6 1 2
7 2 0
8 2 1
9 2 2
第二個矩陣的三元組為
9 3 3
1 0 0
2 0 1
3 0 2
4 1 0
5 1 1
6 1 2
7 2 0
8 2 1
9 2 2
兩矩陣相乘的結(jié)果為
9 3 3
30 0 0
36 0 1
42 0 2
66 1 0
81 1 1
96 1 2
102 2 0
126 2 1
150 2 2