數據結構知識點大總匯
一、數據結構緒論
數據結構的基本概念
數據結構是一門研究非數值計算的程序設計問題中,計算機的操作對象以及它們之間的關系和操作的學科。
數據元素是數據的基本單位,在計算機程序中通常作為一個整體進行考慮和處理。
數據結構包含三個方面的含義:

邏輯結構:

物理結構:數據的邏輯結構在計算機中的表示,稱此為物理結構,或稱存儲結構。


數據類型:一個值的集合以及定義在這個值集上的一組操作的總稱。

抽象數據類型:通常由用戶定義,用以表示應用問題的數據模型以及定義在該模型上的一組操作。

算法是描述計算機解決給定問題的操作過程,即為決解某一特定問題而由若干條指令組成的有窮序列。

算法的效率分析:
http://univasity.iteye.com/blog/1164707
事后統計法:收集該算法實際的執行時間和實際占用空間的統計資料。
事前分析估算法:在算法運行之前分析該算法的時間復雜度和空間復雜度,來判斷算法的效率。
時間復雜度分析:



、
常見函數的時間復雜度按數量遞增排列及增長率:

二、線性表
線性表的類型定義
線性表是n(n>0)個相同類型數據元素構成的有限序列,其中n為線性表的長度。

線性表的基本操作:


線性表的順序表示和實現
線性表的順序存儲結構:用一組地址連續的存儲單元依次存儲線性表的元素。

線性表的順序存儲,也成為向量存儲,又可以說是一維數組存儲。線性表中結點存放的物理順序與邏輯順序完全一致,它叫向量存儲。

線性表順序存儲結構在插入或刪除數據元素時比較繁瑣,但是它比較適合存取數據元素。
線性表的插入操作:在第i個元素之前插入一個元素時,需將第n至第i(共n-i+1)個元素向后移動一個位置。

線性表的刪除操作:刪除第i個元素時需將從第i+1至第n(共n-i)個元素一次向前移動一個位置

線性表的鏈式表示和實現
用一組任意的存儲單元(可能不連續)存儲線性表的數據元素。
在鏈式存儲結構中,每個存儲結點不僅包含數據元素本身的信息,還必須包含每個元素之間邏輯關系的信息,即包含直接后繼結點的地址信息(指針域)。

邏輯順序與物理順序有可能不一致;屬順序存取的存儲結構,即存取每個元素必須從第一個元素開始遍歷,直到找到需要訪問的元素,所以所花時間不一定相等。


鏈表的創建方式:

結點類的定義:

單鏈表的基本操作
插入方式——頭插法:

插入方式——尾插法:

查找運算——按序號查找:在鏈表中,即使知道被訪問結點的序號i,也不能像順序表中那么直接按序號i訪問結點,而只能從鏈表的頭指針除法,順着鏈域next逐個結點往下搜索,直至搜索到第i個結點為止。鏈表不是隨機存取結構,只能順序存取。

查找運算——按數值查找:

刪除結點:將被刪除結點的前驅指針連接被刪除結點的后繼指針

循環鏈表
表中尾結點的指針域指向頭結點,形成一個環。從表中任意一個點出發都可以找到表中其他的結點。

循環鏈表的操作和線性鏈表的操作基本一致,但循環鏈表中沒有NULL指針,故遍歷操作時,終止條件不再是判斷p或p.next是否為空,而是判斷他們是否等於某一指定指針,如頭指針或尾指針。
雙向鏈表
雙向鏈表是在單鏈表的每個結點里再增加一個指向其直接前驅的指針域prior。這樣就形成了鏈表中有兩個方向不同的鏈,故稱為雙向鏈表。

雙線鏈表——頭插法:

雙向鏈表——尾插法:


插入操作

刪除操作
三、棧和隊列
棧的概念
棧是限制在表的一端進行插入和刪除運算的線性表,通常稱插入、刪除的這一端為棧頂,另一端為棧底。當表中沒有元素時成為空棧。

棧的進出順序判斷:

棧的基本操作:

順序棧
順序棧利用一組地址連續的存儲單元依次存放自棧底到棧頂的數據元素,同時由於棧的操作的特殊性,還必須附設一個位置指針top來動態地指示棧頂元素的順序棧中的位置。通常以top=0表示空棧。


順序棧的基本運算:


鏈棧:采用鏈表作為存儲結構實現的棧。為便於操作,采用帶頭結點的單鏈表實現棧。因為棧的插入和刪除操作僅限制在表頭位置進行,所以鏈表的表頭指針就作為棧頂指針。

順序棧和鏈式棧的比較:實現鏈式棧和順序棧的操作都是需要常數時間,即時間復雜度為O(1),主要從空間和時間兩個方面考慮。初始時,順序堆棧必須說明一個固定的長度,當堆棧不夠滿時,造成一些空間的浪費,而鏈式堆棧的長度可變則使長度不需要預先設定,相對比較節省空間,但是在每個節點中設置了一個指針域,從而產生了結構開銷。
隊列的概念
隊列是一種先進先出的線性表,它只允許在表的一端進行插入,而在另一端刪除元素。在隊列中,允許插入數據一端成為隊尾(rear),允許刪除的那一端稱為隊頭(front)。

隊列的基本操作:


順序隊列
順序隊列利用一組地址連續的存儲單元依次存放自隊首到隊尾的數據元素,同時由於隊的操作的特殊性,還必須附兩個位置指針front和rear來動態地指示隊首元素和隊尾元素在順序隊列中的位置。通常front=rear表示空隊列。

隊列同堆棧一樣也有上溢和下溢現象。以外,隊列中還存在“假溢出”現象。所謂“假溢出”是指在入隊和出隊操作中,頭尾指針不斷增加而不減小或只減小而不增加,致使被刪除元素的空間無法重新利用,最后造成隊列中有空閑空間,但是不能夠插入元素,也不能夠刪除元素的現象。
鏈式隊列:采用鏈表作為存儲結構實現的隊列。為便於操作,采用帶頭結點的單鏈表實現隊列。因為隊列的插入和刪除操作位置不同,所以鏈表需要設置表頭指針和表尾指針分別指隊首和隊尾。
循環隊列
假設向量的空間是m,只要在入出隊列時,將隊首和隊尾的指針對m做求模運算即可實現隊首和隊尾指針的循環,即隊首和隊尾指針的取值范圍是0到m-1之間。



判斷隊空和隊滿的方法


四、數組和廣義表
數組的定義
數組是我們熟悉的數據類型,數組中各元素具有統一的類型,並且數組元素的下標一般具有固定的上界和下界,因此,數組的處理比其它復雜的結構更為簡單。
任何數組A都可以看作一個線性表。數組維數確定后,數據元素個數和元素之間的關系不再發生改變,適合順序存儲。
數組的基本操作

數組的順序表示和實現
行優先順序

列優先順序

矩陣的壓縮存儲
有些特殊矩陣,非零元素呈某種規律分布或者矩陣中出現大量的零元素的情況下,會占用許多單元去存儲重復的非零元素或零元素,這對高階矩陣會造成極大的浪費,為了節省存儲空間,對這類矩陣進行壓縮存儲——即為多個相同的非零元素只分配一個存儲空間;對零元素不分配空間。
特殊矩陣:所謂特殊矩陣是指非零元素或零元素的分布有一定規律的矩陣,如對稱矩陣、三角矩陣、對角矩陣等等。

對稱矩陣

對稱矩陣中元素關於主對角線對稱,故只要存儲矩陣中上三角或下三角中的元素,讓每兩個對稱的元素共享一個存儲空間,這樣能節約近一半的存儲空間。
n2 個元素可以壓縮到 n(n+1)/2個空間中。


三角矩陣
以主對角線划分,三角矩陣有上三角和下三角兩種。上三角矩陣它的下三角中的元素均為常數。下三角矩陣正好相反,它的主對角線上方均為常數。



稀疏矩陣

除了記錄非零元素的值之外,還必須同時幾下它所在的行和列的位置。稀疏矩陣的存儲方法一般有三種:三元組法、行邏輯連接順序表和十字鏈表法。

廣義表
是由零個或多個原子或子表組成的優先序列,是線性表的推廣。







廣義表的存儲結構
廣義表中的數據元素可以具有不同的結構,因此,難以用順序存儲結構表示,通常采用鏈式存儲結構,每個數據元素可用一個節點表示。由於廣義表中有兩種數據元素,因此需要有兩種結構的節點——一種是表結點,一種是原子結點。
表結點由三個域組成:標志域、指示表頭的指針的指針域和指示表尾的指針域;而原子域只需兩個域:標志域和值域。


表結點由三個域組成:標志域、指示表頭的指針域和指向下一個元素的指針;原子結點的三個域為:標志域、值域和指向下一個元素的指針。


五、樹
樹的定義

樹的邏輯表示:樹形表示法、文氏圖表示法、凹入表示法、括號表示法。




結點:表示樹中的元素,包括數據項及若干指向其子樹的分支。
結點的度:結點擁有的子樹樹;樹的度:一棵樹中最大的結點度數

葉子結點:度為0的結點;分支結點:度不為0的結點;孩子:結點子樹的根稱為該結點的孩子;雙親:孩子結點的上層結點叫該結點的雙親;兄弟:同一雙親的孩子。
深度:樹中結點的最大層次數。

有序樹:樹中各結點的子樹從左至右是有次序的,不能互換。否則稱為無序樹。
樹的性質
樹中的結點數等於所有結點的度數加1。

度為m的樹中第i層上至多有mi-1 個結點(i>=1)。



二叉樹的概念

滿二叉樹:定義——一棵深度為k且具有2k-1個結點的二叉樹。特點——每一層上的結點數都是最大結點數。
完全二叉樹:定義——深度為k,有n個結點的二叉樹當且僅當其每一個結點都與深度為k的滿二叉樹中編號從1至n的結點一一對應時,稱為完全二叉樹。
二叉樹的性質

二叉樹的第i層上至多有2i-1(i>=1)個結點。
深度為K的二叉樹至多有2k-1個結點。
對任何一棵二叉樹T,如果其葉子數為n0,度為2的結點數為n2,則n0=n2+1。



一個有n個結點的完全二叉樹,編號最大的分支結點的編號是

一個有n個結點的完全二叉樹,n0的個數是不小於n/2的最小整數。
二叉樹的存儲結構
用一組連續的存儲單元存儲二叉樹的數據元素。在存儲之前對二叉樹的所有結點安排一個適當的序列,使得結點在這個序列中的相互位置能反應出結點之間的邏輯關系。

特點:結點間關系蘊含在其存儲位置中;浪費空間,適於存滿二叉樹和完全二叉樹。
二叉樹的鏈式存儲結構
用一個鏈表來存儲一棵二叉樹,二叉樹中每一個結點用鏈表中的一個鏈結點來存儲。



遍歷二叉樹
遍歷方法:


利用遍歷結果確定二叉樹:

先序遍歷算法:
//先序遍歷遞歸算法
void preorder(Tree t){
if(t==null)
return;
visit(t);
preorder(t.left());
preorder(t.right());
}
//先序遍歷非遞歸算法
void PreOrderUnrec(Bitree t)
{
//創建棧來存放樹的結點
SqStack s;
StackInit(s);
p=t;
while (p!=null || !StackEmpty(s))
{
while (p!=null) //遍歷左子樹
{
visite(p->data);
push(s,p);
p=p->lchild;
}//endwhile
if (!StackEmpty(s)) //通過下一次循環中的內嵌while實現右子樹遍歷
{
p=pop(s);
p=p->rchild;
}//endif
}//endwhile
}//PreOrderUnrec
中序遍歷算法:
//中序遍歷遞歸算法
void inorder(Tree t){
if(t==null)
return;
inorder(t.left());
visit(t);
inorder(t.right());
}
void InOrderUnrec(Bitree t)
{
//創建棧來存放樹的結點
SqStack s;
StackInit(s);
p=t;
while (p!=null || !StackEmpty(s))
{
while (p!=null) //遍歷左子樹
{
push(s,p);
p=p->lchild;
}//endwhile
if (!StackEmpty(s)) //通過下一次循環中的內嵌while實現右子樹遍歷
{
p=pop(s);
visite(p->data); //訪問根節點
p=p->rchild;
}//endif
}//endwhile
}//InOrderUnrec
后序遍歷算法:
后序遍歷遞歸算法
void inorder(Tree t){
if(t==null)
return;
inorder(t.left());
inorder(t.right());
visit(t);
}
//后序遍歷非遞歸算法
對於任一結點P,將其入棧,然后沿其左子樹一直往下搜索,直到搜索到沒有左孩子的結點,此時該結點出現在棧頂,但是此時不能將其出棧並訪問,因此其右孩子還為被訪問。所以接下來按照相同的規則對其右子樹進行相同的處理,當訪問完其右孩子時,該結點又出現在棧頂,此時可以將其出棧並訪問。這樣就保證了正確的訪問順序。可以看出,在這個過程中,每個結點都兩次出現在棧頂,只有在第二次出現在棧頂時,才能訪問它。因此需要多設置一個變量標識該結點是否是第一次出現在棧頂。
void postOrder2(BinTree *root) //非遞歸后序遍歷
{
stack<BTNode*> s;
BinTree *p=root;
BTNode *temp;
while(p!=NULL||!s.empty())
{
while(p!=NULL) //沿左子樹一直往下搜索,直至出現沒有左子樹的結點
{
BTNode *btn=(BTNode *)malloc(sizeof(BTNode));
btn->btnode=p;
btn->isFirst=true;
s.push(btn);
p=p->lchild;
}
if(!s.empty())
{
temp=s.top();
s.pop();
if(temp->isFirst==true) //表示是第一次出現在棧頂
{
temp->isFirst=false;
s.push(temp);
p=temp->btnode->rchild;
}
else //第二次出現在棧頂
{
cout<<temp->btnode->data<<" ";
p=NULL;
}
}
}
}
線索二叉樹
利用二叉鏈表的空指針域,建立指向該結點的前驅/后繼結點的指針,方便二叉樹的線性化使用。

對二叉鏈表以某種次序遍歷使其變為線索二叉樹的過程叫做線索化。有先序線索二叉樹、中序線索二叉樹(更實用)和后序線索二叉樹三種。

建立中序線索二叉樹






樹的存儲結構
雙親表示法:用一組連續空間存儲樹的結點,每個節點包含兩個域——數據域用來存放結點本身信息,雙親域用來指示本結點的雙親結點在數組中位置。

孩子表示法:采用孩子鏈表,每個結點的孩子結點用單鏈表存儲,再用含n個元素的結構數組指向每個孩子鏈表。

帶雙親的孩子鏈表

孩子兄弟表示法:用二叉鏈表作為樹的存儲結構。鏈表中結點的兩個鏈域分為指向該結點的第一個孩子結點和下一個兄弟結點。

森林與二叉樹的轉換
將樹轉換為二叉樹


將二叉樹轉換為樹:


森林轉換成二叉樹:


二叉樹轉換成森林


哈夫曼樹
結點的權及帶權路徑長度:若將樹中結點賦給一個有着某種含義的數值,則這個數值稱為該結點的權。結點的帶權路徑長度為:從根節點到該結點之間的路徑長度與該結點的權的乘積。


在一棵二叉樹中,若帶權路徑長度達到最小,稱這樣的二叉樹為最優二叉樹,也稱哈夫曼樹。

六、圖
圖的概念
圖是一種較線性表和樹更為復雜的數據結構,在圖形結構中,結點之間關系可以是任意的,圖中任意兩個數據元素之間都可能相關。

有向圖和無向圖


若無向圖中的每兩個頂點之間都存在着一條邊,則稱該無向圖稱作完全無向圖;顯然完全無向圖中包含着e=n(n-1)/2條邊。若有無向圖中的每兩個頂點之間都存在方向相反的兩條邊,則稱該有向圖稱作完全有向圖;顯然完全有向圖中包含有e=n(n-1)條邊。

與圖的邊或弧相關的數叫做權,帶權的圖稱為網。



對於有向圖而言,度又分為出度和入度。頂點的出度——以頂點v為弧尾的弧的數目;頂點的入度——以頂點v為弧頭的弧的數目;頂點的度為該頂點的出度和入度的和。


在無向圖G中,如果從頂點v到頂點w存在路徑,則稱v到w是連通的。若圖G中任意兩個頂點之間都有路徑相通,則稱為連通圖。
若無向圖為非連通圖,則圖中各個極大連通子圖稱作此圖的連通分量。任何連通圖的連通分量只有一個,即本身,而非連通圖則有多個連通分量。

在有向圖中,若任意兩個頂點之間都存在一條有向路徑,則稱此有向圖為強連通圖。
若有向圖為非強連通圖,其各個強連通子圖稱為它的強連通分量。

圖的存儲結構——鄰接矩陣
鄰接矩陣是表示頂點之間相鄰關系的矩陣。

無向圖的鄰接矩陣:

有向圖的鄰接矩陣:

網的鄰接矩陣:



圖的存儲結構——鄰接表
鄰接表存儲方法是一種順序分配與鏈式分配相結合的存儲方法。它包括兩部分:一部分是單鏈表,用來存放邊的信息;另一部分是數組,主要用來存放頂點本身的數據信息。

無向圖鄰接表:

有向圖的鄰接表:


圖的存儲結構——十字鏈表
十字鏈表是有向圖的另一種鏈式存儲結構。可以看成是將有向圖的鄰接表和逆鄰接表結合起來得到的一種鏈表。在十字鏈表中,每條弧和每個頂點分別對應着一個結點。



圖的存儲結構——鄰接多重表
鄰接多重表是無向圖的另一種鏈式存儲結構。鄰接多重表和十字鏈表一樣,每條邊和每個頂點分別對應着一個結點。



圖的遍歷
從圖中某個頂點出發游歷圖,訪遍圖中其余頂點,並且使圖中的每個頂點僅被訪問一次的過程
根據搜索方法的不同,圖的遍歷有兩種:深度優先搜索(DFS)和廣度優先搜索(WFS)。
深度優先搜索



連通圖深度優先搜索的算法:



廣度優先搜索

廣度優先搜索是一種分層的搜索過程,每向前走一步可能訪問一批頂點,不像深度優先搜索那樣有回退的情況。因此,廣度優先搜索不是一個遞歸的過程,其算法也不是遞歸的。

連通圖的廣度優先搜索算法:

非連通圖廣度優先搜索:

無向圖的連通分量和生成樹
連通圖生成樹:一個連通圖的生成樹是一個極小連通子圖,它含有圖中全部頂點,但只有構成一棵樹的n-1條邊。


當無向圖為非連通圖時,從圖中某一頂點除法,利用深度優先搜索算法或廣度優先搜索算法不可能遍歷到圖中的所有頂點,只能訪問到該頂點所在的極大連通子圖的所有頂點,該極大連通子圖稱為無向圖的一個連通分量。

使用不同的遍歷圖的方法,可以得到不同的生成樹;從不同的頂點出發,也可能得到不同的生成樹。

最小生成樹
在連通網的眾多生成樹中,各邊權值之和最小的生成樹稱為最小代價生成樹,簡稱最小生成樹。

生成最小生成樹算法——普里姆算法:


生成最小生成樹算法——克魯斯卡爾算法:


有向無環圖
一個無環的有向圖稱為有向無環圖,簡稱DAG圖。

拓撲排序
在一個有向圖中,若用頂點表示活動,有向邊表示活動間的先后關系,稱該有向圖叫做頂點表示活動的網絡,簡稱AOV網。

按照有向圖給出的次序關系,將圖中頂點排成一個線性序列,對於有向圖中沒有限定次序關系的頂點,則可以人為加上任意的次序關系。由此所得頂點的線性序列成為拓撲有序序列。
拓撲排序:由某個集合上的一個偏序(集合中僅有部分成員之間可以比較)得到該集合上的一個全序(集合中全體成員之間均可以比較)的操作稱為拓撲排序。

拓撲排序的算法:



關鍵路徑
在一個有向圖中,若用頂點表示事件,弧表示活動,權表示活動持續時間,稱該有向圖叫做邊表示活動的網絡,簡稱為AOE網。








七、查找
概述
查找表:由同一類型的數據元素(或記錄)構成的集合。



靜態查找表
靜態查找是指在靜態查找表上進行的查找操作,在查找表中滿足條件的數據元素的存儲位置或各種屬性。靜態查找表的查找算法主要有:

順序查找:從表的一端開始,順序掃描線性表,依次將掃描到的關鍵字和給定值k進行比較,若當前掃描到的關鍵字與k相等,則查找成功;若掃描結束后,仍未找到關鍵字等於k的記錄,則查找失敗。


折半查找:對給定值k,逐步確定待查記錄所在區間,每次將搜索空間減少一半,直到查找成功或失敗為止。




分塊查找:


動態查找表:表結構在查找過程中動態生成的這樣一種查找表。實現動態查找方法:二叉排序樹、平衡二叉樹、B-樹和B+樹。
二叉排序樹
定義:左子樹的所有結點均小於根的值;右子樹的所有節點均大於根的值;它的左右子樹也分別為二叉排序樹。

二叉排序樹插入新結點的過程:

二叉排序樹插入新節點遞歸算法:


二叉排序樹刪除結點的算法:


二叉排序樹查找算法分析:

平衡二叉樹
平衡二叉樹又稱為AVL樹,設二叉樹中結點的左子樹和右子樹的深度分別為HL和HR。


若在構造二叉排序樹的同時,使其始終保持為AVL樹,則此時的二叉排序樹為平衡的二叉排序樹。將一棵非平衡二叉排序樹調整成平衡二叉排序樹的“旋轉”,分為:LL平衡旋轉、RR平衡旋轉、LR平衡旋轉、RL平衡旋轉。






B-樹:
http://haicang.blog.51cto.com/2590303/1134864
B-樹又稱基本B樹或多路平衡查找樹。它是構造大型文件系統索引結構的一種數據結構類型,適合在磁盤等直接存取設備上組織動態的查找表。



該公式包括沒有任何關鍵字的葉子結點。
B-樹的查找算法思路:


B-樹的查找效率取決於以下兩個因素:包含k的結點在B-樹種所在的層數h;結點中ki的個數n。

- B-樹的生成:
- B-樹的刪除:
- B+樹
- B+樹是B-樹的變形,目的在於有效地組織文件的索引結構。
- m階B+樹與B-樹的差異:
-
- B+樹種可以有兩種查找方式:順序查找——類似單鏈表的查找,直接在數據層進行查找。隨機查找——類似B-樹的查找,不同的是搜索到索引層的key與k相等時,還得繼續搜索下去,直到數據層為止。
-
- 哈希表
-
- 哈希表,根據設定的哈希函數H(key)和處理沖突的方法將一組關鍵字key映射到一個有限的連續的地址集上,並以關鍵字key在地址集中的“像”作為記錄在表中的存儲位置,這種表便稱為哈希表,這一映射過程稱為哈希造表或散列,所得存儲位置稱哈希地址或散列地址。
- 將不同的關鍵碼映射到同一個哈希地址上,該現象稱為沖突。
- 哈希函數的構造方法
- 常用的哈希函數構造方法有:直接定址法、除留余數法、乘余取整法、數字分析法、平方取中法、折疊法、隨機數法。
- 直接定址法:
- 除留余數法:
- 乘余取整法:
- 數字分析法:
- 平方取中法:
- 疊加法:
- 隨機數法:
- 處理沖突的方法
- 開放定址法、鏈地址法、再哈希法、建立一個公共溢出區
- 開放定址法:
- 鏈地址法:
- 再哈希法:
- 建立一個公共溢出區:
- 紅黑樹
- 紅黑樹,一種二叉查找樹,但在每個結點上增加一個存儲位表示結點顏色,可以是Red或Black。通過對任何一條從根到葉子的路徑上各個結點着色方式的限制,紅黑樹確保沒有一條路徑會比其他路徑長出兩倍,因而是接近平衡的。
- 紅黑樹雖然本質上是一棵二叉查找樹,但它在二叉查找樹的基礎上增加了着色和相關的性質使得紅黑樹相對平衡,從而保證了紅黑樹的查找、插入、刪除的時間復雜度最壞為O(log n)。紅黑樹和AVL樹的區別在於它使用顏色來標識結點的高度,它所追求的是局部平衡而不是AVL樹中的非常嚴格的平衡。
- 紅黑樹的五個性質保證了紅黑樹的高度始終保持在logn:
- 紅黑樹的旋轉操作:紅黑樹的旋轉操作和AVL樹一樣,分為LL、RR、LR、RL四種旋轉類型,差別在於旋轉完成后改變的是結點的顏色,而不是平衡因子。
- 紅黑樹的插入和刪除:http://blog.csdn.net/eric491179912/article/details/6179908
八、排序
- 排序概述
- 排序的分類:內部排序和外部排序(若待排序記錄都在內存中,稱為內部排序;若待排序記錄一部分在內存,一部分在外存,則稱為外部排序)。穩定排序和不穩定排序。
- 內部排序的算法:插入排序(希爾排序)、交換排序(快速排序)、選擇排序(堆排序)、歸並排序、基數排序。
- 插入排序
- 思想:每次將一個待排序的對象,按其關鍵碼大小,插入到前面已經排好序的一組對象的適當位置上,直到對象全部插入為止。
- 具體實現算法:直接插入排序、折半插入排序、希爾排序
- 直接插入排序:
void InsertSort(int a[]){
int i,j;
//按照有小到大的順序排序
for(i=2;i<a.length;i++){
//找到無序表的第一個位置
if(a[i]<a[i-1]){
a[0]=a[i];
//將無序表依次向后移動
for(j=i-1;a[0]<a[j];j--){
a[j+1]=a[j];
}
//將數據插入相應位置
a[j+1]=a[0];
}
}
}
該算法的時間復雜度是:O(n2) - 折半插入排序:
void BInsertSort(int a[]){
int i,j,high,low;
for(i=2;i<a.length;i++){
a[0]=a[i];
low=1;
high=i-1;
int min;
while(low<=high){ //使用折半查找到插入的位置
min=(high+low)/2;
if(a[0]<a[min])
high=min-1;
else
low=min+1;
}
for(j=i-1;j=>high+1;j++) //插入的位置是在high位置之后
a[j+1]=a[j];
a[high+1]=a[0];
}
}
- 希爾排序:
void SheelSort(int a[],int dx){
//這是對直接插入排序的修改
//dx表示增量
//當j<=0時,插入位置已經找到
int i,j;
for(i=dx+1;i<a.length;i++){
if(a[i]<a[i-dx]){
a[0]=a[i];
for(j=i-dx;j>0&&a[0]<a[j];j-=dx)
a[j+dx]=a[j];
a[j+dx]=a[0];
}
}
}
- 交換排序
- 兩兩比較待排序記錄的關鍵碼,如果發生逆序(即排列順序與排序后次序正好相反),則交換之,直到所有記錄都排好序為止。
- 冒泡排序:
void bubbleSort(int a[]){
int i,j;
for(i=1;i<a.length-1;i++){
for(j=1;j<a.length-i;j++){
if(a[j]>a[j+1]){
a[0]=a[j];
a[j]=a[j+1];
a[j+1]=a[0];
}
}
}
}
- 快速排序:
void Partition(int a[],int low,int high){
//這只是一趟快速排序的算法
a[0]=a[low];
while(low<high){
//從高位往低位掃描,找到數值小於關鍵字的位置,與low位置交換
while(low<high&&a[0]<=a[high])
high--;
a[low]=a[high];
//從低位往高位掃描,找到數值大於關鍵字的位置,與high位置交換
while(low<high&&a[low]<=a[0])
low++;
a[high]=a[low];
}
//最后將關鍵字放入數組中
a[low]=a[0];
}
快速排序平均時間復雜度和最好時間復雜度為:O(log2n),最壞時間復雜度為O(n2)。
- 選擇排序
- 不斷從待排記錄序列中選出關鍵字最小的記錄插入已排序記錄序列的后面,直到n個記錄全部插入已排序記錄序列中。
- 簡單選擇排序:
- 堆排序:借助於完全二叉樹結構進行的排序,是一種樹形選擇排序。其特點是——在排序過程中,將R[1...N]看成是一棵完全二叉樹的順序存儲結構,利用完全二叉樹雙親結點和孩子結點之間的內在關系,在當前無序區中選擇關鍵字最大(或最小)的記錄。
- 建立一個堆排序的方法:
- 堆排序的過程:
- 堆排序算法實現:
void HeapAdjust(int *a,int i,int size) //調整堆
{
int lchild=2*i; //i的左孩子節點序號
int rchild=2*i+1; //i的右孩子節點序號
int max=i; //臨時變量
if(i<=size/2) //如果i不是葉節點就不用進行調整
{
if(lchild<=size&&a[lchild]>a[max])
{
max=lchild;
}
if(rchild<=size&&a[rchild]>a[max])
{
max=rchild;
}
if(max!=i)
{
swap(a[i],a[max]);
}
}
}
void BuildHeap(int *a,int size) //建立堆
{
int i;
for(i=size/2;i>=1;i--) //非葉節點最大序號值為size/2
{
HeapAdjust(a,i,size);
}
}
void HeapSort(int *a,int size) //堆排序
{
int i;
BuildHeap(a,size);
for(i=size;i>=1;i--)
{
//cout<<a[1]<<" ";
swap(a[1],a[i]); //交換堆頂和最后一個元素,即每次將剩余元素中的最大者放到最后面
//BuildHeap(a,i-1); //將余下元素重新建立為大頂堆
HeapAdjust(a,1,i-1); //重新調整堆頂節點成為大頂堆
}
}
- 歸並排序
- “歸並”的含義是將兩個或兩個以上的有序表合並成一個新的有序表。
-
- 兩個有序表的合並算法Merge():
- 算法分析:
- 基數排序
- 基數排序是一種借助多關鍵字排序的思想對單邏輯關鍵字進行排序的方法,即先將關鍵字分解成若干部分,然后通過對各部分關鍵字的分別排序,最終完成對全部記錄的排序。
- 多關鍵字的排序:
- 鏈式基數排序:
- 排序算法總結
九、時間復雜度
- 該時間復雜度表達作用於遞歸過程。
- n/b表示下一回遞歸的數據量為n/b,a表示遞歸的次數為a,除了遞歸之外的時間復雜度為O(nd)。