一、冒泡法
概念:冒泡排序的大概思想是两两比较相邻记录的关键字,如果反序则交换,直到没有反序为止
1.普通冒泡法:
void BubbleSort(vector<int> &vi)
{
int i, j, temp;
for(i = 0; i < vi.size()-1; i++)
{
for(j = vi.size()-2; j >= i; j--)
{
if(vi[j] >vi[j+1])
{
temp = vi[j];
vi[j] = vi[j+1];
vi[j+1] = temp;
}
}
}
}
2.优化冒泡法,增加标志位,对已经有序的序列不在进行比较
void BubbleSort2(vector<int> &vi)
{
int i, j, temp;
bool flag = true;
for(i = 0; i < vi.size()-1 && flag; i++) //若无发生交换,则说明序列已经是有序的,flag=false退出
{
flag = false;
for(j = vi.size()-2; j >= i; j--)
{
if(vi[j] >vi[j+1])
{
temp = vi[j];
vi[j] = vi[j+1];
vi[j+1] = temp;
flag = true;
}
}
}
}
二、简单选择排序
概念:通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i个记录交换。其特点就是减少了交换移动数据的次数,相应的节约了运算时间。分析它的时间复杂度可以看出,无论最好还是最差的情况,其比较次数相同;而交换次数,最好的时候为0,最坏为n-1次。总的时间复杂程序是O{n²},但性能上还是略优于冒泡法。
void SelectSort(vector<int> &vi)
{
int i, j, min, temp;
for(i = 0; i < vi.size() - 1; i++)
{
min = i; //最小值坐标
for(j = i+1; j <= vi.size()-1; j++)
{
if(vi[min] > vi[j])
min = j; //交换最小坐标值
}
if(i != min) //min不等于i说明min是最小值,否则没必要交换
{
temp = vi[i];
vi[i] = vi[min];
vi[min] = temp;
}
}
}
三、直接插入排序
概念:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入 ,如此重复,直至完成序列排序。
算法分析:
1. 从序列第一个元素开始,该元素可以认为已经被排序
2. 取出下一个元素,设为待插入元素,在已经排序的元素序列中从后向前扫描,如果该元素(已排序)大于待插入元素,将该元素移到下一位置。
3. 重复步骤2,直到找到已排序的元素小于或者等于待排序元素的位置,插入元素
4. 重复2,3步骤,完成排序。
转自https://blog.csdn.net/hlc246/article/details/81076183
直插排序的时间复杂度同样为O(n²),但性能上比冒泡和简单排序要好一些
void InsertSort(vector<int> &vi)
{
for(int i=1;i<vi.size();i++)
{
int temp=vi[i];
int j;
for(j=i-1;j>=0&&temp<vi[j];j--)
{
vi[j+1]=vi[j]; //将较大元素后移,将需要比较的存在temp,从比较处往前一次移动一位,不会导致数据丢失
}
vi[j+1]=temp; //temp插入正确的位置
}
}
四、希尔排序
概念:希尔排序是直接插入排序的一种改进方式,属于缩小增量跳跃式移动,并不是一种稳定的排序算法。
其基本思想是,先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
他的复杂度为O(n^(3/2)),要好于之前三种排序的O(n²)
详细解析:https://blog.csdn.net/weixin_37818081/article/details/79202115
void ShellSort(vector<int> &vi)
{
int i, j;
int increment = vi.size();
do{
increment = increment/3 + 1;
for(i = increment; i < vi.size(); i++)
{
if(vi[i] < vi[i - increment])
{
int temp = vi[i];
for(j = i - increment; j>=0 && temp<vi[j]; j-=increment)
{
vi[j+increment] = vi[j];
}
vi[j+increment] = temp;
}
}
}
while(increment > 1);
}
五、快速排序
概念:快排在我们日常工作中经常遇到,在面试中也会偶尔出现要你手写快排的时候,因此掌握他是非常重要的。快排的本质上来讲可以理解为前面的冒泡排序升级版。他利用了二分法的思维,通过一趟排序将待排数据分割成了两边,其中一边的数据均比另外一边的数据小,通过递归调用的方法循环分割最终达到数据有序的目的。
快排的时间复杂度最优为O(nlogn),最差为O(n²);
普通快排:
int Partition(vector<int> &vi,int low, int high)
{
int pivotkey, temp;
pivotkey=vi[low];
while(low < high)
{
while(low<high && vi[high]>=pivotkey)
high--;
temp = vi[high];
vi[high] = vi[low];
vi[low] = temp;
while(low<high && vi[low]<=pivotkey)
low++;
temp = vi[high];
vi[high] = vi[low];
vi[low] = temp;
}
return low;
}
void QSort(vector<int> &vi,int low, int high)
{
int pivot;
if(low < high)
{
pivot = Partition(vi, low, high);
QSort(vi, low, pivot-1);
QSort(vi, pivot+1, high);
}
}
void QuickSort(vector<int> &vi)
{
Print(vi.begin(), vi.end());
QSort(vi, 0, vi.size()-1);
}
优化快排:
1、将枢轴的取法修改为三数取中间数(头、中、尾三数取中间数),尽量保证枢轴靠近数据中间。
2、参考简单选择排序,优化不必要的交换,将交换放在最后
void swap(vector<int> &vi,int low, int high)
{
int temp;
temp = vi[high];
vi[high] = vi[low];
vi[low] = temp;
}
int Partition1(vector<int> &vi,int low, int high)
{
int pivotkey, temp;
//取头、中、尾三个数,取三个数值中间一个交换为pivotkey,若数值较大也可以考虑用九数取中间
int var = low + (high - low)/2;
if(vi[low] > vi[high])
swap(vi, low, high);
if(vi[var] > vi[high])
swap(vi, var, high);
if(vi[var] > vi[low]);
swap(vi, var, low);
Print(vi.begin(), vi.end());
temp = pivotkey = vi[low];
while(low < high)
{
while(low<high && vi[high]>=pivotkey)
high--;
vi[low] = vi[high]; //将数据进行替换而非交换
while(low<high && vi[low]<=pivotkey)
low++;
vi[high] = vi[low];
}
vi[low] = temp;
return low;
}