希爾排序是基於插入排序的乙個優化。可以使平均時間複雜度減少為o(n * log2(n))。
洛谷1177 排序
題目描述
將讀入的 n 個數從小到大排序後輸出。
輸入格式
第 1 行為乙個正整數 n。
第 2 行包含 n 個空格隔開的正整數 a[i],為你需要進行排序的數,資料保證了a[i]不超過10^9。
輸出格式
將給定的 n個數從小到大輸出,數之間用空格隔開。
輸入輸出樣例
輸入
5
4 2 4 5 1
輸出
1 2 4 4 5
說明提示
對於20% 的資料,有 n <= 10^3。
對於100% 的資料,有 n <=10^5 。
這裡我們定義乙個新的變數gap表示增量,意思是每次將a[1] ~ a[n] 分為gap組:
第1組:a[1],a[gap + 1],a[2 * gap + 1] ……
第2組:a[2],a[gap + 2],a[2 * gap + 2] ……
第3組:a[3],a[gap + 3],a[2 * gap + 3] ……
……………………………………………………
第gap組:a[gap],a[gap + gap],a[2 * gap + gap] ……
之後分別對這gap組進行插入排序,就可以讓序列中變得比之前更加有序。
gap剛開始往往取(n / 2),之後每次讓gap /= 2,直到gap == 0了就停止。這裡可能相對來說比較難理解,你可以自己動手去分析一下樣例。
最後算一下這個演算法的時間複雜度:我們需要先列舉gap,再去列舉i和j,總共有三重迴圈。第一重迴圈每次是gap /= 2,跑了大約log2(n)次,而第二重迴圈跑了大約n次。但第三重迴圈實際上很快,常數次就跑完了,因為每次分完組,都會使這個序列變得更加有序,第三重迴圈去插入的時候跑了幾次就跳出去了。所以綜合起來平均的時間複雜度也就o(n * log2(n))級別,相對於插入排序就快了很多了。
這裡有乙個坑點務必注意:在寫j這個迴圈時,判斷是否跳出迴圈的語句中一定要把j >= 1 放在a[j] > a[j + gap]前面。
for (int j = i - gap; j >= 1 && a[j] > a[j + gap]; j -= gap) // 正確
for (int j = i - gap; a[j] > a[j + gap] && j >= 1; j -= gap) // 錯誤
因為如果說我的j -= gap後萬一變成了負數,那麼a[j]就沒有意義了,而&&運算是先去判斷前面的語句,如果前面的語句正確才去判斷後面的語句,否則不會去判斷後面的語句。
這樣如果是第一行的for迴圈,當j變成負數後,先判斷出j >= 1是假的,根據&&符號的運算法則,不回去判斷a[j] > a[j + gap],是正確的。
而如果是第二行的for迴圈,當j變成負數後,會先去判斷a[j] > a[j + gap],可j是負數,a[j]根本沒有意義,就成功re了。
# include # include # include # include using namespace std;
const int n_max = 100000;
int n;
int a[n_max + 10];
void shellsort()
int main()
排序演算法 希爾排序
如果乙個排序演算法,每次只把諸專案移動乙個位置,則它的平均執行時間至少要和n2成比例.因為在這個排序演算法執行的過程當中,每個記錄平均都必須遍歷n 3個位置,因此如果要對直接插入排序進行有效的,實質性的改進的話,就要有一種演算法,它可以使記錄做長距離的跳躍,而不是一步一步的挪動.希爾排序也是一種插入...
排序演算法 希爾排序
摘要 排序演算法有很多,最簡單的有氣泡排序和插入排序,這兩種方法都具有o n 2 的時間界.我們想要討論的是具有更好的時間界的排序演算法,比如希爾排序.1 希爾排序的思路是通過比較一定間距的元素來進行排序,最後再對所有相鄰元素進行一次插入排序.2 希爾排序最重要的引數是增量序列 h1,h2,ht 只...
排序演算法 希爾排序
希爾排序,也稱遞減增量排序演算法,是插入排序的一種更高效的改進版本。希爾排序是非穩定排序演算法。希爾排序通過將比較的全部元素分為幾個區域來提公升插入排序的效能。這樣可以讓乙個元素可以一次性地朝最終位置前進一大步。然後演算法再取越來越小的步長進行排序,演算法的最後一步就是普通的插入排序,但是到了這步,...