#include <iostream>
#include<vector> //桶排序用到
#include<time.h> //生成随机数用到
using namespace std;
/*十大排序算法*/ //https://mp.weixin.qq.com/s/IAZnN00i65Ad3BicZy5kzQ
// 1、稳定排序:如果 a 原本在 b 的前面,且 a == b,排序之后 a 仍然在 b 的前面,则为稳定排序。
// 2、非稳定排序:如果 a 原本在 b 的前面,且 a == b,排序之后 a 可能不在 b 的前面,则为非稳定排序。
// 3、原地排序:原地排序就是指在排序过程中不申请多余的存储空间,只利用原来存储待排数据的存储空间进行比较和交换的数据排序。
// 4、非原地排序:需要利用额外的数组来辅助排序。
// 5、时间复杂度:一个算法执行所消耗的时间。
// 6、空间复杂度:运行完一个算法所需的内存大小
/*1.选择排序*/
//(1)从第一个元素开始往后遍历,找到最小的那个,将其与第一个元素交换位置,
//(2)再从第二个元素开始向后遍历,找到最小的那个元素,将其交换之第二个元素的位置,依次类推
//n+(n-1)+(n-2)+....
// 1、时间复杂度:O(n^2)
// 2、空间复杂度:O(1)
// 3、非稳定排序
// 4、原地排序
int selectSort(int a[], int n)
{
cout << "This is selectSort \n";
for (int i = 0; i < n; i++)
{
int min = i;
for (int j = i + 1; j < n; j++)
{
if (a[min]>a[j])
{
min = j; //找到值最小的下标
}
}
int tmp = a[i]; //将最小的值交换到最前面来
a[i] = a[min];
a[min] = tmp;
}
return 0;
}
/*2.插入排序*/
//(1)从数组第2个元素开始抽取元素。
//(2)把它与左边第一个元素比较,如果左边第一个元素比它大,则继续与左边第二个元素比较下去,
// 直到遇到不比它大的元素,然后插到这个元素的右边。
//(3)继续选取第3,4,….n个元素,重复步骤 2 ,选择适当的位置插入。
//1、时间复杂度:O(n^2)
//2、空间复杂度:O(1)
//3、稳定排序
//4、原地排序
int insertSort(int a[], int n) //从左边第二个开始往前插
{
cout << "This is insertSort \n";
for (int i = 1; i < n; i++)
{
int tmp = a[i];
int k = i - 1;
while ( //先确定要插入的位置
k >= 0 && a[k]>tmp
)
{
k--;
}
for (int j = i; j > k + 1; j--) //确定位置右侧全部右移一位
{
a[j] = a[j - 1];
}
a[k + 1] = tmp;
}
return 0;
}
/*3.冒泡排序*/
// 把第一个元素与第二个元素比较,如果第一个比第二个大,则交换他们的位置。
// 接着继续比较第二个与第三个元素,如果第二个比第三个大,则交换他们的位置...
//1、时间复杂度:O(n^2)
//2、空间复杂度:O(1)
//3、稳定排序
//4、原地排序
int bubbleSort(int a[], int n)
{
cout << "This is bubbleSort \n";
for (int i = n; i > 0; i--)
{
for (int j = 0; j < i; j++)
{
if (a[j]>a[j + 1])
{
int tmp = a[j];
a[j] = a[j + 1];
a[j + 1] = tmp;
}
}
}
return 0;
}
/*4.希尔排序*/
//插入排序的一个变种,解决原数组的一个元素如果距离它正确的位置很远的话,则需要与相邻元素交换很多次才能到达正确的位置的问题
//希尔排序就是为了加快速度简单地改进了插入排序,交换不相邻的元素以对数组的局部进行排序。
//先让数组中任意间隔为 h 的元素有序,
//刚开始 h 的大小可以是 h = n / 2,接着让 h = n / 4,让 h 一直缩小,
//当 h = 1 时,也就是此时数组中任意间隔为1的元素有序,此时的数组就是有序的了。
//1、时间复杂度:O(nlogn)
//2、空间复杂度:O(1)
//3、非稳定排序
//4、原地排序
int shellSort(int a[], int n)
{
cout << "This is shellSort \n";
for (int h = n / 2; h > 0; h = h / 2) //对每组间隔为h的分组进行排序,刚开始h=n/2 h也是分组的个数
{ //分组间距越小每个分组的元素越多
for (int i = h; i < n; i++) //0,1,2,3,4,5,6,7,8,9,10,
{ //间隔为3的分组:0,3,6,9
int temp = a[i]; // 1,4,7,10
int k; // 2,5,8
for (k = i - h; k >= 0 && temp<a[k]; k = k - h)
{
a[k + h] = a[k];
}
a[k + h] = temp;
}
}
return 0;
}
/*5.归并排序*/
//将一个大的无序数组有序,我们可以把大的数组分成两个,
//然后对这两个数组分别进行排序,之后在把这两个数组合并成一个有序的数组。
//由于两个小的数组都是有序的,所以在合并的时候是很快的。
//通过递归的方式将大的数组一直分割,直到数组的大小为 1,
//此时只有一个元素,那么该数组就是有序的了,
//之后再把两个数组大小为1的合并成一个大小为2的,再把两个大小为2的合并成4的 …..
//直到全部小的数组合并起来
//1、时间复杂度:O(nlogn)
//2、空间复杂度:O(n)
//3、稳定排序
//4、非原地排序
//合并两个序列
void mergeArray(int arr[], int first, int mid, int last, int temp[]) //合并数组
{
int i = first;
int j = mid + 1;
int m = mid;
int n = last;
int k = 0;
while (i <= m && j <= n)
{
if (arr[i] <= arr[j])
temp[k++] = arr[i++];
else
temp[k++] = arr[j++];
}
while (i <= m)
temp[k++] = arr[i++];
while (j <= n)
temp[k++] = arr[j++];
for (i = 0; i < k; i++)
arr[first + i] = temp[i];
}
void mySort(int arr[], int first, int last, int temp[]) //递归
{
if (first < last)
{
int mid = (first + last) / 2;
mySort(arr, first, mid, temp);
mySort(arr, mid + 1, last, temp);
mergeArray(arr, first, mid, last, temp);
}
}
bool mergeSort(int arr[], int len) //归并排序
{
cout << "This is mergeSort \n";
int*p = new int[len]; //申请新的空间
if (NULL == p)
return false;
mySort(arr, 0, len - 1, p);
delete[] p;
return true;
}
//非递归式归并排序
//合并函数
void merge(int arr[], int left, int mid, int right)
{
//先用一个临时数组把它们合并汇总起来
int *a = new int[right - left + 1];
int i = left;
int j = mid + 1;
int k = 0;
while (i <= mid && j <= right)
{
if (arr[i] < arr[j])
{
a[k++] = arr[i++];
}
else
{
a[k++] = arr[j++];
}
}
while (i <= mid) a[k++] = arr[i++];
while (j<=right) a[k++] = arr[j++];
//把临时数组复制到原数组
for (i = 0; i < k; i++) arr[left++] = a[i];
delete[] a;
}
int mergeSortWithoutRecursion(int arr[], int len)
{
cout << "This is mergeSortWithoutRecursion \n";
//子数组大小分别为1,2,4,8...
//刚开始合并的数组大小是1,接着是2,接着4....
for (int i = 1; i < len; i++)
{
//数组进行划分
int left = 0;
int mid = left + i - 1;
int right = mid + i;
//进行合并,对数组大小为i的数组进行两两合并
while (right < len)
{//合并函数和递归型的合并函数一样
merge(arr, left, mid, right);
left = right + 1;
mid = left + i - 1;
right = mid + i;
}
//还有一些被遗漏的数组没合并,千万别忘了
//因为不可能每个子数组的大小刚好为i
if (left < len && mid < len)
{
merge(arr, left, mid, len - 1);
}
}
return 1;
}
/*6.快速排序*/
//我们从数组中选择一个元素,我们把这个元素称之为中轴元素吧,然后把数组中所有小于中轴元素的元素放在其左边,
//所有大于或等于中轴元素的元素放在其右边,显然,此时中轴元素所处的位置就是最终排序后的位置。
//从中轴元素那开始将数组分为两个小数组,在递归地方式在小数组中选择一个中轴元素重复上述步骤,最终整个数组变得有序。
// 1、时间复杂度:O(nlogn)
// 2、空间复杂度:O(logn)
// 3、非稳定排序
// 4、原地排序
int Partion(int arr[], int low, int high) //一次找基准过程
{
int temp = arr[low];
while (low<high) //当low=high时退出循环,此时的位置为基准位置
{
while (low<high && arr[high] >= temp)
{
high--;
}
if (low >= high)
{
break;
}
else
{
arr[low] = arr[high];
}
while (low<high &&arr[low] <= temp)
{
low++;
}
if (low >= high)
{
break;
}
else
{
arr[high] = arr[low];
}
}
arr[low] = temp;
return low;
}
void Quick(int arr[], int start, int end)
{
int par = Partion(arr, start, end); //第一次找基准
if (par>start + 1)
{
Quick(arr, start, par - 1);
}
if (par<end - 1)
{
Quick(arr, par + 1, end);
}
}
void quickSort(int arr[], int len)
{
cout << "This is quickSort \n";
Quick(arr, 0, len - 1);
}
//7.堆排序
//堆的特点就是堆顶的元素是一个最值,大顶堆的堆顶是最大值,小顶堆则是最小值。
// 堆排序就是把堆顶的元素与最后一个元素交换,交换之后破坏了堆的特性,我们再把堆中剩余的元素再次构成一个堆,
// 然后再把堆顶元素与最后第二个元素交换….如此往复下去,等到剩余的元素只有一个的时候,此时的数组就是有序的了。
// 1、时间复杂度:O(nlogn)
// 2、空间复杂度:O(1)
// 3、非稳定排序
// 4、原地排序
void downAdjust(int arr[], int parent, int n)
{
//临时保存要下沉的元素
int temp = arr[parent];
//定位左孩子节点的位置
int child = 2 * parent + 1;
//开始下沉
while (child<=n)
{ //如果右孩子比左孩子大,则定位到右孩子
if (child + 1 <= n && arr[child] < arr[child + 1]) child++;
//如果孩子节点小于或等于父亲节点,则下沉结束
if (arr[child] <= temp) break;
//父节点进行下沉
arr[parent] = arr[child];
parent = child;
child = 2 * parent + 1;
}
arr[parent] = temp;
}
void heapSort(int arr[], int n)
{
cout << "This is heapSort \n";
//构建大顶堆
for (int i = (n-2)/2; i >= 0; i--)
{
downAdjust(arr, i, n - 1);
}
//进行堆排序
for (int i = n-1; i >= 1 ; i--)
{
//把堆顶元素与最后一个元素交换
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
//把打乱的堆进行调整恢复堆的特性
downAdjust(arr, 0, i - 1);
}
}
//8.计数排序
//计数排序是一种适合于最大值和最小值的差值不是不是很大的排序。
//基本思想:就是把数组元素作为数组的下标,然后用一个临时数组统计该元素出现的次数,
//例如 temp[i] = m, 表示元素 i 一共出现了 m 次。最后再把临时数组统计的数据从小到大
//汇总起来,此时汇总起来是数据是有序的。
//1、时间复杂度:O(n + k)
//2、空间复杂度:O(k)
//3、稳定排序
//4、非原地排序
void countSort(int arr[], int n)
{
cout << "This is countSort \n";
if (nullptr == arr || n < 2) return;
int min = arr[0];
int max = arr[0];
//寻找数组最大值与最小值
for (int i = 1; i < n; i++)
{
if (max < arr[i]) max = arr[i];
if (min > arr[i]) min = arr[i];
}
//创建大小为max - min + 1的临时数组
int d = max - min + 1;
int *temp = new int[d];
for (int i = 0; i < n; ++i) temp[i] = 0; //C++要赋初值不然无法排序的
//统计元素i出现的次数
for (int i = 0; i < n; i++)
{
temp[arr[i]-min]++;
}
int k = 0;
//把临时数组统计好的数据汇总到原数组
for (int i = 0; i <= d; i++)
{
for (int j = temp[i]; j >0; j--)
{
arr[k++] = i+min;
}
}
}
//9.桶排序
//桶排序就是把最大值和最小值之间的数进行瓜分,例如
//分成 10 个区间,10个区间对应10个桶,我们把各元素
//放到对应区间的桶中去,再对每个桶中的数进行排序,
//可以采用归并排序,也可以采用快速排序之类的。
//之后每个桶里面的数据就是有序的了,我们在进行合并汇总。
//1、时间复杂度:O(n + k)
//2、空间复杂度:O(n + k)
//3、稳定排序
//4、非原地排序
const int BUCKET_NUM = 10;
struct ListNode {
explicit ListNode(int i = 0) :mData(i), mNext(NULL) {}
ListNode* mNext;
int mData;
};
ListNode* insert(ListNode* head, int val) {
ListNode dummyNode;
ListNode *newNode = new ListNode(val);
ListNode *pre, *curr;
dummyNode.mNext = head;
pre = &dummyNode;
curr = head;
while (NULL != curr && curr->mData <= val) {
pre = curr;
curr = curr->mNext;
}
newNode->mNext = curr;
pre->mNext = newNode;
return dummyNode.mNext;
}
ListNode* Merge(ListNode *head1, ListNode *head2) {
ListNode dummyNode;
ListNode *dummy = &dummyNode;
while (NULL != head1 && NULL != head2) {
if (head1->mData <= head2->mData) {
dummy->mNext = head1;
head1 = head1->mNext;
}
else {
dummy->mNext = head2;
head2 = head2->mNext;
}
dummy = dummy->mNext;
}
if (NULL != head1) dummy->mNext = head1;
if (NULL != head2) dummy->mNext = head2;
return dummyNode.mNext;
}
void bucketSort(int arr[],int n ) {
cout << "This is bucketSort \n";
vector<ListNode*> buckets(BUCKET_NUM, (ListNode*)(0));
for (int i = 0; i<n; ++i) {
int index = arr[i] / BUCKET_NUM;
ListNode *head = buckets.at(index);
buckets.at(index) = insert(head, arr[i]);
}
ListNode *head = buckets.at(0);
for (int i = 1; i<BUCKET_NUM; ++i) {
head = Merge(head, buckets.at(i));
}
for (int i = 0; i<n; ++i) {
arr[i] = head->mData;
head = head->mNext;
}
}
//10.基数排序
//先以个位数的大小来对数据进行排序,接着以十位数的大小来多数进行排序,接着以百位数的大小……
//排到最后,就是一组有序的元素了。不过,他在以某位数进行排序的时候,是用“桶”来排序的。
int maxbit(int data[], int n) //辅助函数,求数据的最大位数
{
int maxData = data[0]; ///< 最大数
/// 先求出最大数,再求其位数,这样有原先依次每个数判断其位数,稍微优化点。
for (int i = 1; i < n; ++i)
{
if (maxData < data[i])
maxData = data[i];
}
int d = 1;
int p = 10;
while (maxData >= p)
{
//p *= 10; // Maybe overflow
maxData /= 10;
++d;
}
return d;
/* int d = 1; //保存最大的位数
int p = 10;
for(int i = 0; i < n; ++i)
{
while(data[i] >= p)
{
p *= 10;
++d;
}
}
return d;*/
}
void radioSort(int data[], int n)
{
cout << "This is radioSort \n";
int d = maxbit(data, n);
int *tmp = new int[n];
int *count = new int[10]; //计数器
int i, j, k;
int radix = 1;
for (i = 1; i <= d; i++) //进行d次排序
{
for (j = 0; j < 10; j++)
count[j] = 0; //每次分配前清空计数器
for (j = 0; j < n; j++)
{
k = (data[j] / radix) % 10; //统计每个桶中的记录数
count[k]++;
}
for (j = 1; j < 10; j++)
count[j] = count[j - 1] + count[j]; //将tmp中的位置依次分配给每个桶
for (j = n - 1; j >= 0; j--) //将所有桶中记录依次收集到tmp中
{
k = (data[j] / radix) % 10;
tmp[count[k] - 1] = data[j];
count[k]--;
}
for (j = 0; j < n; j++) //将临时数组的内容复制到data中
data[j] = tmp[j];
radix = radix * 10;
}
delete[]tmp;
delete[]count;
}
int main()
{
int n,select,a[100];
cout << "This is sort tutorial ! " << endl;
cout << "How many numbers do you want to set ? " << endl;
cin >> n;
srand((int)time(NULL)); // 设置时间为随机点
for (int i = 0; i < n; i++) // 产生n个随机数
a[i] = rand() % 100;
cout << "Those numbers before sort is : \n";
for (int i = 0; i < n; i++)
{
cout << a[i] << " ";
if (0 == (i+1) % 10) cout << endl;
}
cout << "\n Please select the method: \n";
cout << "1.selectSort \t 2.insertSort \t 3.bubbleSort \t 4.shellSort \n";
cout << "5.mergeSort \t 6.mergeSortWithoutRecursion \t 7.quickSort \n";
cout << "8.heapSort \t 9.countSort \t 10.bucketSort \t 11.radioSort \n";
cin >> select;
switch (select)
{
case 1:selectSort(a, n); break; //选择排序
case 2:insertSort(a, n); break; //插入排序
case 3:bubbleSort(a, n); break; //冒泡排序
case 4:shellSort(a, n); break; //希尔排序
case 5:mergeSort(a, n); break; //归并排序
case 6:mergeSortWithoutRecursion(a, 10); break; //非递归归并排序
case 7:quickSort(a, n); break; //快速排序
case 8:heapSort(a, n); break; //堆排序
case 9:countSort(a, n); break; //计数排序
case 10:bucketSort(a, n); break; //桶排序
case 11:radioSort(a, n); break; //基数排序
default:cout << "method select error";
break;
}
// selectSort(a,10); //选择排序
// insertSort(a,10); //插入排序
// bubbleSort(a,10); //冒泡排序
// shellSort(a,10); //希尔排序
// mergeSort(a,10); //归并排序
// mergeSortWithoutRecursion(a, 10); //非递归归并排序
// quickSort(a,10); //快速排序
// heapSort(a, 10); //堆排序
// countSort(a, 10); //计数排序
// bucketSort(a, 10); //桶排序
// radioSort(a, 10); //基数排序
cout << "after sort :" << endl;
for (int i = 0; i < n; i++)
{
cout << a[i] << " ";
if (0 == (i + 1) % 10) cout << endl;
}
cout << endl;
return 0;
}
十大排序算法(C++实现)
最新推荐文章于 2024-08-24 13:21:44 发布