排序是指把一组无序的数据元素(或记录)按照关键字值非递减(或非递增)次序重新排列成一个有序的序列。
下面介绍我所掌握的几种排序算法。
插入排序
排序方法 | 最好情况 | 平均情况 | 最坏情况 | 稳定性 | 备注 |
插入排序 | O(n) | O(n²) | O(n²) | 稳定 | 适用于大部分已有序排列好 |
插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据。
基本思想:初始时,将待排序记录序列中的第一个记录作为一个有序表,依次从第二个记录起逐个进行插入直至将整个序列变为按关键字有序排列。
int main()
{
int arr[20]= { 0, 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48 };
for (int i = 1; i < 15; i++)
{
// 定义待插入的数
int insertValue = arr[i];
// 找到待插入数的前一个数的下标
int insertIndex = i - 1;
while (insertIndex >= 0 && insertValue < arr[insertIndex])
{
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
arr[insertIndex + 1] = insertValue;
for (int j = 0; j < 15; j++)
{
cout<<arr[j]<<" ";
}
cout<<endl;
}
return 0;
}
希尔排序
排序方法 | 最好情况 | 平均情况 | 最坏情况 | 稳定性 | 备注 |
插入排序 | O(nlong2n)~O(n²) | O(n²) | O(nlong2n)~O(n²) | 不稳定 |
传统的插入排序算法在某些场景中存在着一些问题,例如[2,3,4,5,1]这样的一个数组,当我们对其进行插入排序的时候,发现要插入的数字是1,而要想将1插入到最前面,需要经过四个步骤,分别将5、4、3、2后移。所以得出结论:如果较小的数是我们需要进行插入的数,那效率就会比较低。于是有了希尔排序。
希尔排序也是一种插入排序的方法,是对直接插入排序的改进。直接插入排序适合n较小或者初始记录基本有序的情况,希尔排序正是从这两点出发对直接插入排序算法的改进。
基本思想:希尔排序是把记录按下标的一定增量分割成若干子序列,对每组使用直接插入排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
int main()
{
int arr[20] = { 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48 };
for(int gap=15/2;gap>0;gap/=2)
{
//对数组元素进行分组
for(int i=gap;i<15;i++)
{
for(int j=i-gap;j>=0;j-=gap)//遍历各组中的元素
{
if(arr[j]>arr[j+gap])
{
int temp=arr[j];
arr[j]=arr[j+gap];
arr[j+gap]=temp;
}
}
}
for(int i=0;i<15;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
}
return 0;
}
冒泡排序
排序方法 | 最好情况 | 平均情况 | 最坏情况 | 稳定性 | 备注 |
冒泡排序 | O(n) | O(n²) | O(n²) | 稳定 | n小时较好 |
冒泡排序是重复地从头开始访问要排序的元素列,依次比较两个相邻的元素,如果他们的顺序错误就把他们交换过来。访问元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
基本思想:冒泡排序最多执行n-1趟就可以排成一个有序序列。每一趟都将a[1]至a[n-i+1]中的最大(最小)值交换到第n-i+1个位置上。
int main()
{
int arr[20] = { 0, 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48 };
int sum=0;
for(int i=1;i<=15-1;i++) //最多需要n-1次可排完
{
for(int j=1;j<=15-i;j++) //每次需要进行到的位置
{
if(arr[j]>arr[j+1])
{
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
sum++;
}
for(int i=1;i<15;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
}
cout<<"一共比较了"<<sum<<"次"<<endl;
return 0;
}
这是常规的冒泡排序的模板,但是有个缺点,就是当排序过程中已经将数组元素排序完成,但此时它仍然会去比较上面的程序中我们已经得出了比较次数为105次,但是实际不需要105次比较就已经可以得到一个有序序列了。所以我们可以通过一个bool变量来优化这段代码,当某一次从头开始相邻两两比较,发现不需要交换一次,则次序列已经有序,无需执行完n-1次。
int main()
{
int arr[20] = { 0, 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48 };
int sum=0;
for(int i=1;i<=15-1;i++)
{
bool flag=true;
for(int j=1;j<=15-i;j++)
{
if(arr[j]>arr[j+1])
{
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
flag=false;
}
sum++;
}
for(int i=1;i<15;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
if(flag)
break;
}
cout<<"一共比较了"<<sum<<"次"<<endl;
return 0;
}
选择排序
排序方法 | 最好情况 | 平均情况 | 最坏情况 | 稳定性 | 备注 |
选择排序 | O(n²) | O(n²) | O(n²) | 不稳定 | n小时较好 |
选择排序是进行n-1次循环,每一次循环都找出一个最小(最大)值,与待排序最前面的位置互换即可。
基本思想:每一趟在n-i+1(i=1,2,3,...,n-1)个待排序记录中选取关键字最小(最大)的记录作为有序序列中的第i个记录。
int main()
{
int arr[20] = { 0, 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48 };
for(int i=1;i<=15-1;i++)
{
int index=i;
for(int j=i+1;j<=15;j++)
{
if(arr[j]<arr[index])
index=j; //保存最小元素的下标
}
int temp=arr[index];
arr[index]=arr[i];
arr[i]=temp;
for(int i=1;i<=15;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
}
return 0;
}