C 演算法系列之排序

2021-08-11 14:09:25 字數 4369 閱讀 6445

插入排序

o(n2),基本思路就是玩撲克牌的時候,從牌堆裡摸牌放到手上的思路.

#include

#include

#include

#include

#include

#include

const

int m = 1000;

const

int n = 1000;

template

void insertsort(const iterator &a, const iterator &b)

i -- ;

q--;}}

}void producedata(std::vector

&data)

}template

void checkvalid(iterator b, iterator e)

if(comparator()(*t,*b))

b++;

}}template

class smaller

};int main()

歸併排序

//o(nlgn),需要額外空間

template

void merge(iterator s, iterator p, iterator e)

else

tmp++;

}if(i< s1)

if(j < s2)

delete l;

delete r;

}template

void mergesort(iterator s, iterator e)

}int main()

; mergesort(a, a+7);

std::copy(a,a+7, std::ostream_iterator(std::cout, " "));

std::cout

<< std::endl;

}

快速排序o(nlgn)

int randomnumber(int p, int q)

template

iterator partition(iterator s, iterator e, typename

std::iterator_traits::value_type n)//返回乙個序列,使得大於n的在左邊,小於n的在右邊

return p;

}template

iterator randomizedpartition(iterator s, iterator e)//隨機抽取乙個元素,作為主元,用它來重新劃分序列

template

void quicksort(iterator s, iterator e)

}int main()

; quicksort(a,a+sizeof(a)/sizeof(int));

std::copy(a,a+sizeof(a)/sizeof(int), std::ostream_iterator(std::cout, " "));

std::cout

<< std::endl;

}

歸併和快排的**有點相似,都是利用分治策略來進行問題的求解的.

分治策略大體上分為3步,即分解, 解決, 合併

歸併排序和快排也是按照這三個步驟進行的,不過二者有點差異.歸併排序在分解的時候,不做任何處理,問題的分解沒有利用到原問題的資訊,一路分解到子問題規模足夠小(也就是剩下乙個元素的時候),乙個元素預設有序,因此子問題直接解決,最後呼叫合併有序序列的方法將結果進行合併.

快速排序在分解的時候,就進行了對問題的解決(對序列按主元進行了重新劃分),問題分解的時候利用到了原問題的資訊.

歸併排序是按照自底向上的順序進行排序

快速排序是按照自頂向下的順序進行排序

堆排序

o(nlgn)

首先明白堆的性質,操作物件是個陣列,可被視為乙個幾乎完全的二叉樹.

對於所有非根節點,當a[parent[i]] >= a[i],視為最大堆

a[parent[i]] <= a[i], 視為最小堆.

堆排序首先需要建立堆,建立堆的意思就是調整陣列的元素位置,使得陣列的元素符合堆的樣子,即對陣列a, 有a[parent[i]] >= a[i] 或者a[parent[i]] <= a[i].

調整的方式是自下而上,就從所有的非葉子節點開始調整,保證從非葉子節點開始往下都是堆有序的(即所有非葉子節點的子節點i, 滿足a[parent[i]] >= a[i] or a[parent[i]] <= a[i])

堆排序就更簡單了,將堆頂元素和陣列最後乙個元素交換,然後再調整堆頂節點,使得交換後的堆恢復堆有序.不斷交換直至完全有序為止.

template

void downheap(iterator s, int i, int n)

}if(2*i +2

< n)

}if(min != *p)

}template

void upheap(iterator s, int i)}}

}template

void makeheap(iterator s, iterator e)

}template

void heapsort(iterator s, iterator e)

}int main()

; makeheap(a,a+7);

heapsort(a,a+7);

std::copy(a,a+sizeof(a)/sizeof(int), std::ostream_iterator(std::cout, " "));

std::cout

<< std::endl;

}

優先佇列

這東西可以被理解成乙個包裹了heap 的殼子,它的核心就是乙個heap. 不過它可以限定了內建heap的大小.定義了出隊和入隊操作. 出隊和入隊事實上就是堆的插入和刪除操作

這玩意再統計topk裡面很有用.

具體如下,首先弄乙個最小堆

堆頂元素是最小值.

當讀入元素小於k時,就入隊.

當讀入元素大於k時,比較該元素與堆頂元素大小.如果比堆頂小,捨棄;如果大於堆頂元素,刪除堆頂元素,將該元素入隊

template

class prioqueue

void enqueue(t e)

t deque()

t top()

void print()

};int main()

; makeheap(a,a+3);

std::copy(a,a+sizeof(a)/sizeof(int), std::ostream_iterator(std::cout, " "));

std::cout

<< std::endl;

heapsort(a,a+3);

std::copy(a,a+sizeof(a)/sizeof(int), std::ostream_iterator(std::cout, " "));

std::cout

<< std::endl;

prioqueue q;

q.enqueue(kk);

q.print();

q.dequeue();

q.print();

}

//stl定義了優先佇列的結構,這裡簡單列一下用法

struct node

};struct nodecmp

};int main()

{ std::priority_queuestd::vector

, nodecmp> m;

m.push(node(5, "ss"));

m.push(node(3, "aa"));

m.push(node(1, "bb"));

基於比較的排序次數最壞情況下至少需要nlgn次比較.

假設比較1,2,3. 我們以二叉樹分支表示,頂點位置記成1:2(拿1和2比較),小於進左邊分支,大於進右邊分支.這樣葉子節點就是最終能到3個元素的全排列.全排列是n的階乘.高度h 的滿二叉樹最多有2的h次方個葉子.所以 n!<=2的h次方(公式). 因為任何一次從頂點到根的路徑就是乙個排序過程. 所以最壞排序情況肯定在其中一條路徑上.所做的比較次數 也就是h.

n!<=2的h次方,兩邊取對數h>=lg(n!)>=nlgn.

穩定排序, 假設待排序的序列有兩個相同的元素a,b, 其中a,b的值是一樣的,排序前a 在b前面,排序後a 還在b的前面,則為穩定排序,否則為不穩定排序.

插入排序,歸併排序和堆排序都是穩定排序.快排則不是穩定排序

排序演算法系列之氣泡排序

核心思想 氣泡排序是一種典型的 交換排序 通過比較相鄰元素大小來決定是否交換位置 如上圖所示,以一組資料 為例,進行氣泡排序的演算法演示 氣泡排序 c void swap int a,int b void bubblesort vector vi 演算法改進說明 1,對於是否已經是有序排列進行判斷 ...

排序演算法系列

概述 概念 排序是計算機內經常進行的一種操作,其目的是將一組 無序 的記錄序列調整為 有序 的記錄序列。排序分為內部排序和外部排序。若整個排序過程不需要訪問外存便能完成,則稱此類排序問題為內部排序。反之,若參加排序的記錄數量很大,整個序列的排序過程不可能在記憶體中完成,則稱此類排序問題為外部排序。排...

排序演算法系列之合併排序

歸併排序 merge sort,合併排序 是建立在歸併操作上的一種有效的排序演算法。該演算法是採用分治法 divide and conquer 的乙個非常典型的應用。歸併操作 merge 指的是將兩個已經排序的序列合併成乙個序列的操作,歸併排序演算法依賴歸併操作。歸併操作的過程如下 申請空間,使其大...