数组和算法基础
目錄
一維數(shù)組的定義和初始化
數(shù)組的創(chuàng)建
?數(shù)組的初始化
數(shù)組的輸出
?數(shù)組的存放
數(shù)組名的調(diào)用
?數(shù)組的下標(biāo)越界問題
二維數(shù)組的定義和初始化
數(shù)組的創(chuàng)建
數(shù)組的初始化
數(shù)組的輸出?
?數(shù)組的存儲
向函數(shù)傳遞一維數(shù)組
數(shù)組名的含義
數(shù)組作為參數(shù)傳遞給函數(shù)
排序和查找
冒泡排序
交換法排序
選擇法排序?
特定子項(xiàng)的排序
查找
線性查找
折半查找?
向函數(shù)傳遞二維數(shù)組
一維數(shù)組的定義和初始化
數(shù)組:一組數(shù),是一組相同類型元素的集合
創(chuàng)建方式
type_t arr_name ?[const_n];
元素類型 ? ? ? ? 常量表達(dá)式,用來指定數(shù)組的大小
數(shù)組的創(chuàng)建
int main() {int arr[6];//創(chuàng)建整型數(shù)組char ch[9];//創(chuàng)建字符數(shù)組/*int n = 3;int arr1[n];*///錯誤的,不能將變量名作為下標(biāo)使用return 0; }?數(shù)組的初始化
int main() {int arr1[5] = { 1,2,3,4,5 };//完全初始化int arr2[5] = { 1,2,3 };//不完全初始化,自動補(bǔ)0int arr3[]={1, 2, 3, 4, 5};//根據(jù)初始化內(nèi)容確定大小,等價為int arr3[5] = { 1,2,3,4,5 };return 0; } int main() {char ch1[5] = { 'a','b','c' };//不完全初始化,自動補(bǔ)'\0'char ch2[] = { 'a','b','c' };//根據(jù)初始化內(nèi)容確定大小char ch3[5] = "abc"; //前四個元素為 a b c \0 (\0)char ch4[] = "abc";//僅四個元素 a b c \0return 0; }?當(dāng)數(shù)組在所有函數(shù)外定義,或用static定義為靜態(tài)儲存類型時,即使不給數(shù)組元素賦初值,那么數(shù)組元素也會自動初始化為0,這是在編譯階段完成的
int main() {char a[] = "abc";//四個元素char b[] = { 'a','b','c'};//三個元素printf("%s\n", a);printf("%s\n", b);//沒有結(jié)束標(biāo)志,燙燙燙printf("%d\n", strlen(a));//3printf("%d\n", strlen(b));//隨機(jī)數(shù)return 0; }數(shù)組的輸出
int main() {int arr[10] = { 0 };arr[4] = 5;//下標(biāo)引用操作符int i = 0;int sz = sizeof(arr)/ sizeof(arr[0]);//判斷數(shù)組元素個數(shù)for (i = 0; i < sz; i++){printf("%d", arr[i]);}return 0; } 運(yùn)行結(jié)果: 0000500000?注意:數(shù)組的下標(biāo)是從0開始的
?數(shù)組的存放
int main() {int arr[10] = { 0 };int i = 0;for (i < 0; i < 10; i++){printf("%p\n", &arr[i]);//%p是按地址的格式打印-進(jìn)行補(bǔ)齊十六進(jìn)制的打印}printf("%x\n", 0x12);printf("%p\n", 0x12);return 0; } 運(yùn)行結(jié)果: 0000002D780FF668 0000002D780FF66C 0000002D780FF670 0000002D780FF674 0000002D780FF678 0000002D780FF67C 0000002D780FF680 0000002D780FF684 0000002D780FF688 0000002D780FF68C 12 0000000000000012根據(jù)數(shù)組中一個元素占據(jù)四個字節(jié),所以我們可以發(fā)現(xiàn),一維數(shù)組在內(nèi)存中是連續(xù)存放的
隨著數(shù)組下標(biāo)的增長,地址是由低到高變化的?
數(shù)組名的調(diào)用
int main() {int arr[5] = { 1,2,3,4,5 };int* p = arr;//數(shù)組名是數(shù)組首元素的地址int i = 0;for (i = 0; i < 5; i++){printf("%d", *p);p++;}return 0; } 運(yùn)行結(jié)果: 12345?數(shù)組的下標(biāo)越界問題
int main() {int a=1,c=2,b[5]={0},i;printf("%p %p %p\n", b, &c, &a);//用%p格式打印數(shù)組b,變量c和a的首地址for (i = 0; i <= 8; i++){b[i] = i;printf("%d ", b[i]);}printf("\nc=%d,a=%d,i=%d\n", c, a, i);return 0; }因不同的編譯系統(tǒng)為變量分配的內(nèi)存地址有所不同,因此在不同的系統(tǒng)下運(yùn)行程序可能會輸出不同的值
程序運(yùn)行起來回報錯,輸出結(jié)果可能為2 1 9,也可能為5 6 9,出現(xiàn)第二種結(jié)果的原因是0-4對數(shù)組b進(jìn)行賦值以后,5-8是多余的,造成了下標(biāo)越界,多余的數(shù)則會在之后的訪問中出現(xiàn)?,即訪問c,a時,內(nèi)存中變量a和c的值被修改為了6和5
b[0] b[1]?b[2]?b[3]?b[4] c a i?b[8]
?0? ? ?0? ? ?0? ? ?0? ? ?0? ? 2 1? ? ? ??
進(jìn)行賦值后
b[0] b[1]?b[2]?b[3]?b[4] c a i?b[8]
?0? ? ?1? ? ?2? ? ?3? ? ?4? ? 5 6 9? 8
所以,我們應(yīng)確保元素的正確引用,以免因下標(biāo)越界而造成對其他存儲單元中數(shù)據(jù)的破壞?
二維數(shù)組的定義和初始化
數(shù)組的創(chuàng)建
int main() {int arr[3][4];//整型數(shù)組三行四列char ch[3][5];int arr1[][3];//二維數(shù)組的行可以省略,列不可以省略return 0; }但是在創(chuàng)建但不初始化的時候我們是不可以將二維數(shù)組的行省略的,因?yàn)檫@樣會導(dǎo)致數(shù)組的大小不確定?
n維數(shù)組用n個下標(biāo)來確定各元素在數(shù)組中的順序
數(shù)組的初始化
int main() {//初始化-創(chuàng)建的同時賦值int arr1[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };int arr2[3][4] = { 1,2,3,4,5,6,7 };//整型數(shù)組自動補(bǔ)0,字符數(shù)組自動補(bǔ)\0int arr3[][4] = {1,2,3,4,5,6,7,8,9};//按照初始化列表中提供的初值個數(shù)來定義數(shù)組的大小//{1,2,3,4,5,6,7,8,9,0,0,0};//{{1,2,3,4},{5,6,7,8},{9,0,0,0}};這兩句和上面那一句是等價的int arr4[3][4] = { {1,2},{3,4},{5,6} };//分別對每一行初始化,每一行不足的自動補(bǔ)0return 0; }?對于二維數(shù)組而言,既可以按元組初始化,也可以按行初始化
arr[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}//按元素初始化
arr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}//按行初始化
?上面這兩句本質(zhì)上是等價的
int main() {int i = 0;int j = 0;int arr[][4] = { {1,2},{3,4},{5,6} };int* p = &arr[0][0];for (i = 0; i < 12; i++){printf("%d ", *p);p++;}return 0; } 運(yùn)行結(jié)果: 1 2 0 0 3 4 0 0 5 6 0 0?注意:假設(shè)一個二維數(shù)組arr[3][4],第一行為arr[0][j],以此類推,所以,我們可以認(rèn)為arr[i]是一個一維數(shù)組的數(shù)組名,arr[i][j]是這個一維數(shù)組中的某個元素
數(shù)組的輸出?
int main() {int i = 0;int j = 0;int arr[3][4] = { {1,2},{3,4},{5,6} };for (i = 0; i < 3; i++){for (j = 0; j < 4; j++){printf("%d ", arr[i][j]);}printf("\n");}return 0; }C語言中不帶下標(biāo)的數(shù)組名具有特殊含義,它代表數(shù)組的首地址,因此不能整體引用一個數(shù)組,每次只能引用特定下標(biāo)值的數(shù)組元素?
?數(shù)組的存儲
int main() {int i = 0;int j = 0;int arr[3][4] = { {1,2},{3,4},{5,6} };for (i = 0; i < 3; i++){for (j = 0; j < 4; j++){printf("arr[%d][%d]=%p ",i,j, &arr[i][j]);}printf("\n");}return 0; } 運(yùn)行結(jié)果: arr[0][0]=000000B4FCEFFCB8 arr[0][1]=000000B4FCEFFCBC arr[0][2]=000000B4FCEFFCC0 arr[0][3]=000000B4FCEFFCC4 arr[1][0]=000000B4FCEFFCC8 arr[1][1]=000000B4FCEFFCCC arr[1][2]=000000B4FCEFFCD0 arr[1][3]=000000B4FCEFFCD4 arr[2][0]=000000B4FCEFFCD8 arr[2][1]=000000B4FCEFFCDC arr[2][2]=000000B4FCEFFCE0 arr[2][3]=000000B4FCEFFCE4因此,我們可以看出來,二維數(shù)組在內(nèi)存中也是連續(xù)存放,每一行內(nèi)部是連續(xù)的,行與行之間也是連續(xù)的 ,存完第一行接著存第二行,以此類推,所以數(shù)組的第二維地長度聲明是不可以省略的,系統(tǒng)必須知道每一行有多少個元素才能正確的計算出該元素相對于二維數(shù)組地第一個元素的偏移量
向函數(shù)傳遞一維數(shù)組
數(shù)組名的含義
數(shù)組名是什么?數(shù)組名是數(shù)組首元素的地址
int main() {int arr[10] = { 0 };int sz = sizeof(arr);printf("%d\n", sz);printf("%p\n", &arr[0]);printf("%p\n", arr);printf("%p\n", &arr);return 0; } 運(yùn)行結(jié)果: 40//結(jié)果為什么是40?如果數(shù)組名是首元素地址的話不應(yīng)該是4嗎? 000000746E5EFA88 000000746E5EFA88 000000746E5EFA88//為什么三者結(jié)果是一樣的?對于問題1,我們需要知道雖然數(shù)組名在調(diào)用時,是將數(shù)組地首地址傳了過去,但是我們需要知道有兩個特例:
(1)?sizeof(arr)-數(shù)組名表示整個數(shù)組,計算的是整個數(shù)組的大小,單位是字節(jié)
(2)&數(shù)組名-數(shù)組名表示整個數(shù)組,取出的是整個數(shù)組的地址
對于問題2,我們來看下面這一段代碼
int main() {int arr[10] = { 0 };printf("%p\n", &arr);printf("%p\n", &arr+1);printf("%p\n", arr);printf("%p\n", arr+1);return 0; } 運(yùn)行結(jié)果: 0000002B06B4F958 0000002B06B4F980//二者相差0x28=40 0000002B06B4F958 0000002B06B4F95C//二者相差4所以二者雖然看上去一樣,但是含義不同
數(shù)組作為參數(shù)傳遞給函數(shù)
若要把一個數(shù)組傳遞給一個函數(shù),那么只要使用不帶方括號的數(shù)組名作為函數(shù)實(shí)參調(diào)用函數(shù)即可
數(shù)組名代表數(shù)組第一個元素的地址,因此用數(shù)組名作函數(shù)實(shí)參實(shí)際上是將數(shù)組的首地址傳給被調(diào)函數(shù)
一維數(shù)組作為函數(shù)形參時,數(shù)組的長度不可以出現(xiàn)在數(shù)組名后面的方括號內(nèi),通常用另一個整型形參指定數(shù)組的長度,當(dāng)然我們也可以在方括號中寫入
用數(shù)組名作為函數(shù)實(shí)參時,形參數(shù)組和實(shí)參數(shù)組既可以同名也可以不同名,因?yàn)樗鼈兊拿执淼氖菙?shù)組的首地址,它們都指向了內(nèi)存中的同一段連續(xù)的存儲單元;而用簡單變量作為函數(shù)實(shí)參時,由實(shí)參向形參單向傳遞的是變量的內(nèi)容,不是變量的地址,因此無論它們是否同名,它們都代表內(nèi)存中不同的存儲單元
在被調(diào)函數(shù)中改變形參數(shù)組元素值時,實(shí)參數(shù)組元素值也會隨之改變,這種改變并不是形參反向傳給實(shí)參造成的,而是因?yàn)樾螀⒑蛯?shí)參具有同一地址,共享同一段內(nèi)存單元造型成的
排序和查找
冒泡排序
交換法排序
將第一個元素與后面的元素進(jìn)行比較,一旦比這個元素要小,那么二者就交換順序,將交換順序之后的第一個元素再與上回比較的位置繼續(xù)向后比較,進(jìn)行n-1次比較之后,再將第二個元素依次進(jìn)行比較n-2次,以此類推
#include"Bubble_Sort.h" int main() {int arr[] = { 2,3,1,4,5,0 };int sz = sizeof(arr) / sizeof(arr[0]);//計算數(shù)組元素個數(shù)bubble_sort(arr,sz);for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0; }?
void bubble_sort(int arr[], int sz)//形參arr本質(zhì)是指針,arr[]=*arr {int i = 0;int j = 0;for (j = 0; j < sz - 1; j++)//初始化語句也可以改為j=i+1,判斷條件改成j<n{for (i = 0; i < sz - 1 - j; i++){int tmp = 0;if (arr[i] > (arr[i+1])){tmp = arr[i];arr[i] = arr[i + 1];arr[i + 1] = tmp;}}} }此處將頭文件略去,但不代表不需要寫?
如果我們將數(shù)組名改為指針形式,代碼則是如下樣式
void Bubble_Sort(int* arr, int sz) {int i = 0;int j = 0;for(j=0;j<sz-1;j++,arr++){for (i = 0; i < sz - 1-j; i++){int tmp = 0;if (*arr > *(arr + 1 + i)){tmp = *arr;*arr = *(arr + 1 + i);*(arr + 1+i) = tmp;}}} }選擇法排序?
上面所使用的的交換法排序,第一個最多交換n-1次,第二個n-2次...,這樣排序起來效率太過于低下,所以我們也可以使用選擇法排序
先將第一個數(shù)和第二個數(shù)比較,再與第三個數(shù)比較,以此類推,每次比較之后都將較小的數(shù)的下標(biāo)進(jìn)行保存,這樣我們只需要在每一輪循環(huán)最后比較一下我們保存的元素的下標(biāo)和需要排序的位置數(shù)的下標(biāo)是否相同就可以了,不相同就進(jìn)行交換,這樣每輪最多一次交換操作,一套下來最多n-1次交換操作
void bubble_sort(int* arr, int sz) {int i, j, k, temp;for (i = 0; i < sz - 1; i++){k = i;for (j = i + 1; j < sz; j++){if (arr[j] < arr[k]){k = j;}}if (k != i){temp = arr[k];arr[k] = arr[i];arr[i] = temp;}} }特定子項(xiàng)的排序
對信息進(jìn)行排序時,通常只使用信息的一個子項(xiàng)作為鍵值,由鍵值決定信息的全部子項(xiàng)的排列順序
假如存在數(shù)組a,數(shù)組b,且兩個數(shù)組中的元素個數(shù)相等,數(shù)組a中的第i個元素對應(yīng)數(shù)組b中第i個元素,當(dāng)我們需要根據(jù)數(shù)組b中的元素大小進(jìn)行排序時,我們可以同時將數(shù)組a和數(shù)組b作為參數(shù)傳遞給函數(shù),對b中的元素進(jìn)行冒泡排序,之后再進(jìn)行交換的操作時加一個將數(shù)組a中的元素也進(jìn)行交換的操作即可
temp1 = arr1[k];arr1[k] = arr1[i];?arr1[i] = temp;
temp2 = arr2[k];arr2[k] = arr2[i];?arr2[i] = temp;
查找
當(dāng)用戶輸入相關(guān)的一個信息時,我們進(jìn)行搜索這個元素的過程稱為查找,我們所要學(xué)習(xí)的有線性查找和折半查找
線性查找
使用循環(huán)查找值為x的數(shù)組元素,若找到則返回x在數(shù)組中的下表位置,沒找到則返回-1
int Search(int arr[], int x, int n) {int i;for (i = 0; i < n; i++){if (arr[i] == x){return i;}}return -1; }折半查找?
折半查找也稱二分查找,這種查找方式我們之前已經(jīng)講過,在這里就不在做過多的說明
注意:
(1)雖然折半查找速度比線性查找要快很多,但是在使用折半查找時需要保證元素的大小順序是排好的,不可以是亂序,所以我們在使用折半查找之前,一般會對數(shù)組進(jìn)行排序操作
(2)在進(jìn)行折半查找時,我們一般會將計算中間值mid語句寫為:
mid=(left+right)/2
看似沒有什么問題,但是如果二者的加和過大,超出了limits.h中定義的有符號整數(shù)的極限值,那么將會導(dǎo)致mid變成負(fù)數(shù),所以在這里我們不妨將mid的計算語句寫為:
mid=較小值+(較大值-較小值)/2
向函數(shù)傳遞二維數(shù)組
二維數(shù)組向函數(shù)傳遞和一維數(shù)組向函數(shù)傳遞實(shí)際上并沒有太大的區(qū)別,唯一要注意的一點(diǎn)是,二維數(shù)組作為形參時,第二維的長度不可以省略,不可以像一位數(shù)組一樣空下之后在后面使用其他整型形參指定數(shù)組長度,但是第一維長度可以
總結(jié)
- 上一篇: 2022年欧盟的寒冬,中国外贸企业送温暖
- 下一篇: 第一本Docker书pdf