數(shù)據(jù)結(jié)構(gòu)05-排序和查找

1:排序算法分為如下5類:

  1. 插入排序:普通插入排序竹观,shell排序等;
  2. 選擇排序:普通選擇排序潜索,堆排序臭增;
  3. 交換排序:冒泡法,快速排序竹习;
  4. 歸并排序:
  5. 基數(shù)排序誊抛。

待排數(shù)據(jù)為:9,6,2,3,10,有小到大排列整陌;下面來(lái)實(shí)現(xiàn)上面的各種排序算法

2:插入排序(希爾排序)

基本插入排序:每次將一個(gè)待排序的數(shù)據(jù)元素拗窃,插入到前面已經(jīng)排好序的數(shù)列中的適當(dāng)位置,使數(shù)列依然有序泌辫;直到待排序數(shù)據(jù)元素全部插入完為止随夸。

穩(wěn)定排序:指待排序序列中可能存在兩個(gè)或兩個(gè)以上關(guān)鍵字相等的記錄。排序前的序列中Ri領(lǐng)先于Rj(即i<j)震放。若在排序后的序列中Ri仍然領(lǐng)先于Rj逃魄,則稱所用的方法是穩(wěn)定的。比如序列:3,6,1a,2,1b,8,4,7澜搅,其中1a和1b的值都是1,為了區(qū)別邪锌,1a在1b的前面勉躺。如果排序之后,1a依然在1b的前面觅丰,那么就是穩(wěn)定排序饵溅,否則就是非穩(wěn)定排序。

//基本插入排序
//基本插入排序的時(shí)間復(fù)雜度為O(n^2)妇萄,是一種穩(wěn)定排序算法
void insert_sort(int a[], int n) {
    int i, j, tmp;
    
    for(int i=1; i<n; i++) {
        tmp = a[i];
        j = i - 1;
        
        while(tmp < a[j]) {//當(dāng)tmp大于等于a[j]時(shí)不用交換
            a[j+1] = a[j];
            j--;
        }
        
        a[j+1] = tmp;
    }
}

希爾排序:是一種經(jīng)過(guò)改進(jìn)的插入排序算法蜕企。

希爾排序基本思想:先將整個(gè)待排元素序列分割成若干個(gè)子序列(由相隔某個(gè)“增量”的元素組成的)分別進(jìn)行直接插入排序,然后依次縮減增量再進(jìn)行排序冠句,待整個(gè)序列中的元素基本有序(增量足夠星嵫凇)時(shí),再對(duì)全體元素進(jìn)行一次直接插入排序懦底。因?yàn)橹苯硬迦肱判蛟谠鼗居行虻那闆r下(接近最好情況)唇牧,效率是很高的,因此希爾排序在時(shí)間效率上比前兩種方法有較大提高。具體做法:首先確定一組增量d0丐重,d1腔召,d2,d3扮惦,...,dt-1()其中n>d0>d1>...>dt-1=1)臀蛛,對(duì)于i =0,1,2,...,t-1,依次進(jìn)行下面的各趟處理:根據(jù)當(dāng)前增量di將n個(gè)元素分成di個(gè)組,每組中元素的下標(biāo)相隔為di;再對(duì)各組中元素進(jìn)行直接插入排序崖蜜。

//希爾排序的實(shí)現(xiàn)算法:
//希爾排序的復(fù)雜度為:O(n^1.25)浊仆,它不是穩(wěn)定排序。
void shellsort(int a[], int n) {
     int i, j, gap, tmp;
     gap = n/2;

     while (gap > 0) {
         for (i = gap + 1; i <= n; i++) {
              j = i - gap;

              while (j > 0) {
                   if (a[j] > a[j+gap]) {
                       tmp = a[j];
                       a[j] = a[j+gap];
                       a[j+gap] = a[j];
                   } else {
                       j = 0;
                   }
              }
         }

         gap /= 2;
     }
}

3:選擇排序(堆排序)

選擇排序:每一次都是從待排序的數(shù)據(jù)元素中選出最心芍怼(或最大)的一個(gè)元素氧卧,順序放在已排好序的數(shù)列的最后,直到全部待排序的數(shù)據(jù)元素排完氏堤。

//簡(jiǎn)單選擇排序
//時(shí)間復(fù)雜度為O(n^2)沙绝,它是不穩(wěn)定排序。
void selectsort(int a[], int n) {
       int i, j, k, tmp;

       for (i = 0; i < n-1; i++) {
              k = i;

              for (j = i + 1; j < n; j++) {
                     if (a[j] < a[k])
                            k = j;
              }

              if (k != i) {
                     tmp = a[i];
                     a[i] = a[k];
                     a[k] = tmp;
              }
       }
}

堆排序:是一樹形選擇排序鼠锈,在排序過(guò)程中闪檬,將R[1..N]看成是一顆完全二叉樹的順序存儲(chǔ)結(jié)構(gòu),利用完全二叉樹中雙親結(jié)點(diǎn)和孩子結(jié)點(diǎn)之間的內(nèi)在關(guān)系來(lái)選擇最小的元素购笆。

//堆排序
//時(shí)間復(fù)雜度為O(nlogn)粗悯,是不穩(wěn)定排序
void sfilter(int a[], int l, int m) {
     int i, j x;
     i = l;
     j = 2 * i;
     x = a[i];

     while ( j <= m) {
         if (j < m && a[j] < a[j+1])
              j++;

         if (x < a[j]) {
              a[i] = a[j];
              i = j;
              j = 2 * i;
         } else {
              j = m + 1;
         }
     }

     a[i] = x;
}

void heapsort(int a[], int n) {
     int i, w;

     for (i = n/2; i >= 1; i--)
         sfilter(a, i, n);

     for (i = n; i >= 2; i--) {
         w = a[i];
         a[i] = a[1];
         a[1] = w;
         sfilter(a, 1, i - 1);
     }
}

4:交換排序(冒泡排序/快排)

交換排序:兩兩比較待排序記錄的關(guān)鍵字,發(fā)現(xiàn)兩個(gè)記錄的次序相反時(shí)即進(jìn)行交換同欠,直到?jīng)]有反序的記錄為止样傍。

首先來(lái)看著名的交換排序算法:冒泡法。冒泡法的排序思想是:從第n個(gè)元素(a[n-1])開始铺遂,掃描數(shù)組衫哥,比較相鄰兩個(gè)元素,如果次序相反則交換襟锐。如此反復(fù)撤逢,直到?jīng)]有任何兩個(gè)違反相反原則的元素

//冒泡排序:
//時(shí)間復(fù)雜度為O(n^2),它是穩(wěn)定排序
void bubble_sort(int a[], int n) {
       int i, j, tmp;

       for (i = 0; i < n-1; i++) {
              for (j = n-1; j >= i+1; j--) {
                     if (a[j] < a[j-1]) {
                            tmp = a[j];
                            a[j] = a[j-1];
                            a[j-1] = tmp;
                     }
              }
       }
}

快速排序:在當(dāng)前無(wú)序區(qū)R[1..H]中任取一個(gè)數(shù)據(jù)元素作為比較的“基準(zhǔn)”(不妨記為X粮坞,R[1])蚊荣,用此基準(zhǔn)將當(dāng)前無(wú)序區(qū)劃分為左右兩個(gè)較小的無(wú)序區(qū):R[1..I-1]和R[I+1..H],且左邊的無(wú)序子區(qū)中數(shù)據(jù)元素均小于等于基準(zhǔn)元素莫杈,右邊的無(wú)序子區(qū)中數(shù)據(jù)元素均大于等于基準(zhǔn)元素互例,而基準(zhǔn)X則位于最終排序的位置上,即R[1..I-1]≤X≤RI+ 1..H筝闹,當(dāng)R[1..I-1]和R[I+1..H]均非空時(shí)敲霍,分別對(duì)它們進(jìn)行上述的劃分過(guò)程俊马,直至所有無(wú)序子區(qū)中的數(shù)據(jù)元素均已排序?yàn)橹埂?/p>

找一個(gè)數(shù)X(比如頭一個(gè)元素)做基準(zhǔn),右邊比X小的移動(dòng)到左邊肩杈,左邊比X大的移動(dòng)到右邊柴我,最后空出的位置就是X自己的位置

快速排序的時(shí)間復(fù)雜度為O(nlogn),最壞情況為O(n^2)扩然。對(duì)于大的艘儒、亂數(shù)序列一般相信是最快的已知排序。待排記錄序列按關(guān)鍵字順序有序時(shí)夫偶,直接插入排序和起泡排序能達(dá)到O(n)的時(shí)間復(fù)雜度界睁,而對(duì)于快速排序而言,這是最不好的情況兵拢。對(duì)于很小的數(shù)組(如N<=20)翻斟,快速排序不如插入排序好。

void quickSort(int a[],int left,int right) {
   int i,j,temp;
   i=left;
   j=right;
   temp=a[left];

   if(left>right)
      return;

   while(i<j) {/*找到最終位置*/ 
      while(a[j]>=temp && j>i)
         j--;

      if(j>i)
         a[i++]=a[j];

      while(a[i]<=temp && j>i)
          i++;

      if(j>i)
          a[j--]=a[i];
   }

   a[i]=temp;
   quickSort(a,left,i-1);/*遞歸左邊*/
   quickSort(a,i+1,right);/*遞歸右邊*/
}

void qsort(int a[], int n) {
       quickSort(a, 0, n-1);

}

5:排序時(shí)間復(fù)雜度空間復(fù)雜度比較

排序算法 平均時(shí)間 最壞情況 輔助存儲(chǔ) 是否是穩(wěn)定算法
簡(jiǎn)單排序 O(n^2) O(n^2) O(1)
快速排序 O(nlogn) O(n^2) O(logn)
堆排序 O(nlogn) O(nlogn) 0(1)
歸并排序 O(nlogn) O(nlogn) O(n)
基數(shù)排序 O(d(n+rd)) O(d(n+rd)) O(rd)

6:查找(折半查找/HASH查找)

查找:將表中記錄的關(guān)鍵字與查找關(guān)鍵字比較说铃,如果兩者相等访惜,則查找成功,返回查找位置腻扇。

包含:鏈表查找债热,數(shù)組查找,二叉樹查找幼苛,hash

6.1: 折半查找

折半查找:又叫二分查找窒篱,首先,假設(shè)表中元素是按升序排列舶沿,將表中間位置記錄的關(guān)鍵字與查找關(guān)鍵字比較墙杯,如果兩者相等,則查找成功括荡;否則利用中間位置記錄將表分成前霍转、后兩個(gè)子表,如果中間位置記錄的關(guān)鍵字大于查找關(guān)鍵字一汽,則進(jìn)一步查找前一子表,否則進(jìn)一步查找后一子表低滩。重復(fù)以上過(guò)程召夹,直到找到滿足條件的記錄,使查找成功恕沫,或直到子表不存在為止监憎,此時(shí)查找不成功。

image.png

注意:折半查找必須滿足兩個(gè)條件:一婶溯,元素必須是連續(xù)存儲(chǔ)鲸阔;二偷霉,元素必須有序。時(shí)間復(fù)雜度:O(logn)

//折半查找算法:
//a為存放數(shù)據(jù)的有序表褐筛,n為數(shù)據(jù)元素個(gè)數(shù)类少,k為要查找的元素
int bin_search(int a[], int n, int k) {
     int low, high, mid, i;

     low = 0;
     high = n-1;

     while (low <= high) {
         mid = (low + high)/2;

         if (a[mid] < k)
              low = mid + 1;
         else if (a[mid] > k)
              high = mid - 1;
         else {
              return mid;
         }
     }

     return -1;
}

//遞歸版本:

 

int iter_biSearch(int data[], const int x, int beg, int last) {  
    int mid = -1;  
    mid = (beg + last) / 2;  

    if (x == data[mid]) {  
        return mid;  
    } else if (x < data[mid]) {  
        return iter_biSearch(data, x, beg, mid - 1);  
    } else if (x > data[mid]) {  
        return iter_biSearch(data, x, mid + 1, last);  
    }  

    return -1;  
} 

int bin_search2(int a[], int n, int k) {
    return  iter_biSearch(a,k,0,n-1);
}

6.2:Hash表

Hash表用于存放key-value數(shù)據(jù)。比如一個(gè)學(xué)生的成績(jī)渔扎,那么學(xué)生的學(xué)號(hào)可以當(dāng)做key硫狞,成績(jī)當(dāng)做value,存放與hash表中晃痴。

image.png

Hash查找必須提供一個(gè)Hash函數(shù)残吩,用于通過(guò)Key來(lái)計(jì)算數(shù)據(jù)存放在hash表中的位置。一般hash函數(shù)可以設(shè)計(jì)為key%N倘核,其中N為hash表中元素的個(gè)數(shù)(一般為質(zhì)數(shù))泣侮。

假如HASH表的大小為N,那么Hash函數(shù)為:Hash(key)=key%N

當(dāng)對(duì)于不同的兩個(gè)key紧唱,計(jì)算出來(lái)的hash值可能相同活尊,在相同的時(shí)候,就叫做hash沖突琼蚯。解決hash沖突的方法不止一種酬凳,比如通過(guò)鏈?zhǔn)椒ń鉀Q,即將所有含有相同hash值的數(shù)據(jù)存放在同一個(gè)鏈表中遭庶,而將鏈表的頭結(jié)點(diǎn)存放在HASH表中宁仔。

所謂hash查找,就是通過(guò)對(duì)應(yīng)的key峦睡,按照hash函數(shù)翎苫,計(jì)算出數(shù)據(jù)在hash表中的位置。Hash查找的復(fù)雜度為O(1)榨了,所以具有較高的查找效率煎谍。

6.3:二叉搜索樹查找

typedef struct _node {
     int data;
     struct _node *left;
     struct _node *right;
} node, btree;

btree *search(btree *b, int x) {
     if (b == NULL) {
         return NULL;
     } else {
         if (b->data == x) {
              return b;
         } else if (x < b->data) {
              return (search(b->left));
         } else {
              return (search(b->right));
         }
     }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市龙屉,隨后出現(xiàn)的幾起案子呐粘,更是在濱河造成了極大的恐慌,老刑警劉巖转捕,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件作岖,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡五芝,警方通過(guò)查閱死者的電腦和手機(jī)痘儡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)枢步,“玉大人沉删,你說(shuō)我怎么就攤上這事渐尿。” “怎么了矾瑰?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵砖茸,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我脯倚,道長(zhǎng)渔彰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任推正,我火速辦了婚禮恍涂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘植榕。我一直安慰自己再沧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布尊残。 她就那樣靜靜地躺著炒瘸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪寝衫。 梳的紋絲不亂的頭發(fā)上顷扩,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音慰毅,去河邊找鬼隘截。 笑死,一個(gè)胖子當(dāng)著我的面吹牛汹胃,可吹牛的內(nèi)容都是我干的婶芭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼着饥,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼犀农!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起宰掉,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤呵哨,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后轨奄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體孟害,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年戚绕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枝冀。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡舞丛,死狀恐怖耘子,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情球切,我是刑警寧澤谷誓,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站吨凑,受9級(jí)特大地震影響捍歪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鸵钝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一糙臼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧恩商,春花似錦变逃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至粟矿,卻和暖如春凰棉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背陌粹。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工撒犀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人申屹。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓绘证,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親哗讥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嚷那,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容