希尔排序
简介:
希尔排序又称为缩小增量排序,是基于插入排序上进行的优化后的排序,是一种非稳定排序
希尔排序是把序列按一定间隔分组,对每组使用直接插入排序;随着间隔减小,一直到1,使得整个序列有序。
步骤:
1:先用一个增量将待排序的数组分为若干个子数组,每个子数组中的下标间距就是增量值
2:然后对每个子数组进行插入排序
3:再用第二个增量进行相同操作
(与插入排序类似,多了个增量)
注意事项:
1: 增量元素最好互质,如果不互质,可能会导致小的增量起不到效果
2: 最后一个增量必须为1
代码实现
void ShellSort(int *a,int len)
{
int h = 1; //h为增量
while (h < len / 3 )
{
h = h * 3 + 1; //给增一个初始的值
}
while (h >= 1)
{
for (int i = h; i < len; i++)
{
int temp = a[i];
int j = i - h;
while (j >= 0 && a[j]>temp)
{
a[j + h] = a[j];
j -= h;
len2++;
}
a[j + h] = temp;
}
h /= 3;
}
}
时间复杂度:
最好情况
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
最坏情况
O
(
n
2
)
O(n^2)
O(n2)
基数排序
基数排序的概念
基数排序是一种分配式的排序,又称为桶排序,它的原理是通过键值(计算机及应用程序执行时使用的实际配置信息和数据,这里用来表示数据存放到桶中的依据),将排序的数据存放到相应的桶中,进而实现排序。
具体步骤(以整数排序为例)
1.获取所有数中的最高位(最大数是几位数);
2.创建一个桶的数组,大小为10,编号为0-9;
3.将排序的数据,以个位上的值为键值,放入桶中,然后将这些桶子中的数值重新串接起来,这样就可以保证每个数个位上的数已经排好序了;
4.再以十位上的值为键值,把个位已经排好的数据放入桶中,然后继续将桶中的数据重新串起来,这样就可以保证每个数个位和十位上的数已经排好序了;
5.按照这样的方法,依次循环,直到键值为最高位。
![图片来源:https://www.runoob.com/w3cnote/radix-sort.html](https://img-blog.csdnimg.cn/2020081615354366.gif#pic_center)
代码实现
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;//定义一个最大值的位数
while (maxData >= 10)
{
maxData /= 10;
++d;
}
return d;
}
dixsort(int* data, int n) //基数排序,data为数组,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;
}
时间复杂度
O ( n ) O(n) O(n)
归并排序
归并排序的概念
归并排序是比较类中最快的排序算法,其次是快速排序和堆排序,两者排序速度不相上下,但是数据的初始状态对堆排序的影响不大但是却能极大的影响快速排序的排序效率。
归并排序的排序思路
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。主要思路是将一个需要排序的序列通过递归不断的划分成一个小的区间,直至每一个小的区间只有一个元素的时候即可(因为只有一个元素的区间默认是有序的),然后将排序好的子序列进行合并最后合并成一个已经排序好的完整序列。
代码实现
//合并函数代码:
void Merge(int arr[], int low, int mid, int high){
//low为第1有序区的第1个元素,i指向第1个元素, mid为第1有序区的最后1个元素
int i = low, j = mid + 1, k = 0; //mid+1为第2有序区第1个元素,j指向第1个元素
int *temp = new int[high - low + 1]; //temp数组暂存合并的有序序列
while (i <= mid&&j <= high){
if (arr[i] <= arr[j]) //较小的先存入temp中
temp[k++] = arr[i++];
else
temp[k++] = arr[j++];
}
while (i <= mid)//若比较完之后,第一个有序区仍有剩余,则直接复制到t数组中
temp[k++] = arr[i++];
while (j <= high)//同上
temp[k++] = arr[j++];
for (i = low, k = 0; i <= high; i++, k++)//将排好序的存回arr中low到high这区间
arr[i] = temp[k];
delete[]temp;//释放内存,由于指向的是数组,必须用delete []
}
//划分函数代码
void MergeSort (int arr [], int low,int high) {
if(low>=high) { return; } // 终止递归的条件,子序列长度为1
int mid = low + (high - low)/2; // 取得序列中间的元素
MergeSort(arr,low,mid); // 对左半边递归
MergeSort(arr,mid+1,high); // 对右半边递归
Merge(arr,low,mid,high); // 合并
}
//主函数如下:
int main()
{
int a[10] = { 2, 7, 34, 54, 12, 5, 19, 33, 88, 23 };
cout << "原来的数组为" << endl;
for (int i = 0; i < 10; i++)
{
cout << a[i] << " ";
}
cout << endl;
MergeSort(a, 0, 9);
cout << "排序后的数组为:" << endl;
for (int i = 0; i < 10; i++)
{
cout << a[i] << " ";
}
cout << endl;
}
时间复杂度
O ( n l o g n ) O(nlogn) O(nlogn)
空间复杂度
O ( n ) O(n) O(n)
堆排序
利用大顶堆(小顶堆)的根节点记录最大数值(最小数值)的特性,可以对数值进行排序
基本思想
现以大顶堆的基本思想为例:
- 将int 数组arr[1,…,n]构造为大顶堆,实现函数为heapadjust()
- 可得大顶堆的根节点,即arr[1]的数值为最大数值,然后将该值与arr[n]互换
- 对数组arr[1,…,n-1]构造为大顶堆并将arr[1]与arr[n-1]互换,直到n-1为2停止互换,堆排序完成
对于堆排序,最重要的两个操作就是构造大顶堆和调整堆,其实构造初始堆事实上也是调整堆的过程。
代码实现
#include <stdio.h>
#include <stdlib.h>
//对当前节点进行调整,这样得到一个大顶堆
void heapadjust(int k[],int n)
{
int i,temp=0;
for(i=n/2;i>0;i--)
{
if(k[i]<k[2*i+1] && (2*i+1)<=n)
{
temp = k[i];
k[i] = k[2*i+1];
k[2*i+1] = temp;
}
if(k[i]<k[2*i] && 2*i<=n)
{
temp = k[i];
k[i] = k[2*i];
k[2*i] = temp;
}
}
}
//将堆排序后数组的根节点和最后一个节点的值互换
void swap(int k[],int i,int j)
{
int temp = k[i];
k[i] = k[j];
k[j] = temp;
}
//堆排序
void heapsort(int k[], int n)
{
int i =n/2;
heapadjust(k,n);
//将本次根节点的数值和最后的叶子节点互换,然后排序的数据减1,找到减一后的最大数值,再和倒数第二个节点互换
for(i=n;i>=1;i--)
{
swap(k,1,i);
heapadjust(k,i-1);
}
}
int main()
{
//堆排序,采用二叉树结构,所以下标从1开始
int arr[10]={-1,45,68,29,10,95,978,2,3,9};
heapsort(arr,9);
int i=0;
printf("排序后的结果是:\n");
for(i=1;i<10;i++)
{
printf("%d\t",arr[i]);
}
return 0;
}
时间复杂度
O
(
n
l
g
n
)
。
O(nlgn)。
O(nlgn)。
小队成员(Wu_0526,qq_49032326,ly1196324806,SamGeren,qq_39124199,Teeeee_)
素材来源:https://www.runoob.com/w3cnote/radix-sort.html