练习了十一种排序算法的C++实现:以下依次为,冒泡、选择、希尔、插入、二路归并、快排、堆排序、计数排序、基数排序、桶排序,可建立sort.h和main.cpp将代码放入即可运行。如有错误,请指出更正,谢谢交流。
Python版本实现:王培军:https://blog.csdn.net/qq_33287645/article/details/81702993
// sort.h
# include <iostream>
# include "chain.h"
using namespace std;
typedef struct ChainNode {
int value;
struct ChainNode *next;
//ChainNode() {}
ChainNode(int value, ChainNode * next) {
this->value = value;
this->next = next;
}
} CNode, *Chain;
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
void blob(int arr[], int len) {
for (int i = len - 1; i > 0; i--)
for (int j = 0; j < i; j++)
if (arr[j] > arr[j + 1])
swap(arr[j], arr[j + 1]);
}
void select(int arr[], int len) {
for (int i = 0; i < len; i++) {
int min = i;
for (int j = i; j < len; j++)
if (arr[j] < arr[min])
min = j;
swap(arr[i], arr[min]);
}
}
void shell(int arr[], int len) {
// https://zh.wikipedia.org/wiki/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F
int i, j, step;
for (step = len >> 1; step > 0; step >>= 1) // 初始步长为n/2,每次对步长取半直到步长达到1
for (i = step; i < len; i++) { // 从第step个数开始,依次将后面的数与前面的step步长数进行比较
int temp = arr[i]; // 将大于当前值的希尔序列中的值,不断地进行后移,最终将当前值放到希尔序列的合适位置
for (j = i - step; j >= 0 && arr[j] > temp; j -= step) // 初始为当前位置的第前step个数,比较arr[j + step] 与 arr[j]
arr[j + step] = arr[j]; // 希尔序列中前面的值,往后移(第一次会冲掉初始值temp)
arr[j + step] = temp;
}
}
void insert(int arr[], int len) {
for (int i = 1; i < len; i++) {
int temp = arr[i];
int j;
for (j = i - 1; j >= 0 && temp < arr[j]; j--)
arr[j + 1] = arr[j];
arr[j + 1] = temp;
}
}
void merge(int arr[], int temp[], int start, int end) {
if (start >= end)
return;
int mid = (start + end) / 2;
int start1 = start, end1 = mid;
int start2 = mid + 1, end2 = end;
merge(arr, temp, start1, end1);
merge(arr, temp, start2, end2);
int k = start; // 从start起始位置开始合并进temp中
while (start1 <= end1 && start2 <= end2) { // 对两个数组同时处理,小的放到temp中
temp[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
}
while (start1 <= end1) // 归并的两个数组不一定长,如果start1剩下,则合并进temp
temp[k++] = arr[start1++];
while (start2 <= end2) // 如果第二个数组剩下,将其合并进temp
temp[k++] = arr[start2++];
for (int k = start; k <= end; k++) // 拷贝回原数组
arr[k] = temp[k];
}
void quick(int arr[], int start, int end) {
if (start > end)
return;
int left = start, right = end;
int mid = arr[left]; // 先在左边第一个数挖个坑
while (left < right) {
while (left < right && arr[right] >= mid) // 从右边开始找小于中间值得第一个数
right--;
if (left < right) {
arr[left] = arr[right]; // 将‘右边的小于中间值的那个数’ 填入到 ‘左边的坑(初始为第一个)’
left++; // 左边坑被填好之后,处理下一个,往右挪一下
}
while (left < right && arr[left] <= mid)
left++;
if (left < right) {
arr[right] = arr[left]; // 将‘左边大于中间中的数’ 填入到 ‘右边被挖出来的坑’
right--; // 右边坑被填好之后,处理下一个,往做挪一下
}
}
arr[left] = mid; // //退出时,left等于right。将x填到这个坑中。这个坑也是中间数。
quick(arr, start, left - 1); // 中间的数不需要再排序
quick(arr, left + 1, end);
}
void max_heapify(int arr[], int start, int end) {
// 初始化dad和son
int dad = start;
int son = dad * 2 + 1;
while (son <= end) { // 在不停地向下调整过程中,son不越过堆尾的前提下
if (son + 1 <= end && arr[son + 1] > arr[son]) // 在不越过堆尾的前提下,找出最大的孩子节点的位置
son++;
if (arr[dad] > arr[son]) // 如果父节点值大于最大孩子节点值,则返回
return;
else {
swap(arr[dad], arr[son]); // 交换父子节点值
dad = son; // 重新赋值dad和son,进一步的向下调整
son = dad * 2 + 1;
}
}
}
void heap(int arr[], int len) {
// https://zh.wikipedia.org/wiki/%E5%A0%86%E6%8E%92%E5%BA%8F
// 建立初始大根堆,只有这里考虑的是数量号len,其余地方全部考虑索引号
for (int i = len / 2 - 1; i >= 0; i--) // 从最后一个父节点,依次向前开调整,len/2 - 1 可以确定最后一个父节点的索引位置。这里使用节点数量len确定索引位置i,而不是索引数len-1(67确定索引2,(位置Wie3))
max_heapify(arr, i, len - 1);
// 逆序生成堆排序结果。依次取出堆顶元素,与最近一个尾部交换,重新调整大根堆,此时只需要调整堆顶一个元素即可
for (int i = len - 1; i > 0; i--) {
swap(arr[0], arr[i]); // 拿下堆顶元素,与堆尾元素交换
max_heapify(arr, 0, i - 1); // 调整当前堆尾之前的所有元素为大根堆,只需要向下调整0号位置
}
}
void count(int arr[], int len) {
// https://zh.wikipedia.org/wiki/%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F
// 找到数组中的最大值最小值
int min, max;
min = max = arr[0];
for (int i = 0; i < len; i++) {
if (arr[i] > max)
max = arr[i];
else if (arr[i] < min)
min = arr[i];
}
// 确定最大值,最小值之间的范围差,并初始化相应大小的数组
int lenC = max - min + 1;
int *arrC = new int[lenC];
for (int k = 0; k < lenC; k++)
arrC[k] = 0;
// 遍历原始数组,将对应的新计数数组上的值+1,每个值可以看作是一个桶
for (int i = 0; i < len; i++) {
int index = arr[i] - min;
arrC[index] += 1;
}
// 遍历计数数组,输出上面关联的数值(桶里的所有个数的数值)
for (int k = 0, i = 0; k < lenC; k++) {
if (arrC[k] != 0) {
for (int j = 0; j < arrC[k]; j++)
arr[i++] = k + min; // 这里的k+min就是真实值
}
}
}
int getMaxBit(int arr[], int len) {
int max = arr[0];
for (int i = 0; i < len; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
int maxBit = 0;
while (max >= 10) {
max /= 10;
maxBit++;
}
return ++maxBit;
}
void radix(int arr[], int len) {
// 求最大位数,个位为1,十位为2...
int maxBit = getMaxBit(arr, len);
cout << maxBit << endl;
int *temp = new int[len];
// 从小到达,遍历每一位进行排序
int radix = 1;
for (int i = 0; i < maxBit; i++) {
// 计数器清空
int buckets[10] = { 0 };
// 遍历所有数据,将其放入到桶中
for (int j = 0; j < len; j++)
{
int d = arr[j] / radix % 10;
buckets[d] ++;
}
// 修订bucket数组中值,索引位0的数值,代表0桶内有多少个数,索引位1上的值,代表1+0上的桶内一共有多少个数
// 使得bucket中的数组值,变成其索引所对应基数的偏移量,注意,默认均加了0号索引上的数量。
for (int j = 1; j < 10; j++) {
buckets[j] = buckets[j] + buckets[j - 1];
cout << buckets[j] << endl;
}
// 将bucket中的数值存储到temp数组中
for (int j = len - 1; j >= 0; j--) {
int d = arr[j] / radix % 10; // 找到桶索引,即基数
temp[buckets[d] - 1] = arr[j]; // 桶索引中的数从1开始计数的,比如索引0上1个数,其值为1,但实际temp中索引从0开始。所以减1.
buckets[d]--; // 存储一个,则对应的基数上数值个数减一。这里如果有6、7、8三个数,先赋值给index为7的位置,然后6,然后5.
}
// 将temp数组赋值给原数组,此时,完成低位上的排序有序
for (int j = 0; j < len; j++)
arr[j] = temp[j];
radix *= 10;
}
delete []temp;
}
void bucket(int arr[], int len) {
// 初始化及设置固定空桶数
int bucketNum = 10;
CNode **bucketTable = new CNode *[bucketNum]; // 分配动态二维(指针)数组,bucketNum个CNode*数组
// vector<vector<CNode *> > bucketTable(bucketNum,vector<CNode *>(1)?)); // vector方法
for (int i = 0; i < bucketNum; i++) // 为指针数组的每个元素,分配一个元素
bucketTable[i] = new CNode(0, NULL); // 初始化桶内元素计数,用第一个CNode?
for (int i = 0; i < len; i++) {
// 计算当前元素映射后的桶号
int index = arr[i] / bucketNum;
CNode *p = bucketTable[index];
// 将数据放到对应的空桶中, 并进行排序
CNode *pNode = new CNode(arr[i], NULL); // 添加数组中的元素到桶中
if (p->value == 0) { // 该桶中还没有数据
bucketTable[index]->next = pNode;
(bucketTable[index]->value)++; // 桶中数量加1
}
else {
// 链表结构的插入排序
while (p->next != NULL && p->next->value <= pNode->value)
p = p->next;
pNode->next = p->next;
p->next = pNode;
(bucketTable[index]->value)++;
}
}
// 拼接不为空的桶中的数据,得到结果
for (int i = 0; i < bucketNum; i++) {
CNode *p = bucketTable[i]->next; // bucketTable[i]这个CNode中包含了计数,其第一个next为第一个元素
while (p) {
cout << p->value << ",";
p = p->next;
}
}
for (int i = 0; i < bucketNum; i++)
{
delete[bucketNum]bucketTable[i]; // 先释放指针数组的每个元素指向的数组
bucketTable[i] = NULL;
}
delete[bucketNum]bucketTable; // 再释放该指针数组
bucketTable = NULL;
// 创建二维动态数组
// https://blog.csdn.net/samuelcoulee/article/details/8674388
}
// main.cpp
# include <iostream>
# include "sort.h"
using namespace std;
void testSort() {
int temp[8];
int arr[] = { 7, 9, 1, 2, 6, 0, 5 , 8 };
int len = sizeof(arr) / sizeof(int);
for (int i = 0; i < len; i++)
cout << arr[i] << ", ";
// blob(arr, len);
// select(arr, len);
// insert(arr, len);
// merge(arr, temp, 0, len-1);
// quick(arr, 0, len - 1);
// heap(arr, len);
// shell(arr, len);
// count(arr, len);
// radix(arr, len);
bucket(arr, len);
for (int i = 0; i < len; i++)
cout << arr[i] << ", ";
}
int main() {
testSort();
}