常見的數據結構類型
1.集合結構 線性結構 樹形結構 圖形結構
1.1、集合結構 說白了就是一個數學意義上的集合,就是一個圓圈中有很多個元素,元素與元素之間沒有任何關係 , 這個很簡單
1.2、線性結構 說白了就是一個條線上站着很多個人。 這條線不一定是直的。也可以是彎的。也可以是值的 相當於一條線被分成了好幾段的樣子 (發揮你的想象力)。 線性結構是一對一的關係
1.3、樹形結構 說白了就是一棵倒立的樹,子節點是就像是大樹的樹枝和樹葉。 做開發的肯定或多或少的知道xml 解析 樹形結構跟他非常類似。也可以想象成一個金字塔。樹形結構是一對多的關係
1.4、圖形結構 這個就比較複雜了。他呢 無窮。無邊 無向(沒有方向箭頭)圖形機構 你可以理解爲多對多 類似於我們人的交集關係
2. 數據結構的存儲
2.1 順序存儲結構發揮想象力啊。 舉個列子。
數組。1-2-3-4-5-6-7-8-9-10。這個就是一個順序存儲結構 ,存儲是按順序的 舉例說明啊。
棧。做開發的都熟悉。棧是先進後出 ,後進先出的形式 對不對 ?!他的你可以這樣理解hello world 在棧裏面從棧底到棧頂的邏輯依次爲 h-e-l-l-o-w-o-r-l-d 這就是順序存儲 再比如 隊列 ,隊列是先進先出的對吧,從頭到尾 h-e-l-l-o-w-o-r-l-d 就是這樣排對的
2.2 鏈式存儲結構再次發揮想象力 這個稍微複雜一點 這個圖片我一直弄好 ,回頭找美工問問,再貼上 例如 還是一個數組1-2-3-4-5-6-7-8-9-10 鏈式存儲就不一樣了 1(地址)-2(地址)-7(地址)-4(地址)-5(地址)-9(地址)-8(地址)-3(地址)-6(地址)-10(地址)。每個數字後面跟着一個地址 而且存儲形式不再是順序 ,也就說順序亂了,1(地址) 1後面跟着的這個地址指向的是2,2後面的地址指向的是3,3後面的地址指向是誰你應該清楚了吧。他執行的時候是 1(地址)-2(地址)-3(地址)-4(地址)-5(地址)-6(地址)-7(地址)-8(地址)-9(地址)-10(地址),但是存儲的時候就是完全隨機的。明白了?!
**3 單向鏈表\雙向鏈表\循環鏈表還是舉例子。**理解最重要。不要去死記硬背 哪些什麼。定義啊。邏輯啊。理解纔是最重要滴
3.1 單向鏈表 A->B->C->D->E->F->G->H. 這就是單向鏈表 H 是頭 A 是尾 像一個只有一個頭的火車一樣 只能一個頭拉着跑
3.2 雙向鏈表 H<- A->B->C->D->E->F->G->H. 這就是雙向鏈表。有頭沒尾。兩邊都可以跑 跟地鐵一樣 到頭了 可以倒着開回來
3.3 循環鏈表 發揮想象力 A->B->C->D->E->F->G->H. 繞成一個圈。就像蛇喫自己的這就是循環 不需要去死記硬背哪些理論知識。
4.二叉樹/平衡二叉樹
4.1 什麼是二叉樹
樹形結構下,兩個節點以內 都稱之爲二叉樹 不存在大於2 的節點 分爲左子樹 右子樹 有順序 不能顛倒 ,懵逼了吧,你肯定會想這是什麼玩意,什麼左子樹右子樹 ,都什麼跟什麼鬼? 現在我以普通話再講一遍,你把二叉樹看成一個人 ,人的頭呢就是樹的根 ,左子樹就是左手,右子樹就是右手,左右手可以都沒有(殘疾嘛,聲明一下,絕非歧視殘疾朋友,勿怪,勿怪就是舉個例子,i am very sorry) , 左右手呢可以有一個,就是不能顛倒。這樣講應該明白了吧 二叉樹有五種表現形式
1. 空的樹(沒有節點)可以理解爲什麼都沒 像空氣一樣
2. 只有根節點。 (理解一個人只有一個頭 其他的什麼都沒,說的有點恐怖)
3. 只有左子樹 (一個頭 一個左手 感覺越來越寫不下去了)
4. 只有右子樹
5. 左右子樹都有二叉樹可以轉換成森林 樹也可以轉換成二叉樹。
這裏就不介紹了 你做項目絕對用不到數據結構大致介紹這麼多吧。理解爲主 別死記,死記沒什麼用
5.算法
5.1 冒泡排序 和選擇排序
冒泡排序:冒泡排序的定義就不提了,總結起來就一句話(劃重點):從左到右,數組中相鄰的兩個元素進行比較,將較大的放到後面
我們同樣,以上面的例子爲例 int [] a= {2,6,5,3,1};
從圖可以看出,第一輪比較,比較了4輪,找出了最小數1,與第一個位置的數字進行了換位;
第二輪排序開始時的數組已經變成了{1,6,5,3,2};
從圖可以看出,第二輪比較,比較了3次,確定剩餘數中的最小數爲2,與第二個位置的數交換。
第三輪排序開始時的數組已經變成了{1,2,5,3,6};
從圖可以看出,第三輪比較,比較了2次,確定了剩餘數中最小的數3,與第三個位置的數互換位置。
第四輪排序開始時的數組已經變成了{1,2,3,5,6};
從圖可以看出,第四輪比較,比較了1次,確定了剩餘數中最小的數5,放在了第4個位置。
這樣4輪比較後,這組數已經排序好了,接下來同上,去找規律,實現代碼了:
運行結果:
選擇排序,總結一句話就是(劃重點):從第一個位置開始比較,找出最小的,和第一個位置互換,開始下一輪。
我們同樣,以上面的例子爲例 int [] a= {2,6,5,3,1};
從圖可以看出,第一輪比較,比較了4輪,找出了最小數1,與第一個位置的數字進行了換位;
第二輪排序開始時的數組已經變成了{1,6,5,3,2};
從圖可以看出,第二輪比較,比較了3次,確定剩餘數中的最小數爲2,與第二個位置的數交換。
第三輪排序開始時的數組已經變成了{1,2,5,3,6};
從圖可以看出,第三輪比較,比較了2次,確定了剩餘數中最小的數3,與第三個位置的數互換位置。
第四輪排序開始時的數組已經變成了{1,2,3,5,6};
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20190721221106174.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTQxNTYyMw==,size_16,color_FFFFFF,t_70)
從圖可以看出,第四輪比較,比較了1次,確定了剩餘數中最小的數5,放在了第4個位置。
這樣4輪比較後,這組數已經排序好了,接下來同上,去找規律,實現代碼了:
運行結果:
選擇排序也就結束了,這樣一弄有沒有更清楚呢?
那麼好,是時候來總結下他們的區別了(劃重點)。
(1)冒泡排序是比較相鄰位置的兩個數,而選擇排序是按順序比較,找最大值或者最小值;
(2)冒泡排序每一輪比較後,位置不對都需要換位置,選擇排序每一輪比較都只需要換一次位置;
(3)冒泡排序是通過數去找位置,選擇排序是給定位置去找數;
冒泡排序優缺點:優點:比較簡單,空間複雜度較低,是穩定的;
缺點:時間複雜度太高,效率慢;
選擇排序優缺點:優點:一輪比較只需要換一次位置; 缺點:效率慢,不穩定(舉個例子5,8,5,2,9 我們知道第一遍選擇第一個元素5會和2交換,那麼原序列中2個5的相對位置前後順序就破壞了)
5.2 插入排序
**算法思路:**插入排序的工作方式非常像人們排序一手撲克牌一樣。開始時,我們的左手爲空並且桌子上的牌面朝下。然後,我們每次從桌子上拿走一張牌並將它插入左手中正確的位置。爲了找到一張牌的正確位置,我們從右到左將它與已在手中的每張牌進行比較,
算法步驟:
- 從第一個元素開始,該元素可以認爲已經被排序
- 取出下一個元素,在已經排序的元素序列中從後向前掃描
- 如果該元素(已排序)大於新元素,將該元素移到下一位置
- 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置
- 將新元素插入到該位置後
代碼實現:
class InsertionSort
{
/*Function to sort array using insertion sort*/
void sort(int arr[])
{
int n = arr.length;
for (int i=1; i<n; ++i)
{
int key = arr[i];
int j = i-1;
/* Move elements of arr[0..i-1], that are
greater than key, to one position ahead
of their current position */
while (j>=0 && arr[j] > key)
{
arr[j+1] = arr[j];
j = j-1;
}
arr[j+1] = key;
}
}
/* A utility function to print array of size n*/
static void printArray(int arr[])
{
int n = arr.length;
for (int i=0; i<n; ++i)
System.out.print(arr[i] + " ");
System.out.println();
}
// Driver method
public static void main(String args[])
{
int arr[] = {12, 11, 13, 5, 6};
InsertionSort ob = new InsertionSort();
ob.sort(arr);
printArray(arr);
}
}
5.3 希爾排序
(1)希爾排序(shell sort)這個排序方法又稱爲縮小增量排序,是1959年D·L·Shell提出來的。該方法的基本思想是:設待排序元素序列有n個元素,首先取一個整數increment(小於n)作爲間隔將全部元素分爲increment個子序列,所有距離爲increment的元素放在同一個子序列中,在每一個子序列中分別實行直接插入排序。然後縮小間隔increment,重複上述子序列劃分和排序工作。直到最後取increment=1,將所有元素放在同一個子序列中排序爲止。
(2)由於開始時,increment的取值較大,每個子序列中的元素較少,排序速度較快,到排序後期increment取值逐漸變小,子序列中元素個數逐漸增多,但由於前面工作的基礎,大多數元素已經基本有序,所以排序速度仍然很快。
(3)希爾排序舉例:
1>下面給出一個數據列:
2>第一趟取increment的方法是:n/3向下取整+1=3(關於increment的取法之後會有介紹)。將整個數據列劃分爲間隔爲3的3個子序列,然後對每一個子序列執行直接插入排序,相當於對整個序列執行了部分排序調整。圖解如下:
3>第二趟將間隔increment= increment/3向下取整+1=2,將整個元素序列劃分爲2個間隔爲2的子序列,分別進行排序。圖解如下:
4>第3趟把間隔縮小爲increment= increment/3向下取整+1=1,當增量爲1的時候,實際上就是把整個數列作爲一個子序列進行插入排序,圖解如下:
5>直到increment=1時,就是對整個數列做最後一次調整,因爲前面的序列調整已經使得整個序列部分有序,所以最後一次調整也變得十分輕鬆,這也是希爾排序性能優越的體現。
(4)希爾排序算法的代碼實現(C++)
//函數功能,希爾排序算法對數字遞增排序
//函數參數,數列起點,數列終點
void shell_sort(const int start, const int end) {
int increment = end - start + 1; //初始化劃分增量
int temp{ 0 };
do { //每次減小增量,直到increment = 1
increment = increment / 3 + 1;
for (int i = start + increment; i <= end; ++i) { //對每個劃分進行直接插入排序
if (numbers[i - increment] > numbers[i]) {
temp = numbers[i];
int j = i - increment;
do { //移動元素並尋找位置
numbers[j + increment] = numbers[j];
j -= increment;
} while (j >= start && numbers[j] > temp);
numbers[j + increment] = temp; //插入元素
}
}
} while (increment > 1);
}
上面的函數的第一個do……while控制increment每次的縮小,其內部便是直接插入排序算法的使用,與直接插入排序算法稍有不同的一點是:其j每次的變化量是increment而不是1。
(5)關於希爾排序increment(增量)的取法。
增量increment的取法有各種方案。最初shell提出取increment=n/2向下取整,increment=increment/2向下取整,直到increment=1。但由於直到最後一步,在奇數位置的元素纔會與偶數位置的元素進行比較,這樣使用這個序列的效率會很低。後來Knuth提出取increment=n/3向下取整+1.還有人提出都取奇數爲好,也有人提出increment互質爲好。應用不同的序列會使希爾排序算法的性能有很大的差異。
(6)希爾排序應該注意的問題
從上面圖解希爾排序的過程可以看到,相等的排序碼25在排序前後的順序發生了顛倒,所以希爾排序是一種不穩定的排序算法。
5.4 二分查找
二分查找法實質上是不斷地將有序數據集進行對半分割,並檢查每個分區的中間元素。在以下介紹的實現方法中,有序數據集存放在sorted中,sorted是一塊連續的存儲空間。參數target是要查找的數據。
此實現過程的實施是通過變量left和right控制一個循環來查找元素(其中left和right是正在查找的數據集的兩個邊界值)。首先,將left和right分別設置爲0和size-1。在循環的每次迭代過程中,將middle設置爲left和right之間區域的中間值。如果處於middle的元素比目標值小,將左索引值移動到middle後的一個元素的位置上。即下一組要搜索的區域是當前數據集的上半區。如果處於middle的元素比目標元素大,將右索引值移動到middle前一個元素的位置上。即下一組要搜索的區域是當前數據集的下半區。隨着搜索的不斷進行,left從左向右移,right從右向左移。一旦在middle處找到目標,查找將停止;如果沒有找到目標,left和right將重合。圖示如下:
5.5 快排
快速排序原理:從一組數中任意選出一個數,將大於它的數放右邊,小於它的數放左邊,然後再從左邊和右邊的倆組數中分別執行此操作,知道組中元素數爲1,此時,數組就是有序的了。
實現代碼:
int partsort(int a[],int l,int r){ //將比a[r]小的元素放左邊,比它大的放右邊,最後把a[r]放中間
2
3 int i=l; //i爲比a[r]大的元素的下標,初始爲開始位置l
4
5 for(int j=l;j<r;j++){
6
7 if(a[j]<=a[r]){ //如果元素比a[r]小則和大的元素交換位置,目的讓小的放一起,大的放一起
8
9 int t=a[j]; //可以自己手運行幾遍這個循環
10
11 a[j]=a[i];
12
13 a[i]=t;
14
15 i++;
16
17 }
18
19 }
20
21 int t=a[i]; //a[i]爲小元素和大元素的交界處,將a[r]與之交換
22
23 a[i]=a[r];
24
25 a[r]=t;
26
27 return i; //返回交界處下標,繼續排前邊的和後邊的這倆組
28
29 }
30
31 void quicksort(int a[],int l,int r){
32
33 if(l<r){ //遞歸結束條件爲組中只剩一個元素
34
35 int p=partsort(a,l,r); //分成倆組返回交界處
36
37 quicksort(a,l,p-1); //繼續分左邊
38
39 quicksort(a,p+1,r);
40
41 }
42
43 }
4、堆排序
要想理解堆排序,首先你要知道最大堆,要想理解最大堆,你得知道二叉樹。
二叉樹:每個節點最多有倆個孩子節點。
最大堆:父親節點的值總是大於孩子節點的值。
當然在這裏二叉樹的存儲結構不是鏈表,是使用數組存的:
(1)數組下標爲0處是根節點。
(2)父親節點下標2爲左孩子下標,父親節點下標2+1爲右孩子下標。
根據這倆條準則我們就可以將二叉樹存在數組了。
堆排序原理:我們知道最大堆的性質(父親節點的值總是大於孩子節點的值),那麼根節點處不就是當前數列的最大值嗎,那麼我們每次取根節點的值放在末尾,然後將最大堆的大小-1,更新最大堆,取根節點放後邊…不斷執行這個過程,直到最大堆中只剩一個元素,此時數組就是一個有序數組了。
根據原理可以看出我們需要編的操作有
(1)建最大堆
(2)更新最大堆,其實建立最大堆就是不斷更新最大堆的過程,如果我們將每個結點都執行一遍更新最大堆操作(即父親節點的值總是大於孩子節點的值,不符合的話將父親節點與最大的孩子交換位置),當然執行順序必須是從下往上,然後只需從非葉子節點開始執行就好了(非葉子節點就是有孩子的結點)。
堆排序代碼如下:
//更新最大堆操作
2
3 void dfDui(int x) { //參數爲父親節點下標
4
5 int lchild=x*2; //左孩子下標
6
7 int rchild=x*2+1; //右孩子下標
8
9 int max=x; //最大值下標初始爲父親下標
10
11 if(lchild<size&&a[lchild]>a[max]) //比較找出最大值
12
13 max=lchild;
14
15 if(rchild<size&&a[rchild]>a[max])
16
17 max=rchild;
18
19 if(max!=x){ //若父親節點爲最大值,則符合性質,否則交換,將最大值移到父親節點處,然後因爲孩子節點處已改變,更新此節點。
20
21 int t=a[max];
22
23 a[max]=a[x];
24
25 a[x]=t;
26
27 dfDui(max);
28
29 }
30
31 }
32
33 //建最大堆操作
34
35 void creatDui(){
36
37 for(int i=a.length/2+1;i>=0;i--){ //葉子結點數爲結點總數一半且都在最後(可以從孩子節點下標的算法爲父親節點*2看出),因此 duDui(i); // a.length/2+1處開始爲非葉子節點
38
39 }
40
41 }
42
43 //堆排序操作
44
45 void sort(){
46
47 creatDui(); //建最大堆
48
49 for(int i=size-1;i>=1;i--){ //每次將第一個數與最後一個數交換,然後大小-1,更新已經改變的根節點
50
51 int t=a[0];
52
53 a[0]=a[size-1];
54
55 a[size-1]=t;
56
57 size--;
58
59 dfDui(0);
60
61 }
62
63 }
轉載鏈接:https://www.jianshu.com/p/1b43f4eac8d8
參考原文:https://blog.csdn.net/zxm317122667/article/details/83344178
參考原文:https://blog.csdn.net/weixin_37818081/article/details/79202115
給大家推薦一個優秀的iOS交流平臺,平臺裏的夥伴們都是非常優秀的iOS開發人員,我們專注於技術的分享與技巧的交流,大家可以在平臺上討論技術,交流學習。歡迎大家的加入(密碼:001)