各种排序算法
(1)冒泡排序
算法复杂度O(n^2)
void BubbleSort(vector<int>& vec)
{
int n = vec.size();
for(int i=0;i<n;++i)
{
for(int j=n-1;j>=i;--j)
{
if(vec[j-1] > vec[j])
swap(vec[j-1],vec[j]);
}
}
return;
}
冒泡算法的优化:
防止数据在已经有序的前提下还进行不必要的比较
void BubbleSort2(vector<int>& vec)
{
int n = vec.size();
bool isOrder = true;
for(int i=0;i<n && isOrder;++i)
{
for(int j=n-1;j>=i;--j)
{
isOrder = false;
if(vec[j-1] > vec[j])
{
swap(vec[j-1],vec[j]);
isOrder = true;
}
}
}
return;
}
(2) 选择排序
算法复杂度O(n^2)
每次固定第i个位置,从i之后的数据找出应该放在这里的数据
void selectSort(vector<int>& vec)
{
int min;
for(int i=1;i<vec.size();++i)
{
min = i;
for(int j = i+1;j<n;++j)
{
if(vec[min] > vec[j])
min = j;
}
if(i != min)
swap(i,min);
}
}
(3)插入排序
类似于玩扑克牌:将一个记录插入到已经排好序的有序表中,从而得到一个新的,记录数增加1的记录
void insertSort(vector<int> & vec)
{
int i,j;
for(i =1;i<vec.size();++i)
{
if(vec[i] < vec[i-1])
{
int k = vec[i];
for(j = i-1;vec[j] > k; --j)
{
vec[j+1] = vec[j];
}
vec[j+1] = k;
}
}
}
(4) 希尔排序
对插入排序的改进
基本有序:小的数据基本在前面,打的数据基本在后边,不大不小的基本在中间
将相距某个增量的记录组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序,而不是局部有序
时间复杂度:O(nlogn)~O(n^2)
void shellSort(vector<int>& vec)
{
int i,j;
int increament = vec.size(); //这里假设vec的长度为n+1,对1-n范围的数据进行排序;
do
{
increment = increment/3+1; //增量序列
for(int i = increment+1;i<vec.size();i++)
{
if(vec[i] < vec[i-increment])
{
vec[0] = vec[i];
for( j = i-increment;j>0&&vec[0] < vec[j];j-=increment)
{
vec[j+increment] = vec[j];
}
vec[j+increment] = vec[0];
}
}
}
while(increment > 1);
}
(4)堆排序
对简单选择排序的改进
堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子节点的值,称为大顶堆,或者每个节点的值都小于或等于其左右孩子结点的值,称为小顶堆
堆排序思想:将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶根节点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是,最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就得到n个元素中的次大值,如此反复.算法时间复杂度O(nlogn)
这里仍然假设数组的长度为n+1
代码:
void HeapSort(vector<int> &vec)
{
int i;
for(i = vec.size()/2;i>0;--i)
{
heapAdjust(vec,i,vec.size()-1);
}
for(i = vec.size()-1;i>1;--i)
{
swap(1,i);
heapAdjust(vec,1,i-1);
}
}
void HeapSort(vector<int> &vec,int s,int m)
{
//这里是假设s-m的数据中,除了s外,其他已经是大顶堆结构了,这里是对s的位置进行调整
int temp ,i;
temp = vec[s];
for(i=2*s;i<m;i*=2)
{
if(vec[i] < vec[i+1])
i = i+1;
if(temp > vec[i])
break;
vec[s] = vec[j];
s = j;
}
vec[s] = temp;
}
(5)归并排序
归并排序使用了分治的思想,将数组二分成一个一个的单元,逐个单元进行两两归并排序,最后合并成一个有序的序列
时间复杂度O(nlogn)
代码:
void MergeSort(vector<int> &vec,vector<int> &vec2)
{
Msort(vec,vec2,1,vec.size()-1);
}
void Msort(vector<int> &vec,vector<int> &vec2,int s,int t)
{
if( s == t)
vec2[s] = vec[s];
else
{
int m = (s+t)/2;
Msort(vec,vec2,s,m);
Msort(vec,vec2,m+1,t);
Merge(vec,vec2,s,m,t);
}
}
void Merge(vector<int>& vec,vector<int>& vec2,int i,int m,int n)
{
int j ,k,l;
for(j = m+1,k=i;i<=m&&j<=n;++k)
{
if(vec[i] < vec[j])
vec2[k] = vec[i++];
else
vec[k] = vec[j++];
}
if(i<=m)
{
for(l =0;l<=m-i;l++)
vec2[k+l] = vec[i+l];
}
if(j<=n)
{
for(l =0;l<=m-i;l++)
vec2[k+l] = vec[j+l];
}
}
归并排序的非递归实现:
vector<int> Merge(vector<int>& data,int begin,int mid,int end)
{
int n1 = mid - begin+1;
int n2 = end - mid;
int left[n1],right[n2];
for(int i=0;i<n1;i++)
{
left[i] = data[begin+i];
}
for(int i=0;i<n2;++i)
{
right[i] = data[mid+i+1];
}
int i=0,j=0,k = begin;
while(i<n1&& j<n2)
{
if(left[i] <= right[j])
data[k++] = left[i++];
else
data[k++] = right[j++];
}
while(i<n1)
data[k++] = left[i++];
while(j < n2)
data[k++] = right[j++];
return data;
}
vector<int> MergeSort(vector<int> data)
{
vector<int> res;
int n = data.size();
if(n == 0)
return res;
int i =1;
while(i<n)
{
for(int begin =0;begin < n;begin += 2*i)
{
int mid = begin+i-1;
int end = min(begin + 2*i-1,n-1);
res = Merge(data,begin,mid,end);
}
i*=2;
}
return res;
}
非递归的思想就是从2开始,以2的倍数增长进行两两归并操作,最后使整个数组有序
(6)快速排序
注意当基准数选择最左边的数字时,那么就应该先从右边开始搜索,当基准数选择最右边的数字时,就应该从左边开始搜索,不论从小到大排序还是从大到小排序;
代码:
void quickSort(vector<int> &vec,int left,int right)
{
if(left < right)
return;
int i = left,j = right,base = vec[left],temp;
while(i<j)
{
while(i<j && vec[j] >= base)
++j;
while(i<j && vec[i] <= base)
++i;
if(i<j)
{
swap(i,j);
}
}
vec[left] = vec[i];
vec[i] = base;
quickSort(vec,left,i-1);
quickSort(vec,i+1,right);
}
快速排序的优化方法:
1.基准值的选择:随机选择基准值、数组的开头、结尾、中间的三数取中间的那部分
2.当待排序的部分分割到一定长度后,采用插入排序法
3.每次排序后将与基准值相等的元素聚集起来