1. 程式人生 > >陣列出現的理由及一維陣列的應用11-5日

陣列出現的理由及一維陣列的應用11-5日

友情提示:由於上課期間發現了幾個程式碼錯誤,已訂正,謝謝大家。電腦近期出現了故障,裝的軟體全是亂碼,所以近期無法除錯程式,嘿嘿,正好你們可以找找錯誤哦。

## 1. 陣列出現的理由

問題1:如何求解從鍵盤輸入的n個數據的和?
大家都知道,針對這個問題,可以通過迴圈來實現。

#include<stdio.h>
int main()
{
	int n,sum=0,i,t;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&t);
		sum=sum+t;
	}
	printf("sum=%d\n",t);
	return 0;	
}
  • **分析:**用t來依次儲存這n個數據。新輸入的資料會將上一個資料覆蓋掉,最終t裡面儲存的是最後一次輸入的資料。前n-1個數據並未得到儲存。
  • 思考:如果遇到需要儲存所有原始資料的情況,例如有時候需要核對資料,計算的結果對不對呀;隨後可能還需要對這些資料進行其它操作呢。此刻,我們該怎麼辦呢?
    顯然很簡單,定義n個變數即可,讓n個數據分別儲存到這n個變數裡面。如果n很小,例如n=3,宣告3個int變數就可以了。如果n為10000呢,就需要宣告10000個int變數,其繁瑣程度可想而知。用什麼方法來處理這10000個變數呢?可採用陣列,它就是用於儲存和處理大量同類型資料的資料結構。
    - 什麼是陣列?
    陣列屬於自定義資料型別,用於
    儲存和處理
    大量同類型資料的資料結構。如何宣告一個數組呢?必須確定三個方面:確定陣列的名稱;確定陣列元素的型別;確定陣列的結構(維數及每一維的大小)。
    int a[3];
    定義一個數組a,裡面有3個元素,分別用a[0]、a[1]和a[2]來表示,元素型別均為int。
    int b[3]={1,2,3};
    定義一個數組b,並初始化3個元素的值:b[0]=1, b[1]=2, b[2]=3,所有元素均為int。
    問題:如何求解從鍵盤輸入的10個數據相加的和(儲存這10個數據)?
#include<stdio.h>
int main()
{
	int a[10],sum=0,i;
	for(i=0;i<10;i++)
	{
		scanf("%d",&a[i]);
		sum=sum+a[i];
	}
	printf("sum=%d\n",sum);
	return 0;	
}

其實,細心的我們可以發現,採用陣列的方法並沒有節省記憶體空間,也沒有節省變數。如果我們不採用陣列,就需要定義10個變數來儲存10個數據,採用陣列的話,是將這10個數據存放在陣列a中,用a[i]來表示陣列中的各個元素。
由此可知:與定義10個變數來儲存10個數據的方法相比,採用陣列只是在程式的描述上更加方便而已。

  • 一維陣列的常見操作
    C語言常常要對陣列進行遍歷(訪問各個元素)、尋找最值(尋找最大或最小值)、排序等操作,靈活地使用陣列對實際開發很重要。

3.1 陣列的遍歷

陣列的遍歷指的是:依次訪問陣列中的各個元素。
程式設計例項:定義一個數組a,將1,2,3,4,5這5個數據儲存在a中,並將它們輸出。
將a中各個元素輸出,所以說需要訪問各個元素,由於陣列元素的表示方法為:a[i]。所以可採用迴圈語句,並且用標號i遞增的方法。

#include<stdio.h>
int main( )
{
	int a[5]={1,2,3,4,5},i;
	for(i=0;i<5;i++)
		printf("%d\t",a[i]);
	printf("\n");
	return 0;
}

3.2 陣列的最值

程式設計例項:從1,5,3,8,6,12,90,0,13,5中找出最大值和最小值,並輸出。
分析:定義陣列來儲存這10個元素,先假定陣列中下標為0的元素是最大值;然後將最大值與陣列中下標為1的元素進行相比,如果後者大於最大值,則用後者替代最大值;依次類推,一直到將下標為9的元素與最大值進行比較後為止。
同理,先假定陣列中下標為0的元素是最小值;然後將最小值與陣列中下標為1的元素進行相比,如果後者小於最小值,則用後者替代最小值;依次類推,一直到將下標為9的元素與最小值進行比較後為止。

#include<stdio.h>
int main()
{
	int a[10]={1,5,3,8,6,12,90,0,13,5},i;
	int max,min;
	max=min=a[0];
	for(i=1;i<10;i++)
	{
		if(a[i]>max)
			max=a[i];
		if(a[i]<min)
			min=a[i];
	}	
	printf("max:%d\nmin:%d\n",max,min);
	return 0;
}    

3.3陣列的排序

在運算元組時,經常需要對陣列中的元素進行排序,有三個比較常見的排序演算法:選擇排序、氣泡排序和插入排序。

3.3.1選擇排序

問題:輸入6個學生的成績,將其從小到大進行排序。
分析:可將6個數據存入一個數組中。採用選擇排序演算法來進行排序。具體步驟如下:
步驟1:在陣列中選擇出最小的元素,將其與下標為0的元素進行交換,即陣列的首元素存放的是最小的資料;
步驟2:由於下標為0的位置已是最小資料,所以需要從下標為1開始一直到下標為5的元素中找出最小的元素,將它與下標為1的元素進行交換,即整個陣列中次小的元素放在了下標為1的位置;
步驟3:由於下標為0和下標為1的位置已是最小及次小資料,所以需要從下標為2開始一直到下標為5的元素中找出最小的元素,將它與下標為2的元素進行交換,即整個陣列中第三小的元素放在了下標為2的位置;
依次類推。。。。
最後一個步驟:陣列中只剩兩個元素了,也即下標為4和下標為5兩個元素,比較二者大小,判斷是否交換。排序完成。
如何程式設計實現上述排序過程呢?
肯定要用到迴圈,第一層迴圈計數從0到4,來控制比較的趟數。6個元素一共需要比較5趟,所以迴圈計數從0到4,一共是5;第二層迴圈計數從i+1到5(5為最後一個元素的下標),來控制每趟需要進行比較的次數。
注意:在程式中,min不是儲存最小的資料,min是最小元素在陣列中的下標。

#include<stdio.h>
int main()
{
	double score[6];
	int i,j,min,temp;
	for(i=0;i<6;i++)
		scanf("%lf",&score[i]);
	for(i=0;i<5;i++)
		{
			min=i;
			for(j=i+1;j<6;j++)
				if(score[j]<score[min])
					min=j;
			if(min!=i)   
			{
				temp=score[i];
				score[i]=score[min];
				score[min]=temp;
			}
		}
	for(i=0;i<6;i++)
		printf("%lf\t",score[i]);
	return 0;
}

因為min是所要查詢範圍內的最小元素的下標,所以當min!=i時,說明score[i]不是所要查詢範圍內的最小元素,要將score[min]換到下標為i的位置。當min==i時,說明score[i]就是所要查詢範圍內的最小元素,無需交換。
思考:能不能把選擇排序操作放在一個子函式中實現,main函式向它傳遞資料,由子函式來完成排序,最終排序後的資料反饋給main函式。這樣的話,排序的子函式可以被很多人拷貝走直接來實現,多方便呀。

#include<stdio.h>
void SelSort(double s[],int n)
{
	int i,j,min,temp;
	for(i=0;i<n-1;i++)
		{
			min=i;
			for(j=i+1;j<n;j++)
				if(s[j]<s[min])
					min=j;
			if(min!=i)
			{
				temp=s[i];
				s[i]=s[min];
				s[min]=temp;
			}
		}
}
int main()
{
	double score[6];
	int i;
	for(i=0;i<6;i++)
		scanf("%lf",&score[i]);
	SelSort(score,6);
	for(i=0;i<6;i++)
		printf("%lf\t",score[i]);
	return 0;
}

子函式SelSort的形參:double s[],int n。s接收主函式傳遞過來的陣列,n接收陣列的大小。
重磅丟擲問題:s是子函式SelSort的一個形參,因而是它的區域性變數,在子函式中排序的是s中資料元素,那麼主函式中的score陣列中的元素會不會被排序???

3.3.2氣泡排序

同樣的問題:輸入6個學生的成績,將其從小到大進行排序。
氣泡排序名字的由來;在氣泡排序的過程中,不斷地比較陣列中相鄰的兩個元素,較小者向上浮,較大者往下沉,整個過程和水中氣泡上升的原理相似。
具體步驟如下:
步驟1:從下標為0的元素開始,將相鄰的兩個元素依次進行比較,直到最後兩個元素完成比較。在進行比較的過程中,如果前一個元素(下標小的)比後一個元素(下標大的)大,則進行位置的交換;這一趟下來,陣列中最後一個元素就是整個陣列中的最大值;
步驟2:除最後一個已知是最大值的元素(下標為5)外。從下標為0的元素開始,將相鄰的兩個元素依次進行比較,直到最後兩個元素(二者的下標分別是3和4)完成比較。在進行比較的過程中,如果前一個元素(下標小的)比後一個元素(下標大的)大,則進行位置的交換;這一趟下來,陣列中倒數第二個元素就是整個陣列中的次大值。
步驟3:除最後兩個已知是最大和次大值的元素(下標為5和4)外。從下標為0的元素開始,將相鄰的兩個元素依次進行比較,直到最後兩個元素(二者的下標分別是2和3)完成比較。在進行比較的過程中,如果前一個元素(下標小的)比後一個元素(下標大的)大,則進行位置的交換;這一趟下來,陣列中倒數第三個元素就是整個陣列中的第三大值。
依次類推。。。。
最後一個步驟:陣列中只剩兩個元素了,也即下標為0和下標為1兩個元素,比較二者大小,判斷是否交換。排序完成。
如何程式設計實現上述排序過程呢?
肯定要用到迴圈,第一層迴圈計數i從0到4,來控制比較的趟數。6個元素一共需要比較5趟,所以迴圈計數從0到4,一共是5;第二層迴圈計數j從0到4-i,來控制每趟需要進行比較的次數。舉個例子,我們可以發現,第0趟需要比較5次,第1趟需要比較4次。。。第4趟需要比較1次。
也就是說:當i=0時,j控制比較次數為5,需從0,1,2,3,4變換,因而j的值最大等於4-i;當i=1時,j控制比較次數為4,需從0,1,2,3變換,因而j的值最大也等於4-i;。。。
程式為:

#include<stdio.h>
int main()
{
	double score[6];
	int i,j,temp;
	for(i=0;i<6;i++)
		scanf("%lf",&score[i]);
	for(i=0;i<5;i++)
		for(j=0;j<=4-i;j++)
			if(score[j]<score[j+1])
			{
				temp=score[j];
				score[j]=score[j+1];
				score[j+1]=temp;
			}  
	for(i=0;i<6;i++)
		printf("%lf\t",score[i]);
	return 0;
}

思考:能不能把氣泡排序操作放在一個子函式中實現,main函式向它傳遞資料,由子函式來完成排序,最終排序後的資料反饋給main函式。

#include<stdio.h>
void BubbleSort(double s[],int n)
{
	int i,j,temp;
	for(i=0;i<n-1;i++)
		for(j=0;j<=n-1-1-i;j++) //4-i,j<n-1-i
			if(s[j]<s[j+1])
			{
				temp=s[j];
				s[j]=s[j+1];
				s[j+1]=temp;
			}  
}
int main()
{
	double score[6];
	int i;
	for(i=0;i<6;i++)
		scanf("%lf",&score[i]);
	BubbleSort(score,6);
	for(i=0;i<6;i++)
		printf("%lf\t",score[i]);
	return 0;
}

子函式BubbleSort的形參:double s[], int n。s接收主函式傳遞過來的陣列,n接收陣列的大小。
重磅丟擲問題:s是子函式BubbleSort的一個形參,因而是子函式的區域性變數,在子函式中排序的是s中資料元素,那麼主函式中的score陣列中的元素會不會被排序???

3.3.3插入排序

插入排序指的是:將一個待排序的元素插入到已經排序的元素中的適當位置,直到所有元素插入完畢。
同樣的問題:輸入6個學生的成績,將其從小到大進行排序。採用插入排序的具體步驟如下:
步驟1:將下標為0的元素視為已排序好的元素。
步驟2:將下標為1的元素與下標為0的元素進行比較,如果其小於下標為0的元素,則將下標為0的元素向後移動,存放在下標為1的位置上,原本在下標為1的元素則存放在下標為0的位置(如果下標為1的元素大於下標為0的元素,則不執行任何操作)。這樣下標為0和1的元素就是排好序的。
步驟3:將下標為2的元素與下標為1的元素進行比較,如果其小於下標為1的元素,則將下標為1的元素向後移動,存放在下標為2的位置上,繼續將下標為2的元素與下標為0的元素進行比較,如果其小於下標為0的元素,則將下標為0的元素向後移動,存放在下標為1的位置,原本在下標為2的元素則放在下標為0的位置。這樣前三個元素就是排好序的。注意:如果下標為2的元素大於下標為1的元素,則不進行移動,並且不再繼續將其與下標為0的元素進行比較。如果下標為2的元素大於下標為0的元素,則將下標為2的元素插入到下標為1的位置。
以此類推。。。
步驟n:將最後一個元素與前面的元素進行比較,找到它的位置,插入即可,排序完成。

#include<stdio.h>
int main()
{
	double score[6];
	int i;
	for(i=0;i<6;i++)
		scanf("%lf",&score[i]);
	for(i=1;i<6;i++)
	{
		temp=score[i];
		j=i;
		while(j>0&&score[j-1]>temp)
		{
			score[j]=score[j-1];
			j--;
		}
		score[j]=temp;
	}
	for(i=0;i<6;i++)
		printf("%lf\t",score[i]);
	return 0;
}

採用子函式的實現形式如下:

#include<stdio.h>
void InsertSort(double s[],int n)
{
	double temp;
	int i,j;
	for(i=1;i<n;i++)
	{
		temp=s[i];
		j=i;
		while(j>0&&s[j-1]>temp)
		{
			s[j]=s[j-1];
			j--;
		}
		s[j]=temp;
	}	
}
int main()
{
	double score[6];
	int i;
	for(i=0;i<6;i++)
		scanf("%lf",&score[i]);
	InsertSort(score,6);
	for(i=0;i<6;i++)
		printf("%lf\t",score[i]);
	return 0;
}

3.4二分查詢法

有時候我們還需要查詢陣列中的某一個元素。前面已經講過如何查詢陣列中的最大或最小值。

3.4.1 無序陣列的查詢

對於一個無序的陣列來說,需要從陣列的第0個元素開始一直到陣列的最後一個元素為止。

#include<stdio.h>
int main()
{
	int a[10],i,s;
    for(i=0;i<10;i++)
    	scanf("%d",&a[i]);
    printf("input the data that you needs select");
    scanf("%d",s);
	for(i=0;i<10;i++)
	{
		if(a[i]==s)
			printf("a[%d]=%d\n",i,a[i]);
	}	
	return 0;
}    

如果a[i]==s則找到了該元素,如果陣列中有多個相同的該元素,可繼續找;如果陣列中的元素均不相同,在找到該元素後,就採用break結束迴圈。

3.4.2 有序陣列的查詢

對於一個有序的陣列來說,如果還從陣列的開頭一一查詢的話,效率太低了。可採用二分查詢法。
二分查詢法,又稱為折半查詢法。具體步驟如下:
步驟1:找到陣列的中間位置,將要查詢的資料與中間位置上的資料進行比較,若相等,則查詢成功;否則從中間位置將陣列一分為二。
步驟2:如果要查詢的資料小於中間位置上的資料,則在陣列的前半部分進行查詢;否則在後半部分查詢;
步驟3:重複步驟2,直到找到所需的資料,即查詢成功;如果沒有找到所需的資料,說明該資料不存在。
問題:在有序陣列中尋找資料。

#include<stdio.h>
int main()
{
	int score[6]={11,22,4466,88,99};
	int lower,high,mid,m;
	lower=0;high=5;
	scanf("%d",m);
	while(lower<=high)
	{
		mid=(lower+high)/2;
		if(score[mid]==m)
		{
			printf("success!\n");
			break;
		}	
		else if(score[mid]<m)
				lower=mid+1;
			else
				high=mid-1;	
	}
	return 0;
}