排序算法以及复杂度介绍

                            排序算法以及复杂度介绍

各种排序复杂度

选泽:     o(n)好   o(N^2)平均    o(n^2)坏  辅助空间o(1)不稳
直接插入: o(n)好   o(N^2)平均    o(n^2)坏  辅助空间o(1)稳定
冒泡:     o(n)好   o(N^2)平均    o(n^2)坏  辅助空间o(1)稳定


堆排序:   o(nlog n)好  o(nlog n)平均   o(nlog n)坏 辅助空间o(1)不稳
希尔排序: o(n)好     o(N^1.3)平均  o(n^2)坏  辅助空间o(1)不稳
快排:     o(nlog n)好  o(nlog n)平均   o(n^2)坏  辅助空间o(logn)不稳
归并排序: o(nlogn)好       o(nlog n)平均   o(nlog n)坏  辅助空间o(n)稳定


算法的稳定性定义为:对于待排序列中相同项的原来次序不能被算法改变则称该算法稳定.
比如待排序列为:(2) 3 6 [2] 4 5 ,,,序列中的(2)排在[2]前面,不能因为算法把[2]排到(2)前面. 
直接选择排序算法,不稳定性,举个简单的例子,就知道它是否稳定..例如:(7) 2 5 9 3 4 [7] 1...
当我们利用直接选择排序算法进行排序时候,(7)和1调换,(7)就跑到了[7]的后面了,原来的次序改变了
,这样就不稳定了.






/////////////// 冒泡排序 ////////////////////////////////////////
#include<iostream>
#include<vector>
using namespace std;
void Bubble_sort(vector<int> &vec)
{
for(int i=0;i<vec.size();i++)
{
for(int j=1;j<vec.size()-i;j++)
{
if(vec[j-1]>vec[j])
{
swap(vec[j-1],vec[j]);
}
}
}
}


////////////// 直接插入排序 ////////////////////////////////////
#include<iostream>
#include<vector>
using namespace std;
void insert_sort(vector<int> &vec)
{
int i, j;
for (i = 2; i < vec.size(); i++)
{
if (vec[i - 1]>vec[i])
{
vec[0] = vec[i];
for (j = i - 1; vec[j] > vec[0]; j--)
{
vec[j + 1] = vec[j];
}
vec[j + 1] = vec[0];
}
}
}
第一个for循环:从第二个有效数字开始便利到最后一个有效数字
只要是有前面的数字大于后面的数字就开始调整这时候vec[i]是指向小数字,将vec[i]
保存近vec[0]。
然后开始第二个循环j的开始就是保存的这个数字前一个数字所以是i-1,用它覆盖
保存过得那个数字一直往前所以是j--,知道有vec[j] > vec[0],说明vec[0]应该
插入到vec[j]之后这个位置上所以vec[j + 1] = vec[0];(这里之所以在vec[j+1]要
考虑在for循环出来以后j做了--操作所以真正插入的位置是j+1。)
完成插入排序


//////////////////希尔排序///////////////////////////////////////
类似于直接插入排序唯一不同时整体大循环要用while(increase>1)控制
在接下来基本一致每次进入while重置一次 increase=increase/3+1(3需要证明)
这里要注意优先级比较原则(j>0&&vec[j]>vec[0])这里两个不可以反,
如果j先小于0那么vec[j-crease]就越界了


////////////////////堆排///////////////////////////////////
大顶堆,小根堆
void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
void adjust(vector<int> &vec, int s, int m)
{
for (int i = s * 2 + 1; i < m; i = i * 2 + 1)
{
if (i+1<=m&&vec[i] < vec[i + 1])
i++;
if (vec[s]>vec[i])
break;
swap(vec[s], vec[i]);
s = i;
}
}
void heap_sort(vector<int> &vec)
{
for (int i = (vec.size()-1) / 2; i >= 0; i--)(1)
adjust(vec, i, vec.size()-1);
for (int i = vec.size()-1; i > 0; i--)(2)
{
swap(vec[0],vec[i]);
adjust(vec, 0, i-1);(3)
}
}
堆排序如上程序(1)是为了将初始带入vector的数据排成大根堆(vec.size()-1)/2是为了找到非
叶子节点的节点。只要将他们每个子树做成大根堆整体就是一个大根堆。adjust是调整函数,第二个参数
代表调整子树的根节点下标,m带表这个子树的大小。(2)这个循环就是为了将大的树放在最后面和根
节点做交换,这里交换完成直接可以写(3)是因为整个树只有这个非叶子节点是不符合大根堆的。
其他节点没有变换所有不用像(1)一样再写循环。




/////////////////二路归并排序////////////////////////////////////
void merge(vector<int> &vec, int low, int mid, int high)
{
int i=low, j=mid+1, k=0;
vector<int> vec1(high - low + 1);
while (i <= mid&&j <= high)
{
if (vec[i] < vec[j])
{
vec1[k++] = vec[i++];
}
else
{
vec1[k++] = vec[j++];
}
}
while(i <= mid)
vec1[k++] = vec[i++];
while(j <= high)
vec1[k++] = vec[j++];
for (i = low, k = 0; i <= high; i++)
{
vec[i] = vec1[k++];
}
/*
for (k = 0; k <= (high - low); k++)
{
vec[low++] = vec1[k];
}
*/这个for循环看似与上边一至,但是我们用的是等号右边的low作为下边vec[low++]
变换条件当第一次进入的时候,low++以后第二次进入的时候k <= (high - low)
low已经变化了所以原先的循环次数改变了,要养成良好的for循环习惯尽量用左边的值
用变量作为for的限制条件。
}
void Mergesort(vector<int> &vec, int low, int high)
{
if (low < high)
{
int mid = (low + high) / 2;
Mergesort(vec, low, mid);
Mergesort(vec, mid + 1, high);
merge(vec, low, mid, high);
}
}
二路归并排序也是一种类似二叉中序遍历的顺序,递归实现。先把一组数据分割成
两份,一直递归分割如上Mergesort如同中序遍历一直朝左走当low==high完成递归
回来到上一个节点走右边,同样low==high完成在回到这个节点,将vec,0,0,1带入
merge中将他们拍好序放入vec1中,然后再放回去,就这样递归完成。


///////////////// 快速排序 /////////////////////////////////////
void swap(int &a, int &b)///交换函数
{
int temp;
temp = a;
a = b;
b = temp;
}
int partition(vector<int> &vec, int low, int high)///3函数
{
while(low<high)
{
while (low<high&&vec[high] >= pivot_key)
{
high--;
}
swap(vec[low], vec[high]);
while (low < high&&vec[low] <= pivot_key)
{
low++;
}
swap(vec[high], vec[low]);
}
return low;
}
void Q_sort(vector<int> &vec, int low, int high)////2函数
{
int pivot;
if (low < high)
{
pivot = partition(vec, low, high);
Q_sort(vec, low, pivot - 1);
Q_sort(vec, pivot + 1, high);
}
}
void Qsort(vector<int> &vec)///1函数
{
Q_sort(vec, 0, vec.size()-1);
}


1函数要注意对于下表的注意如果从0开始那么要vec.size()-1否则就会越界。快拍的
函数递归调用是一种二茬数的形式,由于需要确定size()所以独立写出1函数。
做出2函数可以有统一的递归调用的形式。
2函数pivot是中轴数,保证这个数左边的数小于它,右边的数大于它,我们不能用
while而要用if这棵树只有两种选择,不停的往深处走,而不是一颗横向发展的数。
partition是可以求出中轴量并返回下表的函数(函数3)
3函数左右high,low两个下标,保证两个下标重合就不在移动所以while(low<high)
控制,while (low<high&&vec[high] >= pivot_key)在不重合的情况下(这里中轴量是vec[low])
如果右边大于它就不动,逐个遍历,直到有数小于它交换,左边的数也一样小于它移动
大于它交换直到两个指针重合,遍历完毕。

猜你喜欢

转载自blog.csdn.net/qq_41784469/article/details/80846328