數據結構(DataStructure)與算法(Algorithm)、STL應用

cataloguehtml

0. 引論 
1. 數據結構的概念 
2. 邏輯結構實例 
2.1 堆棧 
2.2 隊列 
2.3  樹形結構 
  2.3.1 二叉樹 
3. 物理結構實例  
3.1 鏈表 
  3.1.1 單向線性鏈表 
  3.1.2 單向循環鏈表 
  3.1.3 雙向線性鏈表 
  3.1.4 雙向循環鏈表 
  3.1.5 數組鏈表 
  3.1.6 鏈表數組 
  3.1.7 二維鏈表 
3.2  順序存儲 
4. 算法   
4.1 查找算法 
4.2 排序算法

 

0. 引論node

0x1: 爲何要學習數據結構ios

N.沃思(Niklaus  Wirth)教授提出程序員

程序 = 算法 + 數據結構
以上公式說明了以下兩個問題
1. 數據上的算法決定如何構造和組織數據(算法 -> 數據結構)
2. 算法的選擇依賴於做爲基礎的數據結構(數據結構 -> 算法)

軟件工程的觀點web

軟件 = 程序 + 文檔

0x2: 數值計算解決問題的通常步驟算法

數學模型 -> 選擇計算機語言 -> 編出程序 -> 測試 -> 最終解答 

數值計算的關鍵是編程

如何得出數學模型(方程)

程序設計人員比較關注程序設計的技巧數組

0x3: 求解非數值計算的問題網絡

主要考慮的是設計出合適的數據結構及相應的算法,即數據結構

 首先要考慮對相關的各類信息如何表示、組織和存儲

所以,能夠認爲,數據結構是一門研究非數值計算的程序設計問題中計算機的操做對象以及它們之間的關係和操做的學科

0x4: STL

STL(Standard Template Library,標準模板庫)是算法和其餘一些組件的集合,STL的目的是標準化組件。STL如今是C++的一部分,咱們能夠很方便地使用STL直接得到數據結構的特性以及操做集

Relevant Link:

http://www.cnblogs.com/LittleHann/p/4011614.html

 

1. 數據結構的概念

0x1:  相關術語

1. 數據: 是計算機化的信息載體
2. 數據元素: 是數據的基本單位,是數據集合中的個體(例如:結點、頂點、記錄等)
3. 數據項: 是具備獨立含義的數據最小單位,例如: 在田徑比賽表中,一個選手的有關信息就是數據元素(記錄),而選手參賽的項目就是數據項(字段)

0x2: 定義

1. 定義1: 數據元素之間的相互關係稱爲結構,帶有結構的數據元素的集合稱爲數據結構
2. 定義2: 按某種邏輯關係組織起來的一批數據(或稱帶結構的數據元素的集合)應用計算機語言並按必定的存儲表示方式把它們存儲在計算機的存儲器中,並在其上定義了一個運算的集合

0x3: 數據結構的三個層次

1. 邏輯結構: 數據元素間抽象化的相互關係(簡稱爲數據結構),它與數據的存儲無關,獨立於計算機,它是從具體問題抽象出來的數學模型 
    1) 集合結構: 集合結構的集合中任何兩個數據元素之間都沒有邏輯關係,組織形式鬆散 
    2) 線性結構: 數據結構中線性結構指的是數據元素之間存在着"一對一"的先後關係
        2.1) 結構中必須存在惟一的首元素
        2.2) 結構中必須存在惟一的尾元素
        2.3) 除首元素外,結構中的每一個元素有且僅有一個前趨元素
        2.4) 除尾元素外,結構中的每一個元素有且僅有一個後繼元素
    3) 樹狀結構: 樹狀結構是一個或多個節點的有限集合,樹型結構中的元素具備一對多的父子關係
        3.1) 結構中必須存在惟一的根元素
        3.2) 除根元素外,結構中的每一個元素有且僅有一個前趨元素
        3.3) 除葉元素外,結構中的每一個元素擁有一到多個後繼元素
    4) 網絡結構: 網狀結構中的元素具備多對多的交叉映射關係
        4.1) 結構中的每一個元素均可以擁有任意數量的前趨和後繼元素
        4.2) 結構中的任意兩個元素之間都可創建關聯

2. 存儲結構(物理結構): 存儲結構是指數據結構在計算機中的表示(又稱映像),也稱物理結構。它包括數據元素的表示和關係的表示。數據的存儲結構是邏輯結構用計算機語言的實現,它依賴於計算機語言
    1) 順序存儲: 把邏輯上相鄰的元素存儲在物理位置上也相鄰的存儲單元裏,元素之間的關係由存儲單元的鄰接關係來體現
        1.1) 其優勢是能夠實現隨機存取,每一個元素佔用最少的存儲空間
        1.2) 缺點是隻能使用相鄰的一整塊存儲單元,所以可能產生較多的外部碎片(零散存儲區域碎片)
    2) 連接存儲: 不要求邏輯上相鄰的元素在物理位置上也相鄰,藉助指示元素存儲地址的指針表示元素之間的邏輯關係
        2.1) 其優勢是不會出現碎片現象,充分利用全部存儲單元
        2.2) 缺點是每一個元素因存儲指針而佔用額外的存儲空間,而且只能實現順序存取
    3) 索引存儲: 在存儲元素信息的同時,還創建附加的索引表。索引表中的每一項稱爲索引項,索引項的通常形式是: (關鍵字,地址)
        3.1) 其優勢是檢索速度快
        3.2) 缺點是增長了附加的索引表,會佔用較多的存儲空間
        3.3) 另外,在增長和刪除數據時要修改索引表,於是會花費較多的時間 
    4) 散列存儲: 根據元素的關鍵字直接計算出該元素的存儲地址,又稱爲Hash存儲
        4.1) 其優勢是檢索、增長和刪除結點的操做都很快
        4.2) 缺點是若是散列函數很差可能出現元素存儲單元的衝突,而解決衝突會增長時間和空間開銷 

3. 運算(算法): 施加在數據上的運算包括運算的定義和實現
    1) 運算的定義是針對邏輯結構的,指出運算的功能
    2) 運算的實現是針對存儲結構的,指出運算的具體操做步驟

1. 邏輯結構

邏輯結構是指數據元素之間的邏輯關係,即從邏輯關係上描述數據。它與數據的存儲無關,是獨立於計算機的
邏輯結構是咱們在學習數據結構時最重要的一個部分,本質上它是一種面向接口編程的範式思想,即無論底層物理存儲採用何種方式和算法,邏輯結構規定了該結構"必須"要遵照的規範(例如pop、push),這表現爲該數據結構的變化行爲,而底層的物理存儲方式解決了如何實現這些"接口範式"的需求

2. 存儲結構(物理結構)

數據的存儲結構是指數據的邏輯結構在計算機中的表示,順序存儲和連接存儲是數據的兩種最基本的存儲結構

1. 在順序存儲中,每一個存儲空間含有所存元素自己的信息,元素之間的邏輯關係是經過數組下標位置簡單計算出來的線性表的順序存儲,若一個元素存儲在對應數組中的下標位置爲i,則它的前驅元素在對應數組中的下標位置爲i-1,它的後繼元素在對應數組中的下標位置爲i+1
2. 在鏈式存儲結構中,存儲結點不只含有所存元素自己的信息,並且含有元素之間邏輯關係的信息,數據的鏈式存儲結構可用連接表來表示。
其中data表示值域,用來存儲節點的數值部分。Pl、p二、...、Pill(1n≥1)均爲指針域,每一個指針域爲其對應的後繼元素或前驅元素所在結點(之後簡稱爲後繼結點或前驅結點)的存儲位置。經過結點的指針域(又稱爲鏈域)能夠訪問到對應的後繼結點或前驅結點,若一個結點中的某個指針域不須要指向其餘結點,則令它的值爲空(NULL)
3. 在數據的順序存儲中,因爲每一個元素的存儲位置均可以經過簡單計算獲得,因此訪問元素的時間都相同
4. 而在數據的連接存儲中,因爲每一個元素的存儲位置保存在它的前驅或後繼結點中,因此只有當訪問到其前驅結點或後繼結點後纔可以按指針訪問到,訪問任一元素的時間與該元素結點在鏈式存儲結構中的位置有關 

在學習分析一個新的數據結構的時候,咱們須要從邏輯結構和物理結構這2個不一樣的層次去理解,全部的數據結構和算法本質上都能在這個大的框架中找到對應的位置,所不一樣的只是爲了解決特定領域的問題時,對通常化的數據結構附加了一些"額外的約束",使之表現出新的特性,同時解決了新的問題

3. 運算(算法)

所謂算法(Algorithm)是描述計算機解決給定問題的操做過程(解題方法),即爲解決某一特定問題而由若干條指令組成的有窮序列
一個算法必須知足如下五個準則

1. 有窮性: 執行了有限條指令後必定要終止 
2. 肯定性(無二義): 算法的每一步操做都必須有確切定義,不得有任何歧義性 
3. 可(能)行性: 算法的每一步操做都必須是可行的,即每步操做均能在有限時間內完成 
4. 輸入數據: 一個算法有n(n>=0)個初始數據的輸入 
5. 輸出數據: 一個算法有一個或多個與輸入有某種關係的有效信息的輸出 

Relevant Link:

http://baike.baidu.com/view/540423.htm
http://student.zjzk.cn/course_ware/data_structure/web/gailun/gailun1.1.1.htm
http://c.biancheng.net/cpp/html/2649.html
http://baike.baidu.com/view/2820182.htm

 

2. 邏輯結構實例

2.1 堆棧

0x1: 基於順序表的堆棧

#include <stdio.h>
#include <stdbool.h>
#define LISTSIZE 10
typedef int DataType;
struct Stack {
    DataType data[LISTSIZE];
    int top; //處了記錄大小  還能夠記錄棧頂位置
};
void init(struct Stack* stack)
{
    stack->top = 0;
}
bool empty(struct Stack* stack) {
    return stack->top == 0;
}
void push(struct Stack* stack, DataType d) {
    if (stack->top == LISTSIZE)
        return;
    stack->data[stack->top++] = d;
}
void pop(struct Stack* stack) {
    if (empty(stack))
        return;
    stack->top--;
}
DataType topData(struct Stack* stack) {
    return stack->data[stack->top - 1];
}
int main()
{
    struct Stack stack;
    init(&stack);
    push(&stack, 30);
    push(&stack, 60);
    push(&stack, 80);
    
    while (!empty(&stack)) {
        printf("%d ", topData(&stack));
        pop(&stack);
    }
    printf("\n");
    
    return 0;
}

0x2: 基於鏈式存儲結構的堆棧

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int DataType;
struct Stack {
    DataType data;
    struct Stack *next;
};
void init(struct Stack** top)
{
    *top = NULL;
}
bool empty(struct Stack* top)
{
    return top == NULL;
}
void push(struct Stack** top, DataType d)
{
    struct Stack *newNode = (struct Stack*)malloc(sizeof(struct Stack));
    newNode->data = d;
    newNode->next = *top;
    *top = newNode;
}
void pop(struct Stack** top)
{
    if (empty(*top))
        return;
    struct Stack *tempNode = *top;
    *top = (*top)->next;
    free(tempNode);
}
void topData(struct Stack* top, DataType* data)
{
    if (empty(top))
        return;
    *data = top->data;
}
int main()
{
    struct Stack *top;
    init(&top);
    push(&top, 30);
    push(&top, 60);
    push(&top, 80);
    
    while (!empty(top)) {
        int data;
        topData(top, &data);
        printf("%d ", data);
        pop(&top);
    }
    printf("\n");
    
    return 0;
}

0x3: STL Stack

C++ STL Stack(堆棧)是一個容器類的改編,爲程序員提供了堆棧的所有功能,——也就是說實現了一個先進後出(FILO)的數據結構。C++ STL棧stack的頭文件爲:

#include <stack> 

C++ STL棧stack的成員函數介紹

empty(): 堆棧爲空則返回真
pop(): 移除棧頂元素
push(): 在棧頂增長元素
size(): 返回棧中元素數目
top(): 返回棧頂元素

1. 代碼舉例1

#include "stdafx.h"  
#include <stack>  
#include <vector>  
#include <deque>  
#include <iostream>  
   
using namespace std;  
   
int _tmain(int argc, _TCHAR* argv[])  
{  
    deque<int> mydeque(2,100);  
    vector<int> myvector(2,200);  
   
    stack<int> first;  
    stack<int> second(mydeque);  
   
    stack<int,vector<int> > third;  
    stack<int,vector<int> > fourth(myvector);  
   
    cout << "size of first: " << (int) first.size() << endl;  
    cout << "size of second: " << (int) second.size() << endl;  
    cout << "size of third: " << (int) third.size() << endl;  
    cout << "size of fourth: " << (int) fourth.size() << endl;  
   
   
    return 0;  
}

2. 代碼舉例2

#include <iostream>  
#include <stack>  
using namespace std;  
   
int main ()  
{  
  stack<int> mystack;  
  int sum (0);  
   
  for (int i=1;i<=10;i++) mystack.push(i);  
   
  while (!mystack.empty())  
  {  
     sum += mystack.top();  
     mystack.pop();  
  }  
   
  cout << "total: " << sum << endl;  
     
  return 0;  
}

3. 代碼舉例3

#include <iostream>  
#include <stack>  
using namespace std;  
   
int main ()  
{  
  stack<int> mystack;  
   
  for (int i=0; i<5; ++i) mystack.push(i);  
   
  cout << "Popping out elements...";  
  while (!mystack.empty())  
  {  
     cout << " " << mystack.top();  
     mystack.pop();  
  }  
  cout << endl;  
   
  return 0;  
}

Relevant Link:

http://www.cplusplus.com/reference/stack/stack/
http://www.169it.com/article/2839007600903800247.html

2.2 隊列

0x1: 基於順序表的隊列

#include <stdio.h>
#include <stdbool.h>

#define QueueSize 6

typedef int DataType;
typedef struct _Queue2{
    DataType data[QueueSize];
    int front;
    int rear;
}Queue2;

void init(Queue2* q){
    q->front = q->rear = 0;
}

bool empty(Queue2* q){
    return q->rear == q->front;
}

/*
->              empty   full
-arraysize  ~   0   ~   arraysize
 */
bool full(Queue2* q){
    return q->rear - q->front == QueueSize;
}


bool push(Queue2* q, DataType d){
    if(full(q))
        return false;
    q->data[q->rear++ % QueueSize] = d;
    return true;
}

bool pop(Queue2* q){
    if(empty(q))
        return false;
    q->front++;
    return true;
}

DataType getFront(Queue2* q){
    return q->data[q->front % QueueSize];
}

int main(int argc, const char * argv[]){
    int i = 0;
    Queue2 q2;
    init(&q2);
    for (i = 0; i < 6; i++) {
        push(&q2, i);
    }
    pop(&q2);
    push(&q2, 300);
    pop(&q2);
    pop(&q2);
    push(&q2, 200);
    while (!empty(&q2)) {
        printf("%d ", getFront(&q2));
        pop(&q2);
    }
    printf("\n");
                                    
    return 0;
}

0x2: 基於鏈式表的隊列

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef int DataType;
typedef struct _Queue{
    DataType data;
    struct _Queue* next;
}Queue;

void init(Queue** front, Queue** rear){
    *front = NULL;
    *rear = NULL;
}

bool empty(Queue* node){
    return node == NULL;
}

void push(Queue** front, Queue** rear, DataType d){
    Queue* newNode = (Queue*)malloc(sizeof(Queue));
    newNode->data = d;
    newNode->next = NULL;

    if(!empty(*rear))
        (*rear)->next = newNode;
    *rear = newNode;
    //若是隊尾指針爲空,則表示此時隊中沒有元素,因此直接將隊尾指針指向新建立的隊元素節點便可
    if(empty(*front))
        *front = *rear;
}

void pop(Queue** front, Queue** rear){
    if(empty(*rear))
        return;
    Queue* tempNode = *front;
    *front = (*front)->next;
    free(tempNode);
    //判斷刪除一個隊元素後,隊是否爲空,若是隊中沒有元素了,也應該將隊尾指針置爲空,不然隊尾指針將是野指針
    if(empty(*front))
        *rear = NULL;
}

void topData(Queue* front, DataType* data){
    if(empty(front))
        return;
    *data = front->data;
}



int main(){
    Queue* front, *rear;
    init(&front, &rear);
    push(&front, &rear, 30);
    push(&front, &rear, 60);
    push(&front, &rear, 80);
                
    while (!empty(front)) {
        int data;
        topData(front, &data);
        printf("%d ", data);
        pop(&front, &rear);
    }
    printf("\n");

    return 0;
}

0x3: STL Queue

queue模板類的定義在<queue>頭文件中
與stack模板類很類似,queue模板類也須要兩個模板參數,一個是元素類型,一個容器類型,元素類型是必要的,容器類型是可選的,默認爲deque類型
queue的基本操做有

push入隊,q.push(x); 將x接到隊列的末端 
pop: 出隊,q.pop(); 彈出隊列的第一個元素,注意,並不會返回被彈出元素的值 
front: 訪問隊首元素,q.front(),即最先被壓入隊列的元素
back: 訪問隊尾元素,q.back(),即最後被壓入隊列的元素 
empty: 判斷隊列空,q.empty(),當隊列空時,返回true
size: 返回隊列中的元素個數,q.size()

1. 示例代碼1

#include <cstdlib>
#include <iostream>
#include <queue>
using namespace std;
int main()
{
    int e,n,m;
    queue<int> q1;
    for(int i=0;i<10;i++)
       q1.push(i);
    if(!q1.empty())
    cout<<"dui lie  bu kong\n";
    n=q1.size();
    cout<<n<<endl;
    m=q1.back();
    cout<<m<<endl;
    for(int j=0;j<n;j++)
    {
       e=q1.front();
       cout<<e<<" ";
       q1.pop();
    }
    cout<<endl;
    if(q1.empty())
    cout<<"dui lie  bu kong\n";
    system("PAUSE");
    return 0;
}

2. 示例代碼2

#include <iostream>
#include <queue>
#include <assert.h>
/*
調用的時候要有頭文件: #include<stdlib.h> 或 #include<cstdlib> +
#include<queue>       #include<queue>
詳細用法:
定義一個queue的變量     queue<Type> M
查看是否爲空範例        M.empty()    是的話返回1,不是返回0;
從已有元素後面增長元素   M.push()
輸出現有元素的個數      M.size()
顯示第一個元素          M.front()
顯示最後一個元素        M.back()
清除第一個元素          M.pop()
*/
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
    queue <int> myQ;
    cout<< "如今 queue 是否 empty? "<< myQ.empty() << endl; 
    for(int i =0; i<10 ; i++)
    {
        myQ.push(i);
    }
    for(int i=0; i<myQ.size(); i++)
    {
        printf("myQ.size():%d\n",myQ.size());
        cout << myQ.front()<<endl;
        myQ.pop();
    }
    system("PAUSE"); 
    return 0;
}

Relevant Link:

http://www.cnblogs.com/LittleHann/p/4392061.html
http://blog.csdn.net/wangshihui512/article/details/8930652
http://www.cplusplus.com/reference/queue/queue/
http://www.169it.com/article/2718050585107790752.html

2.3  樹形結構

2.3.1 二叉樹

0x1: 邏輯定義

1. 在計算機中,二叉樹是指每一個節點最多隻有兩個子節點的樹形結構
2. 其中起始節點叫作根節點,整棵二叉樹只有一個根節點。除了根節點以外,每一個節點都有一個惟一的父節點
3. 其中沒有任何子節點的節點叫作葉子節點,除了葉子節點以外,每一個節點最多有兩個子節點,即葉子節點只有父節點沒有子節點
4. 除了根節點和葉子節點以外,剩下的節點叫枝節點,枝節點既有父節點也有子節點

5. 滿二叉樹: 若是該二叉樹中每層節點均達到最大值,即每一個枝節點都有兩個子節點,則該二叉樹叫滿二叉樹
6. 徹底二叉樹: 若是該二叉樹中除了最下面一層以外,每層節點個數均達到最大值,而且最下面一層的節點都連續集中在左側,則該二叉樹叫作徹底二叉樹
7. 有序二叉樹: 知足下列條件的非空二叉樹即爲有序二叉樹
    1) 若左子樹非空,則左子樹上全部節點的值均小於等於根節點(傳遞性)
    2) 若右子樹非空,則右子樹上全部節點的值均大於等於根節點(傳遞性)
    3) 左右子樹亦分別爲有序二叉樹(遞歸)

8. 二叉樹具備遞歸嵌套的空間結構,也就是說對於一棵二叉樹來講,能夠拆分爲若干個小二叉樹組成,由於這種邏輯結構,採用遞歸的方法處理二叉樹比較方便
if(空樹) 直接處理完畢
else
{
    處理根節點;
    處理左子樹; => 遞歸
    處理右子樹; => 遞歸
}

二叉樹是一種邏輯結構的概念,在物理存儲層面上,能夠採用順序/鏈式方式存儲

0x2: 物理存儲方式

1. 順序存儲結構: 通常來講,從上到下,從左到右依次存儲節點,對於非徹底二叉樹來講,採用虛節點來補全二叉樹
2. (二叉鏈表)鏈式存儲結構: 通常來講,每一個節點中除了存儲數據元素自己以外,還須要兩個指針,分別記錄左右子節點的地址
typedef struct Node{
    int data;
    struct Node* left;
    struct Node* right;
}

3. (三叉鏈表)鏈式存儲結構: 每一個節點包括四個域
    1) 數據域
    2) 兩個分別指向其左右子節點的指針域
    3) 指向其父節點的指針域

0x3: 基本操做(算法)

1. 建立
2. 銷燬
3. 插入新元素
4. 刪除元素
5. 查找指定的元素
6. 修改指定的元素
7. 判斷二叉樹是否爲滿
8. 判斷二叉樹是否爲空
9. 計算二叉樹節點的個數
10. 獲取根節點元素值
11. 遍歷
    1) 前序遍歷: 對於從樹根開始的每一課子樹,先處理根節點中的數據,而後處理它的左子樹,最後處理它的右子樹,簡單表示爲DLR
    2) 中序遍歷: 對於從樹根開始的每一棵子樹,先處理它的左子樹,而後處理根節點中的數據,最後處理它的右子樹,簡單表示爲LDR
    3) 後序遍歷: 對於從樹根開始的每一棵子樹,先處理它的左子樹,而後處理它的右子樹,最後處理根節點中的數據,簡單表示爲LRD

1. 有序二叉樹

1. 若左子樹非空,則左子樹上全部節點的值均小於根節點的值
2. 若右子樹非空,則右子樹上全部節點的值均大於等於根節點
3. 左右子樹也分別遞歸地爲有序二叉樹

用途

1. 排序: 不管以何種順序構建有序二叉樹,其中序遍歷的結果必定是一個有序序列
2. 搜索
    1) 若搜索目標與根節點的值相等,則搜索成功,不然用搜索目標和根節點的值比較大小
    2) 若搜索目標小於根節點的值,則在根節點的左子樹中繼續搜索,不然在根節點的右子樹中繼續搜索
    3) 以遞歸的方式重複以上過程,知道搜索成功,或因子樹不存在而宣告失敗
    4) 基於有序二叉樹的搜索,可達到對數級的平均時間複雜度

bst.c

#include <stdio.h>
#include <stdlib.h>

typedef int DataType;
typedef struct _Node{
    DataType data;
    struct _Node* left;
    struct _Node* right;
}Node;

Node* createNode(DataType d){
    Node* pn = (Node*)malloc(sizeof(Node));
    pn->data = d;
    pn->left = pn->right = NULL;
    return pn;
}

void insert(Node** root, Node* pn){
    if(pn == NULL)
        return;
    if(*root == NULL){
        *root = pn;
        return;
    }
    if(pn->data > (*root)->data){
        insert(&(*root)->right, pn);
    }
    else{
        insert(&(*root)->left, pn);
    }
}

void delete(Node** root, DataType k){
    Node* p, *f, *s, *q;
    p = *root;
    f = NULL;
    //search the node whose data == k
    while(p){
        if(p->data == k)
            break;
        //f is point to p's parent
        f = p;
        if(p->data > k)
            p = p->left;
        else
            p = p->right;
    }
    //when break to here, means have benn find out the node
    if(p == NULL)
        return;
    if(p->left == NULL){
        //p is the root
        if(f == NULL)
            *root = p->right;
        else if(f->left == p)
            f->left = p->right;
        else
            f->right = p->right;
        free(p);
    }
    else{
        q = p;
        s = p->left;
        while(s->right){
            q = s;
            s = s->right;
        }
        if(q == p)
            q->left = s->left;
        else
            q->right = s->left;
        p->data = s->data;
        free(s);
    }
}

Node* find(Node* root,DataType d){
    if(root == NULL)
        return NULL;
    if(d > root->data)
        return find(root->right, d);
    else if(d < root->data)
        return find(root->left, d);
    else
        return root;
}

void modify(Node* root, DataType oldData, DataType newData){
    Node* p;
    p = find(root, oldData);
    p->data = newData;
}

void clears(Node** root){
    if(*root == NULL)
        return;
    clears(&(*root)->left);
    clears(&(*root)->right);
    free(*root);
    *root = NULL;
}

void print(Node* root) {
    if (root == NULL) return;
        printf("%d ", root->data);
        print(root->left);
        print(root->right);
}

int main() {
    Node* root = NULL;
    insert(&root, createNode(10));
    insert(&root, createNode(5));
    insert(&root, createNode(3));
    insert(&root, createNode(2));
    insert(&root, createNode(1));
    insert(&root, createNode(4));
    insert(&root, createNode(7));
    insert(&root, createNode(6));
    insert(&root, createNode(9));
    insert(&root, createNode(8));
    insert(&root, createNode(15));
    print(root);
    printf("\n");

    printf("%d\n", find(root, 5)->data);

    delete(&root, 10);
    print(root);
    return 0;
}

 

 

3. 物理結構實例

3.1 鏈表

0x1:  鏈表的基本運算

1. 追加: 在鏈表的尾部添加新元素
2. 插入: 在特定位置的元素以前或以後加入新元素
3. 刪除: 刪除特定位置的元素
4. 遍歷: 依次訪問鏈表中的每一個元素,不重複、不遺漏

3.1.1  單向線性鏈表

0x1:  定義

鏈表(Linked List)是由節點組成的(node)。而節點實質上是一個數據結構(struct or class)。鏈表和數組(Array)的區別在於鏈表中的節點在內存中的位置不必定是連續的,而且節點個數無需在編譯時肯定
節點包含兩種信息

1. 一種是數據(data)
2. 一種是指向另外一個節點的指針(指針實質上存儲的是節點的物理位置)
3. 鏈表尾節點的指針爲空指針

鏈表結構定義

struct student
{
    long num; /*學號 */
    float score; /*分數,其餘信息能夠繼續在下面增長字段 */
    struct student *next; /*指向下一結點的指針 */
};

0x2: 操做

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

typedef int DataType;
typedef struct _Node{
    DataType data;
    struct _Node *next;
}Node;

void init(Node** head){
    *head = NULL;
}

int getSize(Node *head){
    Node* p = head;
    int count = 0;
    while(p){
        count++;
        p = p->next;
    }
    return count;
}

Node* getptr(Node* head, int pos){
    Node* p = head;
    int i = 0;
    if(p == 0 || pos == 0){
        return head;
    }
    for(i = 0; p && i < pos; i++){
        p = p->next;
    }
    return p;
}

bool insert(Node** head, int position, DataType d){
    if(position < 0 || position > getSize(*head)){
        return false;
    }
    //create node
    Node* node = (Node*)malloc(sizeof(Node));
    node->data = d;
    node->next = NULL;

    //insert before the first node
    if(position == 0){
        node->next = *head;
        *head = node;
        return true;
    }

    //insert between the node linktable
    Node* p = getptr(*head, position - 1);
    Node* r = p->next;
    node->next = r;
    p->next = node;

    return true;
}

bool erases(Node** head, int pos){
    if(pos < 0 || pos >= getSize(*head))
        return false;
    Node* p = *head;
    if(pos == 0){
        *head = (*head)->next;
        free(p);
        p = NULL;
        return true;
    }
    p = getptr(*head, pos - 1);
    Node* q = p->next;
    p->next = q->next;
    free(q);
    q = NULL;

    return true;
}

bool set(Node* head, int pos, DataType d){
    if(pos < 0 || pos >= getSize(head))
        return false;
    Node* p = getptr(head, pos);
    p->data = d;
    return true;
}

void clears(Node* head){
    while(head){
        Node* p = head->next;
        free(head);
        head = p;
    }
}

void print(Node* head) {
    Node *p = head;
    while (p) {
        printf("%d ", p->data);
        p = p->next;
                                    }
        printf("\n");
}

int main(){
    Node* headList;
    init(&headList);

    insert(&headList, 0, 10);
    insert(&headList, 0, 20);
    insert(&headList, 0, 30);
    insert(&headList, 2, 40);
    insert(&headList, 2, 50);
    insert(&headList, 0, 60);
    insert(&headList, 0, 80);
    print(headList);
    erases(&headList, 0);
    print(headList);
    set(headList, 0, 100);
    set(headList, 0, 110);
    print(headList);

    return 0;
}

3.1.2 單向循環鏈表

0x1: 定義

1. 每一個節點依舊跟單向鏈表同樣,包含一個指向下一節點的指針
2. 最後一個節點再也不指向NULL,指向第一個節點,構成環狀
3. 單向循環鏈表能夠從任意一個節點出發遍歷整個鏈表

3.1.3 雙向線性鏈表

0x1: 定義

由一系列內存中不連續的節點組成,每一個節點除了保存數據之外,還須要保存其先後節點的地址,鏈表首節點的前指針和尾節點的後指針都爲NULL,線性和循環的區別在於線性須要一個額外的head頭指針來進行遍歷

typedef struct Node  
{  
    int data;  
    struct Node *pNext;  
    struct Node *pPre;  
}NODE, *pNODE;  

0x2: 操做

1. 在給定節點以前插入
    1) 建立持有待插入元素的新節點,令其前指針指向給定結點的前節點,令其後指針指向給定結點
    2) 若新建結點存在前節點,則令其前節點的後指針指向新建結點,不然新建節點爲新的首節點
    3) 令新建結點的後節點的前指針指向新建節點
2. 刪除節點
    1) 若待刪除節點存在前節點,則令其前節點的後指針指向待刪除節點的後節點(鏈節點合併),不然待刪除節點的後節點爲新的首節點
    2) 若待刪除節點存在後節點,則令其後節點的前指針指向待刪除節點的前節點,不然待刪除節點的前節點爲新的尾節點
    3) 銷燬待刪除節點

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

typedef int DataType;
typedef struct _Node{
    DataType data;
    struct _Node* pre, *next;
}Node;

void init(Node** head){
    *head = NULL;
}

int getSize(Node* head){
    Node* p = head;
    int count = 0;
    while(p){
        count++;
        p = p->next;
    }
    return count;
}

Node* getptr(Node* head, int pos){
    Node* p = head;
    int i = 0;
    if(p == 0 || pos == 0){
        return head;
    }
    for(i = 0; p && i < pos; i++){
        p = p->next;
    }
    return p;
}

bool insert(Node** head, int position, DataType d){
    if(position < 0 || position > getSize(*head)){
        return false;
    }
    //create node
    Node* node = (Node*)malloc(sizeof(Node));
    node->data = d;
    node->pre = NULL;
    node->next = NULL;

    //insert before the first node
    if(position == 0){
        node->next = *head;
        if(*head != NULL)
            (*head)->pre = node;
        *head = node;
        return true;
    }

    //insert between into the node linktable
    Node* p = getptr(*head, position - 1);
    Node* r = p->next;
    node->next = r;
    r->pre = node;
    p->next = node;
    node->pre = p;

    return true;
}
    
bool erases(Node** head, int pos){
    if(pos < 0 || pos >= getSize(*head))
        return false;
    
    //erase the first node
    Node* p = *head;
    if(pos == 0){
        *head = (*head)->next;
        if(*head != NULL)
            (*head)->pre = NULL;
        free(p);
        p = NULL;
        return true;
    }

    //erase the node between the node linktable
    p = getptr(*head, pos - 1);
    Node* q = p->next;
    p->next = q->next;
    q->next->pre = p;
    free(q);
    q = NULL;

    return true;
}    

bool set(Node* head, int pos, DataType d){
    if(pos < 0 || pos >= getSize(head)){
        return false;
    }
    Node* p = getptr(head, pos);
    p->data = d;
    return true;
}

void clears(Node* head){
    while(head){
        Node* p = head->next;
        free(head);
        head = p;
    }
}

void print(Node* head) {
    Node *p = head;
    while (p) {
        printf("%d ", p->data);
        p = p->next;
    }
    printf("\n");
}

int main(){
    //head point
    Node* headList;
    init(&headList);

    insert(&headList, 0, 10);
    insert(&headList, 0, 20);
    insert(&headList, 0, 30);
    insert(&headList, 2, 40);
    insert(&headList, 2, 50);
    insert(&headList, 0, 60);
    insert(&headList, 0, 80);
    print(headList);
    erases(&headList, 1);
    print(headList);
    set(headList, 0, 100);
    set(headList, 0, 110);
    print(headList);
                                                        
    return 0;
}

3.1.4 雙向循環鏈表

0x1: 定義

1. 每一個節點除了存放元素數據之外,還須要保存指向下一個節點的指針,即所謂的後指針,以及指向前一個節點的指針,即所謂的前指針
2. 鏈表首節點的前指針指向鏈表尾節點,尾節點的後指針指向首節點

3.1.5 數組鏈表

0x1: 定義

鏈表中的每一個元素都是一個數組,即由數組構成的鏈表

3.1.6 鏈表數組

0x1: 定義

數組中的每個元素都是一個鏈表,即由鏈表構成的數組

PHP內核的Hashtable就是採用這種物理存儲結構

3.1.7 二維鏈表

0x1: 定義

鏈表中的每個元素都一個鏈表

3.2  順序存儲

使用數組進行存儲

 

4. 算法

0x1: 基本概念

1. 算法就是對解決問題的方案進行準確而完整的描述,是一系列解決問題的清晰指令,算法表明着用系統的方法描述解決問題的策略機制
2. 算法中的指令描述的是一個計算過程,在它的做用下,系統從初始狀態和初始輸入(容許爲空)開始,經歷一些列有限且被明肯定義的中間狀態,最終產生所要求的輸出,並中止於終止狀態
3. 同一個問題,不一樣的算法,可能會在時間、空間等方面表現出明顯的差別,一個算法的優劣能夠用時間複雜度和空間複雜度來衡量

0x1: 基本特徵

1. 有窮性: 算法必須能在執行有限個步驟後終止
2. 確切性: 算法的每一個步驟都必須有確切的定義(DFN 有窮狀態機)
3. 輸入項: 算法有規範化的輸入,以表示運算對象的初始狀態
4. 輸出項: 算法至少有一個輸出,以反映處理的最終結果
5. 可行性: 算法中的每一個執行步驟都必須能在有限時間內完成

0x3: 基本要素

1. 運算和操做: 計算機能夠執行的基本操做是以指令的形式描述的,包括如下
    1) 算法運算
        1.1) 加
        1.2) 減
        1.3) 乘
        1.4) 除
        1.5) 模
        ..
    2) 關係運算
        2.1) 大於
        2.2) 小於
        2.3) 等於
        2.4) 不等於
        ..
    3) 邏輯運算
        3.1) 與
        3.2) 或
        3.3) 非
        ..
    4) 數據傳輸
        4.1) 輸入
        4.2) 輸出
        4.3) 賦值
2. 流程和控制: 算法的功能不只取決於所選用的操做,還與各操做之間的執行順序有關

0x4: 算法的評定標準

1. 時間複雜度: 算法的時間消耗與問題規模之間的函數關係: T(N) = O(F(N))
    1) 常數級複雜度: O(1)
    2) 對數級複雜度: O(logN)
    3) 線性級複雜度: O(N)
    4) 線性對數級複雜度: O(NlogN)
    5) 平方級複雜度: O(N2)
2. 空間複雜度: 算法的空間消耗與問題規模之間的函數關係: S(N) = O(F(N))
//原則上說,複雜度曲線越平越好,越陡越差,常數級複雜度最爲理想
3. 正確性: 執行算法的結果是否知足要求
4. 可讀性: 算法自己可供人們閱讀的難易程度
5. 健壯性: 算法對非正常輸入的反應和處理能力,也稱容錯性

0x5: 算法的思想方法

1. 遞推法
    1) 經過計算前面的一些項來得出序列中指定項的值
    2) 其思想是把一個複雜而龐大的計算過程轉化爲簡單過程的屢次重複
2. 遞歸法
    1) 把大問題轉化爲原問題類似的小問題來求解
    2) 遞歸的神奇之處在於用有限的語句來定義無限的對象的集合
    3) 遞歸須要有終止條件、遞歸前進段和遞歸返回段,若終止條件不知足則遞歸前進,不然遞歸返回
3. 窮舉法
    1) 對於要解決的問題,列舉出它全部的可能性,逐個判斷其中哪些符合問題所要求知足的條件,從而獲得問題的解
4. 貪心算法
    1) 自頂向下,以迭代的方式作出相繼的貪心選擇,沒作一次貪心選擇,就將所求問題簡化爲一個規模更小的子問題,經過每一步貪心選擇,可獲得相應子問題的一個最優解,雖然每一步都可以保證局部最優解,但最終獲得的全局解未必最優
5. 分治法
    1) 把一個複雜問題分紅兩個或更多相同或類似的子問題,再把子問題分紅更小的子問題,知道最後的子問題能夠簡單地直接求解,原問題的解即子問題解的合併
6. 動態規劃法
    1) 將原問題分解爲類似的子問題,在求解的過程當中經過子問題的解求出原問題的解
7. 迭代法
    1) 按照必定的規則,不斷地用舊值推算出新值,直到知足某種特定條件爲止 
8. 分支界限法
    1) 把所有可行的解空間不斷分隔爲愈來愈小的子集,稱之爲分支,併爲每一個子集計算一個下界或上界,稱之爲定界
    2) 在每次分支後,對界限超出已知可行解的子集再也不作進一步分支,搜索範圍迅速收斂
    3) 這一過程一直進行到找出可行解爲止,該可行解的值不大於任何子集的界限,所以這種算法通常能夠求得全局最優解
9. 回溯法
    1) 在包含問題全部解的解空間樹中,從根節點出發,按深度優先搜索解空間樹,當搜索到某一節點時,先判斷該節點是否包含問題的解,若包含則從該節點出發繼續搜索,不然逐層向其父節點回溯
    2) 若但願求得問題的全部解,則必須回溯到根,且根節點的所全部可行子樹都必須被搜索到才能結束,不然只要搜索到問題的一個解便可終止

0x6: 算法的分類

1. 按照執行期限分類
    1) 有限算法: 算法過程不管長短,總能在有限時間內終止
    2) 無限算法: 算法過程因無終止條件或終止條件沒法獲得知足,而永不停息
2. 按照解的肯定性分類
    1) 肯定性算法: 對於肯定的輸入,算法總能獲得肯定的結果
    2) 非肯定算法: 對於肯定的輸入,算法獲得的結果並不惟一肯定
3. 按照理論和應用的專門領域分裂
    1) 基本算法: 數值分析算法
    2) 排序算法: 數據結構算法
    3) 搜索算法: 動態規劃算法
    4) 代數算法: 壓縮解壓算法
    5) 幾何算法: 加密解密算法
    6) 數論算法: 數據摘要算法
    7) 圖論算法: 數據挖掘算法
    8) 隨機算法: 隨機森林算法
    9) 並行算法: 人工智能算法
    10) 分類算法: 模式識別算法

4.1 查找算法

1. 線性查找

算法描述

1. 從頭開始,依次將每個元素與查找目標進行比較
2. 或者找到目標,或者找不到目標

僞碼實現

for each item in the list
    if that item has the desired value
        stop the search and return the item's location
return not found

整體評價

1. 平均時間複雜度: O(N)
2. 對數據的有序性沒有要求

實例代碼

#include <stdio.h>

typedef char DataType;
int mySearch(DataType* ts, int n, const DataType d){
    int i  = 0;
    for(i = 0; i < n; i++){
        if(ts[i] == d)
            return i;
    }
    return -1;
}

int main(){
    char cs[6] = {'*', 'A', 'B', 'C', 'D', 'E'};
    printf("%d\n", mySearch(cs, 6, '*'));
    printf("%d\n", mySearch(cs, 6, 'A'));
}

2. 二分查找

算法描述

1. 假設表中的元素按升序排列
2. 若中間元素與查找目標相等,則查找成功,不然利用中間元素將表劃分紅先後兩個子表
3. 若查找目標小於中間元素,則在前子表中查找,不然在後子表中查找
4. 重複以上過程,直到查找成功,或者因子表不存在而宣告失敗

僞碼實現

基於遞歸的二分查找
function binarySearch(a, value, left, right)
    if right < left
        return not found
    mid := floor( (right - left) / 2 ) + left
    if a[mid] == value
        return mid
    if value < a[mid]
        return binarySearch(a, value, left, mid - 1)
    else
        return binarySearch(a, value, mid + 1, right)

基於循環的二分查找
function binarySearch(a, value, left, right)
    while left <= right
        mid := floor( (right - left) / 2 ) + left
        if a[mid] == value
            return mid
        if value < a[mid]
            right := mid - 1
        else
            left := mid + 1
    return not found

整體評價

1. 平均時間複雜度: O(logN)
2. 數據必須有序

實例代碼

#include <stdio.h>

typedef char DataType;
int mySearch(DataType *ts, int n, DataType d){
    int L = 0;
    int R = n - 1;
    while(L <= R){
        int M = (L + R) / 2;
        if(ts[M] == d)
            return M;
        if(ts[M] < d)
            L = M + 1;
        else R = M - 1;
    }
    return -1;
}

int main()
{
    char cs[6] = {'*','A','B','C','D','E'};
    printf("%d\n", mySearch(cs, 6, '*'));
    printf("%d\n", mySearch(cs, 6, 'A'));
    printf("%d\n", mySearch(cs, 6, 'D'));
    printf("%d\n", mySearch(cs, 6, 'C'));
}

4.2 排序算法

1. 冒泡排序

算法描述

1. 相鄰元素兩輛比較,前者大於後者,彼此交換
2. 從第一對到最後一對,最大的元素沉降到最後
3. 針對未排序部分,重複以上步驟,沉降次最大值
4. 每次掃描愈來愈少的元素,直至再也不發生交換

僞碼實現

procedure bubbleSort(A:list of sortable items)
    n = length(A)
    repeat
        swapped = false
        for i = 1 to n-1 inclusive do
            if A[i-1] > A[i] then    
                swap(A[i-1], A[i])
                swapped = true
            end if
        end for
        n = n - 1
    until not swapped
end procedure

整體評價

1. 平均時間複雜度: O(N2)
2. 穩定排序
3. 對數據的有序性很是敏感(swap的過程須要消耗時間)

代碼示例

#include <stdio.h>
#include <stdbool.h>

typedef int DataType;

void bubble(DataType* a, int n){
    int i = 0, j = 0;
    for(i = 0; i < n-1; i++){
        bool flag = true;
        for(j = 0; j < n-i-1; j++){
            if(a[j] > a[j+1]){
                DataType t = a[j];
                a[j] = a[j+1];
                a[j+1] = t;
                flag = false;
            }
        }
        if(flag)
            break;
    }
}

void print(DataType* a, int n) {
    int i = 0;
    for (i = 0; i < n; i++)
        printf("%d ", a[i]);
    printf("\n");
}

int main() {
    int a[10] = {3, 2, 4, 5, 7, 8, 9, 1, 6, 0};
    bubble(a, 10);
    print(a, 10);

    return 0;
}

2. 插入排序

算法描述

1. 首元素天然有序
2. 取出下一個元素,對已排序序列,從後向前掃描
3. 大於被取出元素者後移
4. 小於等於被取出元素者,將被取出元素插入其後
5. 重複步驟2,直至處理完全部元素

僞碼實現

for i = 1 to length(A)
    x = A[i]
    j = i
    while j > 0 and A[j-1] > x
        A[j] = A[j-1]
        j = j - 1
    A[j] = x

整體評價

1. 平均時間複雜度: O(N2)
2. 穩定排序
3. 對數據的有序性很是敏感(即有序性影響最終的交換次數)
4. 不交換隻移動,優於冒泡排序

代碼示例

#include <stdio.h>
#include <stdbool.h>

typedef int DataType;

void insert(DataType* a, int n){
    int i = 0;
    for(i = 0; i < n; i++){
        DataType t = a[i];
        int j = 0;
        for(j = i; j > 0 && a[j-1] > t; j--){
            a[j] = a[j-1];
        }
        a[j] = t;
    }
}

void print(DataType* a, int n) {
    int i = 0;
    for (i = 0; i < n; i++)
        printf("%d ", a[i]);
    printf("\n");
}
int main() {
    int a[10] = {3, 2, 4, 5, 7, 8, 9, 1, 6, 0};
    insert(a, 10);
    print(a, 10);

    return 0;
}

3. 選擇排序

算法描述

1. 在整個序列中尋找最小元素,與首元素交換
2. 在剩餘序列中尋找最小元素,與次首元素交換
3. 以此類推,直到剩餘序列中僅包含一個元素

僞碼實現

function selectionSort(list[1..n])
    for i from 1 to n-1
        minIndex = i
        for j from i+1 to n
            if list[j] < list[minIndex]
                minIndex = j
        swap list[i] and list[minIndex]

整體評價

1. 平均時間複雜度: O(N2)
2. 非穩定排序(相同元素在排序過程當中可能會改變順序)
3. 對數據的有序性不敏感(必須全量遍歷完)
4. 交換次數少,優於冒泡排序

代碼示例

#include <stdio.h>
#include <stdbool.h>
#include <algorithm>

typedef int DataType;

void selects(DataType* a, int n){
    int i = 0;
    for(i = 0; i < n; i++){
        int k = i;
        int j = 0;
        for(j = i + 1; j < n; j++){
            if(a[j] < a[k]){
                k = j;
            }
        }
        if(k != i){
            std::swap(a[k], a[i]);
        }
    }
}

void print(DataType* a, int n) {
    int i = 0;
    for (i = 0; i < n; i++)
        printf("%d ", a[i]);
    printf("\n");
}
int main() {
    int a[10] = {3, 2, 4, 5, 7, 8, 9, 1, 6, 0};

    selects(a, 10);
    print(a, 10);
    return 0;
}

4. 快速排序

算法描述

1. 從待排序序列中任意挑選一個元素,做爲基準
2. 將全部小於基準的元素放在基準以前,大於基準的元素放在基準以後,等於基準的元素放在基準以前或以後,這個過程稱爲分組
3. 以遞歸的方式,分別對基準以前和基準以後的分組繼續進行分組,直到每一個分組內的元素個數很少於1爲止

就地分組

1. 在不額外分配內存空間的前提下,完成分組過程

僞碼實現

基於分組的排序
quickSort(A, i, k):
    if i < k:
        p := partition(A, i, k)
        quickSort(A, i, p-1)
        quickSort(A, P+1, K)

就地分組
partition(array left, right)
    pivotIndex := choosePivot(array, left, right)
    pivotValue := array(pivotIndex)
    i = left
    j = right
    while i < j
        while i < pivotIndex and array[i] <= pivotValue
            i = i + 1
        if i < pivotIndex
            array[pivotIndex] = array[i]
            pivotIndex = i
        while j > pivotIndex and array[j] >= pivotValue
            j = j - 1
        if j > pivotIndex
            array[pivotIndex] = array[j]
            pivotIndex = j
    array[pivotIndex] = pivotValue
    return pivotIndex

整體評價

1. 平均時間複雜度: O(NlogN)
2. 非穩定排序
3. 若每次都能均勻分組,則排序速度最快

實例代碼

#include <stdio.h>
#include <stdbool.h>

typedef int DataType;

void qsorts(DataType* a, int n){
    if(n <= 1)
        return;
    int L = 0;
    int R = n - 1;
    while(L < R){
        //R backing
        while(L < R && a[L] <= a[R])
            R--;
        DataType t = a[L];
        a[L] = a[R];
        a[R] = t;
        //L going
        while(L < R && a[L] <= a[R])
            L++;
        t = a[L];
        a[L] = a[R];
        a[R] = t;
    }
    qsorts(a, L);
    qsorts(a+L+1, n-L-1);
}

void print(DataType* a, int n) {
    int i = 0;
    for (i = 0; i < n; i++)
        printf("%d ", a[i]);
    printf("\n");
}
int main() {
    int a[10] = {3, 2, 4, 5, 7, 8, 9, 1, 6, 0};

    qsorts(a, 10);
    print(a, 10);

    return 0;
}

5. 歸併排序

算法描述

1. 將待排序序列從中間劃分爲兩個相等的子序列
2. 以遞歸的方式分別對兩個子序列進行排序
3. 將兩個有序的子序列合併成完整序列

有序合併

1. 分配合並序列,其大小爲兩個有序序列大小之和
2. 設定兩個指針,分別指向兩個有序序列的首元素
3. 比較指針目標,較小者進入合併序列,指針後移
4. 重複步驟3,直到某一指針到達序列末尾
5. 將另外一序列的剩餘元素直接複製到合併序列末尾

僞碼實現

基於合併的排序
function mergeSort(list m)
    if length(m) <= 1
        return ,
    var list left, right
    var integer middle = length(m) / 2
    for each x in m before middle
        add x to left
    for each x in m after or equal middle
        add x to right
    left = mergeSort(left)
    right = mergerSort(right)
    return merge(left, right)

有序合併
function merge(left, right)
    var list result
    while length(left) > 0 or length(right) > 0
        if length(left) > 0 and length(right) > 0
            if first(left) <= first(right)
                append first(left) to result
                left = rest(left)
            else
                append first(right) to result
                rigth = rest(right)
        else if length(left) > 0
            append first(left) to result
            left = rest(left)
        else if length(right) > 0
            append first(right) to result
            right = rest(right)
    end while
    return result

整體評價

1. 平均時間複雜度: O(2NlogN)
2. 穩定排序
3. 對數據的有序性不敏感
4. 非就地排序,須要輔助空間,不適合處理海量數據

代碼示例

#include<stdlib.h>
#include<stdio.h>
#define SIZE 8

void Merge(int sourceArr[], int startIndex, int midIndex, int endIndex){
    int start = startIndex;
    int i, j, k;
    int tempArr[SIZE];
    for(i=midIndex+1, j=startIndex; startIndex <= midIndex && i<=endIndex; j++){
        if(sourceArr[startIndex] <= sourceArr[i])
            tempArr[j] = sourceArr[startIndex++];
        else
            tempArr[j] = sourceArr[i++];
    }
    if(startIndex <= midIndex){
        for(k = 0; k <= midIndex - startIndex; k++){
            tempArr[j+k] = sourceArr[i+k];
        }
    }
    for(i=start; i<=endIndex; i++)
        sourceArr[i] = tempArr[i];
}

void MergeSort(int sourceArr[], int startIndex, int endIndex){
    int midIndex;
    if(startIndex < midIndex){
        midIndex = (startIndex + endIndex) / 2;
        MergeSort(sourceArr, startIndex, midIndex);
        MergeSort(sourceArr, midIndex + 1, endIndex);
    }
}

void print(int* a, int n) {
    int i = 0;
    for (i = 0; i < n; i++)
        printf("%d ", a[i]);
    printf("\n");
}
int main() {
    int a[10] = {3, 2, 4, 5, 7, 8, 9, 1, 6, 0};

    MergeSort(a, 0, 10);
    print(a, 10);

    return 0;
}

 

Copyright (c) 2016 Little5ann All rights reserved

相關文章
相關標籤/搜索