赞
踩
本章篇幅较长,大家可以跳过前言部分,在目录快速找到自己需要了解的排序算。
推荐:
另外推荐大家读一下《我的第一本算法书》,书中通过大量图示的方式描述了多种排序算法的实现过程。《挑战程序设计竞赛2》,该书中对算法技巧提升很有帮助。
排序就是将输入的数字按照从小到大的顺序进行排列。这里我们用柱形来表示数字,数字越大,柱形就越高。
假设现在有如上图所示的输入数据,那么我们的目标就是将它们像下图一样,按从小到大的顺序从左边开始依次排列。
如果只有10 个数字,手动排序也能轻松完成,但如果有10000个数据,排序就不那么容易 了。这时,使用高效率的排序算法便是解决问题的关键
排序算法 | 时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 排序方式 | 稳定性 | 排序类型 |
1.冒泡排序 | O(n^2) | O(n) | O(n^2) | O(1) | In-place | 稳定 | 比较 |
2.选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | In-place | 不稳定 | 比较 |
3.插入排序 | O(n^2) | O(n) | O(n^2) | O(1) | In-place | 稳定 | 比较 |
4.堆排序 | O(n logn) | O(n logn) | O(n logn) | O(1) | In-place | 不稳定 | 比较 |
5.归并排序 | O(n logn) | O(n logn) | O(n logn) | O(n) | Out-place | 稳定 | 比较 |
6.快速排序 | O(n logn) | O(n logn) | O(n^2) | O(logn) | In-place | 不稳定 | 比较 |
7.希尔排序 | O(n^1.5) | O(n logn) | O(n^2) | O(1) | In-place | 不稳定 | 比较 |
8.计数排序 | O(n+k) | O(n+k) | O(n+k) | O(k) | Out-place | 稳定 | 非比较 |
9.桶排序 | O(n+k) | O(n+k) | O(n^2) | O(n+k) | Out-place | 稳定 | 非比较 |
10.基数排序 | O(n*k) | O(n*k) | O(n*k) | O(n+k) | Out-place | 稳定 | 非比较 |
时间复杂度:一个算法执行所耗费的时间。
空间复杂度:运行完一个程序所需内存的大小。
内排序:所有排序操作都在内存中完成。
外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行。
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面。
目录
冒泡排序就是重复“从序列右边开始比较相邻两个数字的大小,再根据结果交换两个数字的位置”这一操作的算法。在这个过程中,数字会像泡泡一样,慢慢从右往左“浮”到序列的 顶端,所以这个算法才被称为“冒泡排序”。
1.1在序列的最右边放置一个天平,比较天平两边的数字。如果右边的数字较小,就交换这两个数字的位置。
1.2由于6<7,所以交换这两个数字。
1.3完成后,天平往左移动一个位置,比较两个数 字的大小。此处4<6,所以无须交换。
1.4继续将天平往左移动一个位置并比较数字。重复同样的操作直到天平到达序列最左边为止。
1.5不断对数字进行交换,天平最终到达了最左边。通过这一系列操作,序列中最小的数字就会移动到最左边
1.6最左边的数字已经归位。
1.7将天平移回最右边,然后重复之前的操作,直到天平到达左边第2个位置为止。
1.8当天平到达左边第2个位置时,序列中第2小的数字也就到达了指定位置。
1.9将天平再次移回最右边,重复同样的操作直到所有数字都归位为止。
1.10排序中……
1.11排序中……
1.12排序完成。
- /* 冒泡排序--普通版 */
- void BubbleSort1(int arr[], int len)
- {
- int i, j;
- /*循环遍历*/
- for (i = 0; i < len; i++)
- {
- for (j = len - 1; j >= i + 1; j--)
- {
- if (arr[j] < arr[j - 1])
- {
- /* 交换数据 */
- int temp=arr[j];
- arr[j]=arr[j-1];
- arr[j-1]=temp;
- /* 调用swap函数交换数据,效率会更高 */
- //swap(arr[j], arr[j - 1]);
- }
- }
- }
- }
- /* 冒泡排序--升级版 */
- void BubbleSort2(int arr[],int len)
- {
- int i, j;
- /* flag用来作为标记 */
- bool flag = true;
- /* 若flag为true说明有过数据交换,若没有数据交换,falg为false则数据已经全部是顺序的了 */
- for (i = 0; flag; i++)
- {
- /* 初始为false */
- flag = false;
- for (j = len - 1; j >= i+1; j--)
- {
- if (arr[j] < arr[j - 1])
- {
- /* 交换数据 */
- int temp = arr[j];
- arr[j] = arr[j - 1];
- arr[j - 1] = temp;
- /* 调用swap函数交换数据,效率会更高 */
- //swap(arr[j], arr[j - 1]);
-
- /* 如果有数据交换,则flag为true */
- flag = true;
- }
- }
- }
- }
- #include<iostream>
- #include<algorithm>
-
- using namespace std;
-
- int main()
- {
- int arr[100];
- int len;
- cout << "请输入数组个数:";
- cin >> len;
- cout << "请依次输入"<<len<<"个数组元素,以空格分开,回车结束" << endl;
- for (int i = 0; i < len; i++) {
- cin >> arr[i];
- }
-
- #if 0 //为0则选用BubbleSort2(arr, len);为1则选用BubbleSort1(arr, len);
- BubbleSort1(arr, len);
- #else
- BubbleSort2(arr, len);
- #endif
- /*打印数组*/
- for (int i = 0; i < len; i++) {
- cout << arr[i] << " ";
- }
- cout << endl;
-
- return 0;
- }
选择排序就是重复“从待排序的数据中寻找最小值,将其与序列最左边的数字进行交换” 这一操作的算法。在序列中寻找最小值时使用的是线性查找。
2.1对数字1~9进行排序。
2.2使用线性查找在数据中寻找最小值,于是我们找到了最小值 1。
2.3将最小值 1 与序列最左边的 6 进行交换,最小值 1 归位。不过,如果最小值已经在最左端,就不需要任何操作。
2.4在余下的数据中继续寻找最小值。这次我们找到了最小值2。
2.5将数字 2 与左边第 2 个数字 6 进行交换,最小值 2归位。
2.6重复同样的操作直到所有数字都归位为止。
2.7排序完成。
- /* 选择排序 */
- void selectionSort(int arr[], int len) {
- int i, j;
- int minj;
-
- for (i = 0; i < len - 1; i++) {
- /*minj标记最小数据的下标*/
- minj = i;
- for (j = i; j < len; j++) {
- if (arr[j] < arr[minj]) {
- minj = j;
- }
- }
- /*每循环一次找到一个最小值,当最小值下标是其他位置时,进行交换*/
- if (i != minj) {
- int temp = arr[i];
- arr[i] = arr[minj];
- arr[minj] = temp;
-
- /* 调用swap函数交换数据,效率会更高 */
- //swap(arr[i], arr[minj]);
- }
- }
- }
- #include<iostream>
- #include<algorithm>
-
- using namespace std;
-
- int main()
- {
- int arr[100];
- int len;
- cout << "请输入数组个数:";
- cin >> len;
- cout << "请依次输入" << len << "个数组元素,以空格分开,回车结束" << endl;
- for (int i = 0; i < len; i++) {
- cin >> arr[i];
- }
-
- selectionSort(arr, len);
- /*打印数组*/
- for (int i = 0; i < len; i++) {
- cout << arr[i] << " ";
- }
- cout << endl;
-
- return 0;
- }
插入排序是一种从序列左端开始依次对数据进行排序的算法。在排序过程中,左侧的数据陆续归位,而右侧留下的就是还未被排序的数据。插入排序的思路就是从右侧的未排序区域内取出一个数据,然后将它插入到已排序区域内合适的位置上。
3.1此处同样对数字1~9进行排序。
3.2首先,我们假设最左边的数字5已经完成排序,所以此时只有5是已归位的数字。
3.3接下来,从待排数字(未排序区域)中取出最左边的数字 3,将它与左边已归位的数字进行比较。若左 边的数字更大,就交换这两个数字。重复该操作,直到左边已归位的数字比取出的数字更小,或者取 出的数字已经被移到整个序列的最左边为止。
3.4由于5>3,所以交换这两个数字。
3.5对数字3的操作到此结束。此时3和5已归位,还剩下右边7个数字尚未排序。
3.6接下来是第3轮。和前面一样,取出未排序区域中最左边的数字4,将它与左边的数字5进行比较。
3.7由于 5 > 4,所以交换这两个数字。交换后再把 4 和左边的 3 进行比较,发现 3 < 4,因为出现了比自己 小的数字,所以操作结束。
3.8于是4也归位了。此时3、4、5都已归位,已 排序区域也得到了扩大。
3.9遇到左边的数字都比自己小的情况时……
3.10不需要任何操作即可完成排序。
3.11重复上述操作,直到所有数字都归位。
3.12对所有数字的操作都结束时,排序也就完成了。
- /* 插入排序 */
- void insertionSort(int arr[], int len) {
- int i, j;
- int v;
- /* 循环从第二个元素(arr[1])开始,第一个元素(arr[0])本身看作是有序的 */
- for (i = 1; i < len; i++) {
-
- /* v保存的是最新待排序的数据 */
- v = arr[i];
- /* j为已排序区域最右端数据下标 */
- j = i - 1;
- /* 循环比较,直到找到v待插入的位置 */
- while (arr[j] > v) {
- /* 待插入v数据值较小,较大数据往后移动一位 */
- arr[j + 1] = arr[j];
- j--;
- }
- /* 此时的arr[j]>v不成立,即v下标位置为j+1 */
- arr[j + 1] = v;
- }
- }
- #include<iostream>
- #include<algorithm>
-
- using namespace std;
-
- int main()
- {
- int arr[100];
- int len;
- cout << "请输入数组个数:";
- cin >> len;
- cout << "请依次输入" << len << "个数组元素,以空格分开,回车结束" << endl;
- for (int i = 0; i < len; i++) {
- cin >> arr[i];
- }
-
- insertionSort(arr, len);
- /*打印数组*/
- for (int i = 0; i < len; i++) {
- cout << arr[i] << " ";
- }
- cout << endl;
-
- return 0;
- }
堆排序的特点是利用了数据结构中的堆。
注:下面先了解堆实现过程,若已了解可略过。
4.01这就是堆的示例。结点内的数字就是存储的数据。堆中的每个结点最多有两个子结点。树的形状取决于数据的个数。另外,结点的排列顺序为从上到下,同一行里则为从左到右。
4.02在堆中存储数据时必须遵守这样一条规则 :子结点必定大于父结点。因此,最小值被存储在顶端的根结点中。往堆中添加数据时,为了遵守这条规则,一般会把新数据放在最下面一行靠左 的位置。当最下面一行里没有多余空间时,就再往下另起一行, 把数据加在这一行的最左端。
4.03我们试试往堆里添加数字5。
4.04首先按照4.02的说明寻找新数据的位置。该图中最下面一排空着一个位置,所以将数据加在此处。
4.05如果父结点大于子结点,则不符合上文提到的规则,因此需要交换父子结点的位置。
4.06这里由于父结点的6大于子结点的5,所以交换了这两个数字。重复这样的操作直到数据都符合规则,不再需要交换为止。
4.07现在,父结点的 1 小于子结点的 5,父结点的数字更小,所以不再交换。
4.08这样,往堆中添加数据的操作就完成了。
4.09从堆中取出数据时,取出的是最上面的数据。 这样,堆中就能始终保持最上面的数据最小。
4.010由于最上面的数据被取出,因此堆的结构也需要重新调整。
4.011按照01中说明的排列顺序,将最后的数据(此处为6)移动到最顶端。
4.012如果子结点的数字小于父结点的,就将父结点 与其左右两个子结点中较小的一个进行交换。
4.013这里由于父结点的6大于子结点(右)的5大于子 结点(左)的3,所以将左边的子结点与父结点进行交换。重复这个操作直到数据都符合规则,不再需要交换为止。
4.014现在,子结点(右)的 8 大于父结点的 6 大于子结点(左)的 4,需要将左边的子结点与父结点进行交换。
4.015这样,从堆中取出数据的操作便完成了。
以上为堆的了解。
4.1首先,在堆中存储所有的数据,并按降序来构建堆。
4.2现在,所有数据都存进堆里了。为了排序,需要再从堆中把数据一个个取出来。
4.3我们来试一试吧。首先取出根结点的数字7。
4.4重新构造堆。
4.5同样地,取出根结点的数字6,将它放在右数 第2个位置上。
4.6重新构造堆。
4.7重复上述操作直到堆变空为止。
4.8排序中……
4.9从堆中取出了所有数字,排序完成。
- void heapSet(int arr[], int start, int end) {
- // 建立父节点指标和子节点指标
- int dad = start;
- int son = dad * 2 + 1;
-
- while (son <= end) {
- /* 先比较两个子节点大小,选择最大的 */
- if (son + 1 <= end && arr[son] < arr[son + 1]) {
- son++;
- }
- /* 如果父节点大于子节点代表调整完毕,直接跳出函数 */
- if (arr[dad] > arr[son]) {
- return;
- }
- else { /* 否则交换父子內容再继续子节点和父节点比较 */
- int temp = arr[dad];
- arr[dad] = arr[son];
- arr[son] = temp;
- /* 调用swap函数交换数据,效率会更高 */
- //swap(arr[dad], arr[son]);
- dad = son;
- son = dad * 2 + 1;
- }
- }
- }
-
- void heapSort(int arr[], int len) {
- int i;
- /* 构造堆 */
- for (i = len / 2 - 1; i >= 0; i--) {
- heapSet(arr, i, len - 1);
- }
- /* 先将第一个元素和已排好元素前一位做交换,再重新调整,直到排序完毕 */
- for (i = len - 1; i > 0; i--) {
- int temp = arr[0];
- arr[0] = arr[i];
- arr[i] = temp;
- /* 调用swap函数交换数据,效率会更高 */
- //swap(arr[0], arr[i]);
- heapSet(arr, 0, i - 1);
- }
- }
- #include<iostream>
- using namespace std;
-
- int main() {
- int arr[100];
- int len;
- cout << "请输入数组个数:";
- cin >> len;
- cout << "请依次输入" << len << "个数组元素,以空格分开,回车结束" << endl;
- for (int i = 0; i < len; i++) {
- cin >> arr[i];
- }
- heapSort(arr, len);
- /*打印数组*/
- for (int i = 0; i < len; i++) {
- cout << arr[i] << " ";
- }
- cout << endl;
-
- return 0;
- }
归并排序算法会把序列分成长度相同的两个子序列,当无法继续往下分时(也就是每个子序列中只有一个数据时),就对子序列进行归并。归并指的是把两个排好序的子序列合并成一个有序序列。该操作会一直重复执行,直到所有子序列都归并为一个整体为止。
5.1首先,要把序列对半分割。
5.2先分成两段……
5.3再继续往下分……
5.4分割完毕。接下来对分割后的元素进行合并。
5.5把6和4合并,合并后的顺序为[4, 6]。
5.6接下来把3和7合并,合并后的顺序为[3, 7]。
5.7下面,我们来看看怎么合并 [4, 6]和[3, 7]。合并这种含 有多个数字的子序列时,要 先比较首位数字,再移动较 小的数字。
5.8由于4>3,所以移动3。
5.9同样地,再次比较序列中剩下的首位数字。
5.10由于4<7,所以移动4。
5.11由于6<7,所以移动6。
5.12最后移动剩下的7。
5.13递归执行上面的操作,直到所有的数字都合为 一个整体为止。
5.14这里也要比较两个子序列中的 首位数字。
5.15由于3>1,所以移动1。继续操作……
5.16合并完成,序列的排序也就完成了。
- #define MAX 500000
- #define SENTINEL 2000000000
- int L[MAX / 2 + 2], R[MAX / 2 + 2];
-
- void merge(int arr[], int len, int left, int mid, int right) {
- int i, j, k;
- int len1 = mid - left;
- int len2 = right - mid;
- /* 左边数据临时存储 */
- for (i = 0; i < len1; i++) {
- L[i] = arr[left + i];
- }
- /* 右边数据临时存储 */
- for (i = 0; i < len2; i++) {
- R[i] = arr[mid + i];
- }
- L[len1] = R[len2] = SENTINEL;
- i = 0;
- j = 0;
- /* 排序合并左右两边的数据 */
- for (k = left; k < right; k++) {
- if (L[i] <= R[j]) {
- arr[k] = L[i++];
- }
- else {
- arr[k] = R[j++];
- }
- }
- }
-
- void mergetSort(int arr[],int len, int left, int right) {
- if (left + 1 < right) {
- int mid = (left + right) / 2;
- /* 递归分左边 */
- mergetSort(arr, len, left, mid);
- /* 递归分右边 */
- mergetSort(arr, len, mid, right);
- /* 将左右两部分数据合并排序 */
- merge(arr, len, left, mid, right);
- }
- }
- #include<iostream>
-
- using namespace std;
-
- int main()
- {
- int arr[100];
- int len;
- cout << "请输入数组个数:";
- cin >> len;
- cout << "请依次输入" << len << "个数组元素,以空格分开,回车结束" << endl;
- for (int i = 0; i < len; i++) {
- cin >> arr[i];
- }
-
- mergetSort(arr, len,0,len);
- /*打印数组*/
- for (int i = 0; i < len; i++) {
- cout << arr[i] << " ";
- }
- cout << endl;
-
- return 0;
- }
快速排序算法首先会在序列中随机选择一个基准值(pivot),然后将除了基准值以外的数分 为“比基准值小的数”和“比基准值大的数”这两个类别,再将其排列成以下形式。
[ 比基准值小的数 ] 基准值 [ 比基准值大的数 ]
接着,对两个“[ ]”中的数据进行排序之后,整体的排序便完成了。对“[ ]”里面的数据 进行排序时同样也会使用快速排序。
6.1下面我们就来看看快速排序的步骤。
6.2在序列中随机选择一个基准值。这里选择了4。
6.3将其他数字和基准值进行比较。小于基准值的往左移,大于基准值的往右移。
6.4首先,比较3和基准值4。
6.5因为3<4,所以将3往左移。
6.6接下来,比较5和基准值4。
6.7因为5>4,所以将5往右移。
6.8对其他数字也进行同样的操作,最终结果如上图所示。
6.9把基准值 4 插入序列。这样,4 左边就是比它小 的数字,右边就是比它大的数字。
6.10分别对左边和右边的数据进行排序后,就能完 成整体的排序。
6.11两边的排序操作也和前面的一样。首先来看看 如何对右边的数据进行排序吧。
6.12随机选择一个基准值。这次选择6。
6.13把其余数据分别和基准值6进行比较,小于基 准值的就往左移,大于的就往右移。
6.14完成了大小比较和位置移动。
6.15和前面一样,对左右两边分别进行排序,进而完成整体排序。但是此时左边只有 5,所以已经是排序完 成的状态,不需要任何操作。而右边就和前面一样,先选出基准值。
6.16选择8作为基准值。
6.17将9和7分别与基准值8进行比较后,两个数字 的位置便分好了。8的两边都只有一个数据,因 此不需要任何操作。这样7、8、9便完成排序了。
6.18回到上一行,由于7、8、9完成了排序,所以 5、6、7、8、9也完成了排序。
6.19于是,最初选择的基准值4的右边排序完毕。
6.20左边也以相同的操作进行排序,整体的排序工作也就完成了。
- int partition(int arr[], int len, int p, int r) {
- int i, j;
- int x;
- int temp;
- /* 默认x为基准值 */
- x = arr[r];
- i = p - 1;
- /* 从下标为p到r-1进行for循环对比 */
- for (j = p; j < r; j++) {
- /* 以基准值x做对比,找到左边区域数据 */
- if (arr[j] <= x) {
- i++;
- /* 交换数据,左边p开始为基准值左值区域*/
- temp = arr[i];
- arr[i] = arr[j];
- arr[j] = temp;
- /* 调用swap函数交换数据,效率会更高 */
- //swap(arr[i], arr[j]);
- }
- }
- /* 别忘了将基准值放在左区域与右区域的中间 */
- temp = arr[i + 1];
- arr[i + 1] = arr[r];
- arr[r] = temp;
- /* 调用swap函数交换数据,效率会更高 */
- //swap(arr[i+1], arr[r]);
-
- /* 返回基准值所在下标 */
- return i + 1;
- }
-
- void quickSort(int arr[], int len, int p, int r) {
- int q;
- /* 先判断是否还可以继续划分左右区域 */
- if (p < r) {
- /* 以基准值划分左右区域后,返回基准值下标位置q */
- q = partition(arr, len, p, r);
- /* 递归快排基准值左边数据 */
- quickSort(arr, len, p, q - 1);
- /* 递归快排基准值右边数据 */
- quickSort(arr, len, q + 1, r);
- }
- }
- #include<iostream>
-
- using namespace std;
-
- int main()
- {
- int arr[100];
- int len;
- cout << "请输入数组个数:";
- cin >> len;
- cout << "请依次输入" << len << "个数组元素,以空格分开,回车结束" << endl;
- for (int i = 0; i < len; i++) {
- cin >> arr[i];
- }
-
- /* 注意第三、第四个参数均为数组有效下标 */
- quickSort(arr, len, 0, len-1);
- /*打印数组*/
- for (int i = 0; i < len; i++) {
- cout << arr[i] << " ";
- }
- cout << endl;
-
- return 0;
- }
我们之前提到过插入排序算法,而希尔排序算法是充分发挥插入排序法的高速算法。希尔排序法中,程序会重复进行以间隔为g的元素为对象的插入排序。
1.分两组初始增量第一趟 gap = length/2 = 4。
2.第二趟,增量缩小为 2。
3.第三趟,增量缩小为 1,得到最终排序结果。
- vector<int> G;
- /* 指定了间隔g的插入排序 */
- void insertionSort(int arr[], int len,int g) {
- int i, j;
- int v;
-
- for (i = g; i < len; i++) {
-
- /* v保存的是最新待排序的数据 */
- v = arr[i];
- /* j为已排序区域最右端数据下标 */
- j = i - g;
- /* 循环比较,直到找到v待插入的位置 */
- while (arr[j] > v) {
- /* 待插入v数据值较小,较大数据往后移动一位 */
- arr[j + g] = arr[j];
- j-=g;
- }
- /* 此时的arr[j]>v不成立,即v下标位置为j+g */
- arr[j + g] = v;
- }
- }
-
- void shellSort(int arr[], int len) {
- for (int h = 1;;) {
- if (h > len) {
- break;
- }
- G.push_back(h);
- h = 2 * h;
- }
- for (int i = G.size() - 1; i >= 0; i--) {
- insertionSort(arr, len, G[i]);
- }
- }
- #include<iostream>
- #include<algorithm>
- #include<vector>
-
- using namespace std;
- int main()
- {
- int arr[100];
- int len;
- cout << "请输入数组个数:";
- cin >> len;
- cout << "请依次输入" << len << "个数组元素,以空格分开,回车结束" << endl;
- for (int i = 0; i < len; i++) {
- cin >> arr[i];
- }
-
- shellSort(arr, len);
- /*打印数组*/
- for (int i = 0; i < len; i++) {
- cout << arr[i] << " ";
- }
- cout << endl;
-
- return 0;
- }
计数排序是一种稳定的排序算法,计数排序是一个非基于比较的排序算法,它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。 当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(n*log(n))的时候其效率反而不如基于比较的排序。
8.1找出待排序的数组中最大数据5和最小数据0,统计数组中每个值为i的元素出现的次数,存入数组C的第i项,对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)。
8.2 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1,从最右边放第一个数据5。
8.3放数据0
8.4放数据5
8.5放数据1
8.6放数据3
8.7放数据0
8.8放数据5
8.9 放数据4,新数组B中数据已排序完成。
- #include <iostream>
- using namespace std;
- const int MAXN = 100000;
- const int k = 1000; // range(范围)
- int arr[MAXN],brr[MAXN];
- int crr[MAXN] = { 0 };
-
- int main() {
- int len;
- cout << "请输入数组个数:";
- cin >> len;
- cout << "请依次输入" << len << "个数组元素,以空格分开,回车结束" << endl;
- for (int i = 0; i < len; ++i) {
- cin >> arr[i];
- crr[arr[i]]++;
- }
- for (int i = 1; i < k; ++i)
- crr[i] += crr[i - 1];
- for (int i = len - 1; i >= 0; --i)
- brr[--crr[arr[i]]] = arr[i];
- for (int i = 0; i < len; ++i)
- cout << brr[i] << " ";
- cout<<endl;
- return 0;
- }
桶排序的思想就是把待排序的数尽量均匀地放到各个桶中,再对各个桶进行局部的排序,最后再按序将各个桶中的数输出,即可得到排好序的数。
这个算法就好比有 11 个桶,编号从 0~10。每出现一个数,就在对应编号的桶中放一个小旗子,最后只要数数每个桶中有几个小旗子就 OK 了。
例如 2 号桶中有 1 个小旗子,表示 2 出现了一次;3 号桶中有 1 个小旗子,表示 3 出现了一次;5 号桶中有 2 个小旗子,表示 5 出现了两次;8 号桶中有 1 个小旗子,表示 8 出现了一次。
- #include<iostream>
- using namespace std;
- #define max 101
-
-
- void bucketSort(int arr[], int len, int buck[]) {
- for (int i = 0; i < max; i++) {
- for (int j = 0; j < buck[i]; j++) {
- cout << i << " ";
- }
- }
- cout << endl;
- }
-
- int main() {
- int arr[max-1];
- int len;
- int buck[max] = { 0 };
- cout << "请输入数组个数:";
- cin >> len;
- cout << "请依次输入" << len << "个数组元素,以空格分开,回车结束" << endl;
- for (int i = 0; i < len; i++) {
- cin >> arr[i];
- buck[arr[i]]++;
- }
- bucketSort(arr, len,buck);
-
- return 0;
- }
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
它是这样实现的:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
- #include <iostream>
- #include <vector>
- using namespace std;
-
- /* 辅助函数,求数据的最大位数 */
- int maxbit(int arr[], int len)
- {
- int maxData = arr[0];
- /* 先求出最大数,再求其位数 */
- for (int i = 1; i < len; ++i)
- {
- if (maxData < arr[i])
- maxData = arr[i];
- }
- int d = 1;
- int p = 10;
- while (maxData >= p)
- {
- maxData /= 10;
- ++d;
- }
- return d;
- }
- void radixsort(int arr[], int len) {
- int d = maxbit(arr, len);
- int* tmp = new int[len];
- int* count = new int[10];
- int i, j, k;
- int radix = 1;
- for (i = 1; i <= d; i++) {
- for (j = 0; j < 10; j++) {
- /* 每次分配前清空计数器 */
- count[j] = 0;
- }
-
- for (j = 0; j < len; j++){
- /* 统计每个桶中的记录数 */
- k = (arr[j] / radix) % 10;
- count[k]++;
- }
- for (j = 1; j < 10; j++) {
- /* 将tmp中的位置依次分配给每个桶 */
- count[j] = count[j - 1] + count[j];
- }
- /* 将所有桶中记录依次收集到tmp中 */
- for (j = len - 1; j >= 0; j--) {
- k = (arr[j] / radix) % 10;
- tmp[count[k] - 1] = arr[j];
- count[k]--;
- }
- /* 将临时数组的内容复制到arr中 */
- for (j = 0; j < len; j++) {
- arr[j] = tmp[j];
- }
- radix = radix * 10;
- }
- delete[]tmp;
- delete[]count;
- }
- int main()
- {
- int arr[100];
- int len;
- cout << "请输入数组个数:";
- cin >> len;
- cout << "请依次输入" << len << "个数组元素,以空格分开,回车结束" << endl;
- for (int i = 0; i < len; ++i) {
- cin >> arr[i];
- }
- radixsort(arr, len);
- /*打印数组*/
- for (int i = 0; i < len; i++) {
- cout << arr[i] << " ";
- }
- cout << endl;
- return 0;
- }
1.石田宝辉 宫崎修一:《我的第一本算法书》
2.渡部有隆:《挑战程序设计竞赛2》
2.啊哈磊:《啊哈!算法》
3.王晓华:《算法的乐趣》
4.托马斯.克尔曼等:《算法导论》
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。