您的位置:首页 > 编程语言 > C语言/C++

C++排序算法代码汇总

2017-12-07 20:34 197 查看
排序算法稳定性:两个相同的元素排序前后的相对位置关系不会发生改变。

复杂度比较

算法平均时间复杂度最好情况最坏情况空间复杂度稳定性
冒泡排序O(N^2)O(N^2)O(N^2)O(1)稳定
插入排序O(N^2)O(N)O(N^2)O(1)稳定
选择排序O(N^2)O(N^2)O(N^2)O(1)稳定
希尔排序O(N ^ 3/2 ) O(N^2)O(1)不稳定
堆排序O(NlogN)O(NlogN)O(NlogN)O(1)不稳定
归并排序O(NlogN)O(NlogN)O(NlogN)O(N)稳定
快速排序O(NlogN)O(NlogN)O(N^2)O(logN)不稳定
冒泡排序

排序过程

将第一个元素与第二个元素比较大小,如果第一个元素大于第二个元素则调换他们两的位置;
比较第二个元素和第三个元素的大小,如果第二个元素大于第三个元素则调换他们两的位置;
依次类推,进行两两元素的比较和交换,最终最大的元素排在了最后面;
重复1到3过程,直到所有元素都排序。

图片演示


//冒泡排序
//平均时间复杂度:O(N^2)
//最坏情况复杂度:O(N^2)
//空间复杂度:O(1)
//稳定排序
void bubblesort(vector<int>& a)
{
int n = a.size();
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n - 1 - i; j++)
{
if (a[j] > a[j+1])
swap(a[j], a[j+1]);
}
}
}

//冒泡排序升级版1
//设置一个标记来标志一趟比较是否发生交换
//如果没有发生交换,则数组已经有序
void bubblesort2(vector<int>& a)
{
int n = a.size();
bool flag;
for (int i = 0; i < n; i++)
{
flag = false;
for (int j = 0; j < n - 1 - i; j++)
{
if (a[j] > a[j+1])
{
swap(a[j], a[j+1]);
flag = true;
}
}
if (!flag)
break;
}
}

//冒泡排序优化二
//用一个变量记录下最后一个发生交换的位置,后面没有发生交换的已经有序
//所以可以用这个值来作为下一次比较结束的位置
void bubblesort3(vector<int>& a)
{
int n = a.size();
int flag = n;
int stop_pos;
for (int i = 0; i < n; i++)
{
stop_pos = flag - 1;
flag = 0;
for (int j = 0; j < stop_pos; j++)
{
if (a[j] > a[j+1])
{
swap(a[j], a[j+1]);
flag = j + 1;
}
}
}
}


插入排序
对于第K个元素,将该元素的值存储在零时变量中,比较第前一个元素与该元素的大小,如果大于该元素就将前一个元素往后移动一步;
比较前面第二个元素与该元素的大小,如果大于该元素就将前第二个元素往后移动一步;
重复上述过程直到找到小于等于原来第K个元素(保存在零时变量中)的位置,并将第K个元素插入到这个元素的后面。或者找不到小于等于第K个元素的位置,就将原来第K个元素插入到数组的首地址。

图片演示


//插入排序
//平均时间复杂度:O(N^2)
//最坏情况复杂度:O(N^2)
//最好情况复杂度:O(N)
//空间复杂度:O(1)
//最多需要n(n−1)/2次比较
//最少需要n−1次比较
//稳定排序
void insertsort(vector<int>& a)
{
int n = a.size();
for (int i = 1; i < n; i++)
{
int insert_num = a[i], j;
for (j = i - 1; j >= 0; j--)
{
if (a[j] > insert_num)
a[j + 1] = a[j];
else
break;
}
a[j + 1] = insert_num;
}
}

选择排序
1、每一次从后面选择出一个最小的值(swap_pos),替换到前面来(i)。

//选择排序
//平均时间复杂度 O(n^2)
//最坏时间复杂度 O(n^2)
//最好时间复杂度 O(n^2)
//空间复杂度 O(1)
//我这个写法 是稳定排序
void select_sort(vector<int>& vt)
{
for (int i = 0; i < vt.size() - 1; i ++)
{
int swap_pos = i;
for (int j = i + 1; j < vt.size(); j++)
{
if (vt[swap_pos] > vt[j])
{
swap_pos = j;
}
}

if (swap_pos != i)
{
swap(vt[swap_pos], vt[i]);
}
}
}

希尔排序

希尔排序是在插入排序的基础上进行发展的,通过一个希尔增量先排序一定间隔的数据。

排序过程

插入排序每次与前面一个比较,然后再往前一个,而希尔排序每次往前K个;
当增量为1的时候,希尔排序与插入排序就完全是一样的过程;
所以代码也很好实现,将插入排序中增1的地方改为增K就行。

图片演示

初始81941196123517952858
第一趟5排序后35171128128194959658
第二趟3排序后28121135178158959694
第三趟1排序后11121728355881949596

复杂度分析

希尔排序的时间复杂度比较复杂,选用不同的希尔增量也会导致复杂度不同。//希尔排序
//最坏情况复杂度:O(N^2)
//不稳定排序
void shellsort(vector<int>& a)
{
int n = a.size();
for (int increment = n / 2; increment > 0; increment /= 2)
{
for (int i = increment; i < n; i++)
{
int insert_num = a[i], j;
for (j = i - increment; j >= 0; j -= increment)
{
if (a[j] > insert_num)
a[j + increment] = a[j];
else
break;
}
a[j + increment] = insert_num;
}
}
}

堆排序

排序过程

建立最大堆,建堆的过程是从N/2的位置开始,将父节点与子节点比较,如果子节点大于父节点则交换。为什么是N/2,是因为堆中树叶的个数是N/2。
从堆中删除堆顶元素,对于最大堆而言,堆顶元素也就是最大元素。每删除一个堆顶元素,就将堆顶元素放在数组的后面,因为每删除一个就出现一个空位,所以数组后面是有地方存放的。
进行N-1次的删除以后,整个数组就是排序的状态了。

图片演示


//堆排序
//建堆的平均时间是:O(N)
//建堆的最坏情况是:O(NlogN)
//删除元素的时间是:O(logN)
//整个排序平均时间复杂度:O(N+NlogN)=O(NlogN)
//最坏情况复杂度:O(NlogN)
//不稳定排序

//建立一个大顶堆O(n),要求就是 把最大的元素 移动到堆顶 也就是a[0]
void make_heap(vector<int>& a, int size) //size的当前堆的大小,也就是数组的前size个数
{
for (int i = size - 1; i > 0; i--)
{
if (i % 2 && a[i] > a[(i - 1) / 2])//奇数
swap(a[i], a[(i - 1) / 2]);
else if (i % 2 == 0 && a[i] > a[(i - 2) / 2])//偶数
swap(a[i], a[(i - 2) / 2]);
}
}
void heapsort(vector<int>& a)
{
int n = a.size();
while (n)
{
make_heap(a, n); //每次把新的最大元素移到堆顶,也就是a[0]
n--;
swap(a[0], a
); //然后把当前最大移动到后面来作为排好序的元素
}
}


归并排序

排序过程

将数组N从中间分成两个数组N1和N2;
将N1和N2分别递归用归并排序来排序。
归并N1与N2。

图片演示


//归并排序
//平均时间复杂度:O(NlogN)
//稳定排序
vector<int> mergeHelper(vector<int> &a, int left, int right)
{
if (left == right) return vector<int> (1, a[left]);
int mid = (right - left) / 2 + left;
vector<int> l = mergeHelper(a, left, mid);
vector<int> r = mergeHelper(a, mid + 1, right);
//merge
vector<int> ret;
int ll = 0, rr = 0;
while (ll < l.size() && rr < r.size())
{
if (l[ll] <= r[rr]) ret.push_back(l[ll++]);
else ret.push_back(r[rr++]);
}
while (ll < l.size()) ret.push_back(l[ll++]);
while (rr < r.size()) ret.push_back(r[rr++]);
return ret;
}

void mergesort(vector<int>& a)
{
a = mergeHelper(a, 0, a.size() - 1);
}


快速排序

排序过程

选择一个枢纽元,可以选择首,尾,中三个数的中位数作为枢纽元;
将枢纽元的为止与数组的尾地址进行交换;
定义两个指针,P1指向数组首地址,P2指向数组倒数第二个位置,P1所指元素的值与枢纽元比较,如果小于枢纽元则后移一位,如果大于就停下来。P1所指元素的值与枢纽元比较,如果大于枢纽元则前移一位,如果小于就停下来;
交换P1和P2所指向的元素;
重复3和4直到P1大于P2;
对数组的分割过程同样采用递归的方法。

快排是必须背住的排序!

//快速排序
//平均时间复杂度:O(NlogN)
//最坏情况复杂度:O(N^2)
//不稳定排序
void quicksortHelper(vector<int>& a, int start, int end)
{
if (start >= end) return;
int l = start, r = end;
int pivot = a[(end - start) / 2 + start];
while (l <= r)
{
while (l <= r && a[r] > pivot)  r--;
while (l <= r && a[l] < pivot)  l++;
if (l <= r) swap(a[l++], a[r--]);
}
quicksortHelper(a, start, r);
quicksortHelper(a, l, end);
}
void quicksort(vector<int>& a)
{
quicksortHelper(a, 0, a.size() - 1);
}
//快排的最差时间复杂度为O(n²)
//通常出现在选择的轴值(pivot)不能将数组划分为两个长度相等的子数组的时候
//一个较好的办法是“三数取中”,查看当前数组的第一个、中间一个和最后一个位置的数组,取其中位数,以此来降低轴值选择得不好的可能性。
int findmiddle(int a, int b, int c)
{
if (a >= b && a <= c)
return a;
else if (b >= a && b <= c)
return b;
else
return c;
}
void quicksortHelper(vector<int>& a, int start, int end)
{
if (start >= end) return;
int l = start, r = end;
int pivot = findmiddle(a[start], a[end], a[(end - start) / 2 + start]);
while (l <= r)
{
while (l <= r && a[r] > pivot)  r--;
while (l <= r && a[l] < pivot)  l++;
if (l <= r) swap(a[l++], a[r--]);
}
quicksortHelper(a, start, r);
quicksortHelper(a, l, end);
}
void quicksort(vector<int>& a)
{
quicksortHelper(a, 0, a.size() - 1);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: