希爾排序是一種高性能的排序算法 ,其核心思想在於:
1.將數組分割為若干子數組,對每個子數組進行簡單算法排序(如插入,梳排序均可);
2.將排序后的子序列合並,繼續重復步驟1;
直到所分的數組為1組。,算法結束。
那么上述中,如何分組呢?分組方式見《C++數據結構與算法》393頁最下面的划分方式,當然對於希爾排序而言,目前並沒有一種理論上最優的划分方式,書上提供的是一種較為優秀的划分方式。
下面直接上代碼:
1 # include"iostream" 2 # include "vector" 3 # include "ctime" 4 # include "cstdlib" // 如果我們想通過隨機數來驗證我們的想法。應該預先給vecot分配內存 5 # include "cmath" 6 7 using namespace std; 8 void insert_sort(vector<int> & ); 9 void ChooseSort(vector<int> &); 10 void BubbleSort(vector<int> &); 11 void CombSort(vector<int> & ); 12 void ShellSort(vector<int> &); 13 int main() 14 { 15 const int MAX_NUMBER = 10000;//生成的隨機數的個數 16 const int MIN_VALUE = 1; //隨機數值的下界 17 const int MAX_VALUE = 30000;//隨機數值的上界 18 vector<int> a; 19 a.reserve(MAX_NUMBER);//預先分配內存你,預防迭代器的失效。否則內存搬移,迭代器容易失效 20 21 // 采用控制台輸入的方式 22 //int temp; 23 //while (cin >> temp) 24 //{ 25 // a.push_back(temp); 26 // if (cin.get() == '\n') //注意這種用法,用的很多。 27 // { 28 // break; 29 // } 30 //} 31 32 //采用隨機數生成法。 33 srand((unsigned)time(NULL));//根據時鍾生成不同的種子,保證每次運行程序產生不同的隨機數 34 for (int i = 1; i <= MAX_NUMBER; i++) 35 { 36 int temp; 37 temp = (rand() % (MAX_VALUE - MIN_VALUE + 1)) + MIN_VALUE;//生成[MIN_VALUE,MAX_VALUE]之間的整數隨機數 38 a.push_back(temp); 39 } 40 //cout << "The initial order: "; 數據量太大,不適合輸出 41 //vector<int>::iterator ia = a.begin(); 42 //for (; ia != a.end(); ia++) 43 //{ 44 // cout << *ia << " "; 45 //} 46 //cout << endl; 47 vector<int> b(a); 48 vector<int> c(a); 49 vector<int> d(a); 50 vector<int> e(a); 51 //show the initial order of the vector 52 //cout << "The initial order before Sorting : "; 53 //vector<int> ::iterator ia = a.begin(); 54 //for (; ia != a.end(); ia++) 55 //{ 56 //cout << *ia << " "; 57 //} 58 //cout << endl; 59 // 插入排序測試 60 clock_t start, finish; 61 double runtime; 62 start = clock(); 63 insert_sort(a); 64 finish = clock(); 65 runtime = (double)(finish - start) / CLOCKS_PER_SEC; 66 cout << "The time of Insert Sort algorithm is:" << runtime << "s" << endl; 67 // 選擇排序測試 68 start = clock(); 69 ChooseSort(b); 70 finish = clock(); 71 runtime = (double)(finish - start) / CLOCKS_PER_SEC; 72 cout << "The time of Choose Sort algorithm is:" << runtime << "s" << endl; 73 // 冒泡排序測試 74 start = clock(); 75 BubbleSort(c); 76 finish = clock(); 77 runtime = (double)(finish - start) / CLOCKS_PER_SEC; 78 cout << "The time of Bubble Sort algorithm is:" << runtime << "s" << endl; 79 // 梳排序測試 80 start = clock(); 81 CombSort(d); 82 finish = clock(); 83 runtime = (double)(finish - start) / CLOCKS_PER_SEC; 84 cout << "The time of Comb Sort algorithm is:" << runtime << "s" << endl; 85 //希爾排序 86 start = clock(); 87 ShellSort(e); 88 finish = clock(); 89 runtime = (double)(finish - start) / CLOCKS_PER_SEC; 90 cout << "The time of Shell Sort algorithm is:" << runtime << "s" << endl; 91 system("pause"); 92 return 0; 93 } 94 //插入排序 95 void insert_sort(vector<int> & sort_a) ///注意,采用vector<int> 是可以作為參數進行傳遞的,那么是否可以作為返回類型使用呢? 96 { 97 int a_size ,temp; 98 a_size = sort_a.size(); 99 for (int i = 1,j; i < a_size; i++)//注意,將j放在這里聲明,不要放在下一個循環中聲明,否則會造成多次聲明?(但是作用域沒變,多次聲明應該被禁止,所以沒有多次聲明?) 100 { 101 temp = sort_a[i]; 102 for (j = i; (j > 0) && (sort_a[j - 1] > temp); j--)//這里先后順序的差別,導致了數組的越界, 103 { 104 sort_a[j] = sort_a[j-1]; 105 } 106 sort_a[j] = temp; 107 } 108 //return sort_a;//說明可以將vector<int> 本身作為函數的返回值使用,但是需要清楚的是:返回的時候,內存實際上發生了局部變量復制,所以是否考慮返回引用??? 109 } 110 //選擇排序 111 void ChooseSort(vector<int> & sort_a) 112 { 113 int a_size,MinNum; 114 a_size = sort_a.size(); 115 for (int i = 0, j; i < a_size; i++) 116 { 117 int Loc = i; 118 MinNum = sort_a[i];//本質是隨機迭代器的使用 119 //temp = sort_a[i]; 120 for (j = i+1; j < a_size; j++) 121 { 122 if (sort_a[j] < MinNum) 123 { 124 MinNum = sort_a[j]; 125 Loc = j; 126 } 127 } 128 //sort_a[i] = MinNum;//用於交換,但是本身自帶的函數可以實現 129 //sort_a[Loc] = temp; 130 if (i != Loc) 131 { 132 swap(sort_a[i], sort_a[Loc]);//vector自帶的東西 133 } 134 } 135 } 136 //冒泡排序 137 void BubbleSort(vector<int> & sort_a) 138 { 139 int a_size; 140 a_size = sort_a.size(); 141 for (int j = 0; j < a_size-1; j++) 142 { 143 for (int i = 0; i < a_size - 1-j; i++) 144 { 145 if (sort_a[i]>sort_a[i + 1]) 146 swap(sort_a[i], sort_a[i + 1]); 147 } 148 } 149 } 150 //梳排序 151 void CombSort(vector<int> & sort_a) 152 { 153 int step,j,k; 154 step = sort_a.size(); 155 while ((step = int(step / 1.3)) >0) 156 { 157 for (j = sort_a.size() - 1; j >= step; j--) 158 { 159 k = j - step; 160 if (sort_a[j] < sort_a[k]) 161 { 162 swap(sort_a[j], sort_a[k]); 163 } 164 } 165 } 166 } 167 168 //希爾排序 169 void ShellSort(vector<int> & sort_a) 170 { 171 int a_size; 172 a_size = sort_a.size();//得到數組大小 173 vector<int> ht; 174 ht.resize(floor(log(a_size)/log(3)));//為增量數組預先分配空間 175 ht[0] =1;//為何這樣會出錯???預分配空間后為何不能使用隨機訪問的機制??? 176 for (int i = 1; i<=ht.size()-1; i++) 177 { 178 ht[i] = 3 * ht[i - 1] + 1; 179 } 180 int ht_size = ht.size(); 181 vector<int> new_a; 182 new_a.resize(a_size); 183 for (int count = ht.size(), temp; count >= 1; count--) 184 { 185 temp = ht[count - 1];//temp的值決定了當前會被分成多少組 186 //vector<int>a_divide; 187 vector<vector<int>> aMatrix(temp, vector<int>(ceil(a_size / (1.0*temp))));//定義一個二維數組, 188 for ( int i = 0; i < temp; i++) 189 { 190 int j; 191 for (j = 0; (i + j*temp) < a_size; j++) 192 { 193 aMatrix[i][j] = sort_a[i + j*temp];//將應該分為同一組的數據挑選出來 195 } 196 if (ceil(a_size / (1.0*temp))>j) 197 { 198 aMatrix[i][j] = 33333;//用於解決有0的問題 199 } 200 ;//問題是,本身有些空間存在0,這樣寫,將本身沒有的0元素也進行了排序 201 } 202 //截止到這里,程序是沒有問題的,也就是大部分是正確的,但是下面存在的問題是:數組中的0怎么辦 203 for(int k = 0, q = 0; k < ceil(a_size / (1.0*temp)); k++) 204 { 205 for (int p = 0; p < temp; p++) 206 { 207 sort_a[q] = aMatrix[p][k]; 208 q = q + 1; 209 if (q >= a_size) 210 break; 211 } 212 if (q >= a_size) 213 break; 214 } 215 } 216 217 }
實際上,上述給出很多排序算法的代碼。
169-215行為希爾排序算法的實現,對算法的各個部分進行解讀:
179行以前,求解每次分組的次數;
200行以前,將同一組的數據提取出來,組成一個矩陣,每行表示同一組的,不同行表示不同分組,對每行的數據采取梳排序
200行以后,將輸入向量更新為矩陣列向量的拼接值
重復迭代,直到h =1;
針對希爾排序,其主要思想是一種分治的思想,給出上述排序算法效率的對比:
該次測試中,隨機數的個數為10000個,從結果中可以看出,希爾排序效率最高,但其和梳排序的效率較為接近(思考原因)
在手擼希爾排序算法的過程中,遇到了很多c++中的問題,在此給出以下說明:
ht.resize(floor(log(a_size)/log(3)));這句代碼的本意是給數組ht預分配空間,開始使用的是ht.reserve(),但發現這樣寫,在調試的時候,導致ht[0] =1;出錯,這說明並沒有達到我們想要的結果。百度了下,發現:
可見,使用reserve()並不會改變vector的大小,並不會和我們想的那樣在內存中開辟出一片空間給vector。因此,若我們需要開辟一塊可用空間,應該用resize()。那reserve()的作用是什么呢?(懸而未決)
vector<vector<int>> aMatrix(temp, vector<int>(ceil(a_size / (1.0*temp))));定義了一個temp×ceil(a_size / (1.0*temp))大小的二維數組aMatrix,注意這是使用vector定義的二維數組,需要注意的是vector允許這種嵌套定義。
另一個點在於CombSort(aMatrix[i]),CombSort的形參形式為數組引用,但這里傳遞是一個1維指針,說明某種意義上兩者可以相互轉換。但這個本質上說明了一個二維數組,只取一維其實就是一個指針。