常考的數據結構
二叉樹
首先說說什麼是二叉樹?在計算機科學中,二叉樹(英語:Binary tree)是每個節點最多隻有兩個分支(即不存在分支度大於2的節點)的樹結構。通常分支被稱作“左子樹”或“右子樹”。二叉樹的分支具有左右次序,不能隨意顛倒。
那麼什麼是樹?
一棵樹(tree)是由n(n>0)個元素組成的有限集合,其中:
(1)每個元素稱爲結點(node);
(2)有一個特定的結點,稱爲根結點或根(root);
(3)除根結點外,其餘結點被分成m(m>=0)個互不相交的有限集合,而每個子集又都是一棵樹(稱爲原樹的子樹)
他的每一個節點可以有零個或多個後繼節點,單有且只有一個前驅節點(根節點除外);這些數據節點按分支關係組織起來,清晰地反映了數據元素間的層次關係,
二叉樹的遍歷
1.先(根)序遍歷的遞歸算法定義:
若二叉樹非空,則依次執行如下操作:
(1)訪問根結點;
(2)先序遍歷左子樹;
(3)先序遍歷右子樹。
ABDGHCEIF
2.中(根)序遍歷的遞歸算法定義:
若二叉樹非空,則依次執行如下操作:
- 中序遍歷左子樹;
- 訪問根結點;
- 中序遍歷右子樹。
GDHBAEICF
3.後(根)序遍歷得遞歸算法定義:
若二叉樹非空,則依次執行如下操作:
- 後序遍歷左子樹;
- 後序遍歷右子樹;
- 訪問根結點。
GHDBIEFCA
二叉排序樹
二叉排序或者是一棵空樹,或者是具有下列性質的二叉樹:
(1)若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
(2)若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
(3)左、右子樹也分別爲二叉排序樹;
(4)沒有鍵值相等的節點。
插入:
若二叉排序樹爲空,則創建一個key域爲k的節點,將他作爲根節點,否則將k和根節點的關鍵字比較,若兩者相等,則說明樹中已有k,無需插入,直接返回。若k>T 則插入右子樹,<則插入左子樹。
查找:
查找的平均查找長度和二叉排序樹的形態有關,最差情況爲(n+1)/2和單鏈表的順序查找相同。最好情況爲樹的形態比較勻稱,平均查找長度爲log2n。
刪除:先進行查找,找到待刪除的節點和其父節點,p指向待刪除的節點,q指向p的父節點
- 若待刪除的是葉子節點,則直接刪除該節點
- 若只有左子樹沒有右子樹,則將左子樹的根節點放在p的位置上。同理只有右子樹沒有左子樹。
- 若同時有左子樹和右子樹,則可以將左子樹中最大的節點或右子樹中最小的節點放在被刪除的節點位置。
平衡二叉樹
平衡二叉樹(Self-balancing binary search tree)又被稱爲AVL樹(有別於AVL算法),且具有以下性質:它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。平衡二叉樹的常用實現方法有紅黑樹、AVL、替罪羊樹、Treap、伸展樹等。 最小二叉平衡樹的節點的公式如下 F(n)=F(n-1)+F(n-2)+1 這個類似於一個遞歸的數列,可以參考Fibonacci(斐波那契)數列,1是根節點,F(n-1)是左子樹的節點數量,F(n-2)是右子樹的節點數量。
完全二叉樹
完全二叉樹是效率很高的數據結構,完全二叉樹是由滿二叉樹而引出來的。對於深度爲K的,有n個結點的二叉樹,當且僅當其每一個結點都與深度爲K的滿二叉樹中編號從1至n的結點一一對應時稱之爲完全二叉樹。
堆
堆是一種經過排序的完全二叉樹或滿二叉樹,n個元素的序列{k1,k2,…,kn},當且僅當滿足如下關係時被成爲堆(1)Ki <= k2i 且 ki <= k2i-1或 (2) Ki >= k2i 且 ki >= k2i-1(i = 1,2,…[n/2])當滿足(1)時,爲最小堆,當滿足(2)時,爲最大堆。
插入:最大堆的插入的思想就是先在最後的結點添加一個元素,然後沿着樹上升。跟最大堆的初始化大致相同。
刪除:
操作原理是:當刪除節點的數值時,原來的位置就會出現一個孔,填充這個孔的方法就是,
把最後的葉子的值賦給該孔並下調到合適位置,最後把該葉子刪除。
如圖中要刪除72,先用堆中最後一個元素來35替換72,再將35下沉到合適位置,最後將葉子節點刪除。
“結點下沉”
常考的排序算法
冒泡排序
原理如下:
-
比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
-
對每一對相鄰元素做同樣的工作,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數。
-
針對所有的元素重複以上的步驟,除了最後一個。
-
持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。
//升序
void sort(int num[]){
for(int i=0;i<num.length;i++){
for(int j=0;j<num.length-i-1;j++){
if(num[j]>num[j+1]){
int tamp = num[j];
num[j] = num[j+1];
num[j+1] = tamp;
}
}
}
}
時間複雜度
快速排序
1)設置兩個變量i、j,排序開始的時候:i=0,j=N-1;
2)以第一個數組元素作爲關鍵數據,賦值給key,即key=A[0];
3)從j開始向前搜索,即由後開始向前搜索(j--),找到第一個小於key的值A[j],將A[j]和A[i]互換;
4)從i開始向後搜索,即由前開始向後搜索(i++),找到第一個大於key的A[i],將A[i]和A[j]互換;
5)重複第3、4步,直到i=j; (3,4步中,沒找到符合條件的值,即3中A[j]不小於key,4中A[i]不大於key的時候改變j、i的值,使得j=j-1,i=i+1,直至找到爲止。找到符合條件的值,進行交換的時候i, j指針位置不變。另外,i==j這一過程一定正好是i+或j-完成的時候,此時令循環結束)。
void qsort(int num[],int s,int t){
int i=s ,j=t;
int tamp;
if(s<t){
tamp = num[s];
while(i!=j){
while(j>i&&num[j]>=tamp)
j--;
num[i] = num[j];
while(j>i&&num[i]<=tamp)
i++;
num[j] = num[i];
}
num[i] = tamp;
qsort(num,s,i-1);
qsort(num,i+1,t);
}
}
時間複雜度
最壞情況:每次選取的基準都爲最小或最大時間複雜度爲
最好情況:O(nlog2n)
堆排序
簡單的來說就是構建一個最大堆,然後將他的頂輸出,將尾部放置頂,然後進行下沉操作
時間複雜度:O(nlog2n)