1.直接插入排序
是一种最简单的排序方法,它的基本操作是将一个记录插入到已排好序的有序表中,从而得到一个新的,记录数增1的有序表。
void insertsort(int a[],int length){
int i,j,shaobing;
for(i=1;i<length;i++){
if(a[i]<a[i-1]){//< 的话需要将a[i]插入有序子表
shaobing=a[i];//复制为哨兵
a[i]=a[i-1];
for(j=i-2;j>=0&&shaobing<a[j];--j){
a[j+1]=a[j];//记录后移
}
a[j+1]=shaobing;//插入到正确位置
}
}
}
2.冒泡排序
https://www.cnblogs.com/jhmu0613/p/6780741.html
冒泡排序是一种交换排序,通过两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。
从第一个元素开始逐个地开始比较相邻的元素。每一次排序结尾总有一个最大的数被放在了最后。使用常数个辅助空间,所以空间复杂度为O(1).总的时间复杂度为O(n^2)。借助于“交换”进行排序的方法。一定要把冒泡的写法记清楚!!冒泡的实现细节可以有很多种变化的
void bubble_sort(int a[],int n){
for(int i=0;i<n;i++)
for(int j=0;j<n-i;j++){
if(a[j]>a[j+1])
swap(a[j],a[j+1]);//在逆序时交换相邻记录
}
}
重新贴一个修改的,时间复杂度为什么是O(n^2),从它的比较次数中也可看出,n-1+n-2+...+1=n(n-1)/2
#include <iostream>
using namespace std;
int main()
{
int n;
cin>>n;
int k=0, a[n];
while(k<n){
cin>>a[k];
++k;
}
for(int i=0;i<n-1;++i){
for(int j=0;j<n-i-1;++j){
if(a[j]>a[j+1]){
swap(a[j],a[j+1]);
}
}
}
for(int i=0;i<n;++i){
cout<<a[i]<<" ";
}
return 0;
}
为什么最好的情况下的时间复杂度是O(n),因为仍然需要n-1次的比较。对于时间复杂度怎么计算来说,应该是这样:取决于比较次数和交换次数中较大的那个。
冒泡排序的优化 :
从整个过程中可以看出,即使到某一趟数据全部有序了,还是会继续接下来的i和j控制的循环,没有交换操作却依然有比较操作。增加一个flag
3.快速排序
是对起泡排序的一种改进。它的基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分全小于枢轴元素,另一部分全部大于枢轴元素。
是对冒泡排序的一种改进方法,在冒泡排序中,进行元素的比较和交换是在相邻元素之间进行的,元素每次交换只能移动一个位置,所以比较次数和移动次数较多,效率相对较低。而在快速排序中,元素的比较和交换是从两端向中间进行的,较大的元素一轮就能够交换到后面的位置,而较小的元素一轮就能交换到前面的位置,元素每次移动的距离较远,所以比较次数和移动次数较少,y速度较快,故称为“快速排序”。
快速排序的基本思想是:
- 在待排序的元素任取一个元素作为基准(通常选第一个元素,选择方法是从待排序元素中随机选取一个作为基准),称为基准元素;首先从high所指位置起向前搜索找到第一个关键字小于piotkey的记录和枢轴记录互相交换,然后从low所指位置起向后搜索,找到第一个关键字大于pivotkey的记录和枢轴记录互相交换,重复这两步纸质low=high为止。
- 将待排序的元素进行分区,比基准元素大的元素放在它的右边,比其小的放在它的左边;
- 对左右两个分区重复以上步骤直到所有元素都是有序的
int Partition(int a[],int low,int high){
int pivotkey=a[low];
while(low<high){
while(low<high&&a[high]>=pivotkey){
high--;
}
a[low]=a[high];
while(low<high&&a[low]<=pivotkey){
low++;
}
a[high]=a[low];
}
a[low]=pivotkey;
return low;
}
void Qsort(int a[],int low,int high){
if(low<high){
int pivotloc=Partition(a,low,high);
Qsort(a,low,pivotloc-1);
Qsort(a,pivotloc+1,high);
}
}
void QuickSort(int a[],int length){
if(length<=0) return;
Qsort(a,0,length-1);
}
4.归并排序
归并排序又是一类不同的排序方法。归并的含义是将两个或两个以上的有序表组合成一个新的有序表。递归形式的2路归并算法在形式上较为简洁,但实用性很差。与快速排序和堆排序相比,归并排序的最大特点是它是一种稳定的排序方法。
5.简单选择排序
与冒泡排序不同,简单选择排序是等到合适的关键字出现才交换,每交换一次就可以达到一次冒泡的效果。我们只需要进行n-1趟排序即可。这个排序算法很好出选择题,时间复杂度,最好和最差都是一样的,因为其比较次数一样,第i趟排序都需要进行n-i次关键字的比较,基于最终的排序时间是比较和交换次数的综合,因此总的时间复杂丢仍然为O(n^2) ,简单选择排序的性能上还是要略优于冒泡排序。
如果i选择的元素是最小的则不需要交换
void SelectSort(int a[],int n){
if(n<=1) return;
//利用一个中间元素temp来记录最小的数的位置
for(int i=0;i<n-1;++i){
int temp=i+1;
for(int j=i+1;j<n;++j){
if(a[j]<a[temp]){
temp=j;
}
}
if(a[temp]<a[i]){
swap(a[temp],a[i]);
}
}
}
//教程中的解法
void selectsort(int a[],int n){
int temp;
for(int i=0;i<n-1;i++){
temp=i;
for(int j=i+1;j<n;j++){
if(a[temp]>a[j){
temp=j;
}
}
swap(a[i],a[temp]);
}
}
6.堆排序
堆排序是一种比较特殊的排序方式。堆是一种非线性的数据结构,可以把对看做一个数组,也可以看做一个完全二叉树。
按照堆的特点可以分为大顶堆和小顶堆。实现堆排序需要解决两个问题:如何由一个无序序列建成一个堆? 如何在输出堆顶元素之后,调整剩余元素成为一个新的堆?
从无序序列建堆的过程其实就是一个反复“筛选”的过程。若将此序列看成是一个完全二叉树,则最后一个非终端节点是第n/2向下取整个元素。叶子节点都没有左右儿子了,做HeapAdjust也等于没有操作。
void HeapAdjust(int a[],int s,int m){
//已知记录中的关键字出a[s]外均满足堆的定义,本函数调整a[s],使得a[s]到a[m]成为一个大顶堆。s是开始的位置
int rc=a[s];
//沿k较大的子节点向下筛选
for(int j=2*s+1;j<=m;j=j*2+1){
if(j<m&&(a[j]<a[j+1])) ++j; //j为key较大的记录的下标
if(rc>=a[j]) break; //rc应插入在位置s上,注意这一句:如果记录大于等于a[j]的话说明当前的位置是可以的,不需要挪了
a[s]=a[j];
s=j;
}
a[s]=rc; //插入
}//HeapAdjust
void HeapSort(int a[],int n){
for(int i=n/2-1;i>=0;--i)//把a[0]~a[n-1]建成大顶堆;主要思路:第一次保证0~0位置大根堆结构(废话),
//第二次保证0~1位置大根堆结构,第三次保证0~2位置大根堆结构...直到保证0~n-1位置大根堆结构
HeapAdjust(a,i,n-1);
for(int i=n-1;i>=0;--i){
swap(a[0],a[i]);
HeapAdjust(a,0,i-1);
}
}