1.插入排序
假设有N个待排序元素,下标从0到N - 1。从位置p = 1开始从前往后扫描直到N - 1,在第p趟时,我们将位置p上的元素向左移直至找到它前面的元素都比它小为止。时间复杂度为
O
(
N
2
)
O(N^2)
O(N2)。
#ifndef INSERTION_SORT_H
#define INSERTION_SORT_H
#include <vector>
#include <functional>
using namespace std;
template <typename Comparable>
void insertionSort(vector<Comparable> & a)
{
for (int p = 1; p < a.size(); ++p) {
Comparable tmp = std::move(a[p]);
int j;
for (j = p; j > 0 && tmp < a[j - 1]; --j)
a[j] = std::move(a[j - 1]);
a[j] = std::move(tmp);
}
}
template <typename RandomIterator, typename Comparator>
void insertionSort(const RandomIterator & begin, const RandomIterator & end, Comparator lessThan)
{
if (begin == end)
return;
RandomIterator j;
for (RandomIterator p = begin + 1; p != end; ++p) {
auto tmp = std::move(*p);
for (j = p; j != begin && lessThan(tmp, *(j - 1)); --j)
*j = std::move(*(j - 1));
*j = std::move(tmp);
}
}
template <typename RandomIterator>
void insertionSort(const RandomIterator & begin, const RandomIterator & end)
{
insertionSort(begin, end, less<decltype(*begin)>{});
}
#endif
#include "insertion_sort.h"
#include <iostream>
using namespace std;
int main()
{
int x;
vector<int> a;
for (int i = 0; i < 10; i++) {
cin >> x;
a.push_back(x);
}
insertionSort(begin(a), end(a));
for (auto & tmp : a)
cout << tmp << " ";
cout << endl;
return 0;
}
2.希尔排序
希尔排序是通过比较相距一定间隔的元素实现的,算法的大致思路是先设定一个间隔值gap,然后从第一个元素开始,依次比较与当前元素和相距为gap元素的大小,若当前元素大则交换,当比较的元素到达最后一个时,gap值除以2,再重新从前往后比较,直到gap值为0,排序结束。时间复杂度的上界为
O
(
N
2
)
O(N^2)
O(N2)。要想提高性能可以使用Hibbard增量序列,该增量的通式为2的k次方减1,k从1开始取,最坏运行时间为
O
(
N
3
/
2
)
O(N^{3/2})
O(N3/2),还有Sedgewick提出的几种增量序列。
#ifndef SHELL_SORT_H
#define SHELL_SORT_H
#include <vector>
using namespace std;
template <typename Comparable>
void shellsort(vector<Comparable> & a)
{
for (int gap = a.size() / 2; gap > 0; gap /= 2)
for (int i = gap; i < a.size(); ++i) {
Comparable tmp = std::move(a[i]);
int j = i;
for (; j >= gap && tmp < a[j - gap]; j -= gap)
a[j] = std::move(a[j - gap]);
a[j] = std::move(tmp);
}
}
#endif
#include "shell_sort.h"
#include <iostream>
using namespace std;
int main()
{
int x;
vector<int> v;
for (int i = 0; i < 10; i++) {
cin >> x;
v.push_back(x);
}
shellsort(v);
for (auto & tmp : v)
cout << tmp << " ";
cout << endl;
return 0;
}
3.堆排序
堆排序的运行时间为
O
(
N
l
o
g
N
)
O(NlogN)
O(NlogN),需要一个附加的数组。该算法是通过依次删除(max)堆中最大的元素并把该元素放到堆刚刚减少元素的位置上来实现的,执行N - 1次删除最大值操作后,数组中的元素就变成从小到大排列了。
#ifndef HEAP_SORT_H
#define HEAP_SORT_H
#include <vector>
using namespace std;
inline int leftChild(int i)
{
return 2 * i + 1;
}
template <typename Comparable>
void percDown(vector<Comparable> & a, int i, int n)
{
int child;
Comparable tmp;
for (tmp = std::move(a[i]); leftChild(i) < n; i = child) {
child = leftChild(i);
if (child != n - 1 && a[child] < a[child + 1])
++child;
if (tmp < a[child])
a[i] = std::move(a[child]);
else
break;
}
a[i] = std::move(tmp);
}
template <typename Comparable>
void heapsort(vector<Comparable> & a)
{
for (int i = a.size() / 2 - 1; i >= 0; --i) /* buildHeap */
percDown(a, i, a.size());
for (int j = a.size() - 1; j > 0; --j) { /* deleteMax */
std::swap(a[0], a[j]);
percDown(a, 0, j);
}
}
#endif
#include "heap_sort.h"
#include <iostream>
using namespace std;
int main()
{
int x;
vector<int> v;
for (int i = 0; i < 10; i++) {
cin >> x;
v.push_back(x);
}
heapsort(v);
for (auto & tmp : v)
cout << tmp << " ";
cout << endl;
return 0;
}
4.归并排序
归并排序算法的大概思路是先把待排序数组分成两部分,分别对那两部分进行排序,再将那两部分合并到一起。时间复杂度为
O
(
N
l
o
g
N
)
O(NlogN)
O(NlogN)。
#ifndef MERGE_SORT_H
#define MERGE_SORT_H
#include <vector>
using namespace std;
/**
* tmpArray为放置归并结果的数组
* leftPos为子数组最左元素的下标
* rightPos为后半部分起点的下标
* rightEnd为子数组最右元素的下标
*/
template <typename Comparable>
void merge(vector<Comparable> & a, vector<Comparable> & tmpArray, int leftPos, int rightPos, int rightEnd)
{
int leftEnd = rightPos - 1;
int tmpPos = leftPos;
int numElements = rightEnd - leftPos + 1;
while (leftPos <= leftEnd && rightPos <= rightEnd)
if (a[leftPos] <= a[rightPos])
tmpArray[tmpPos++] = std::move(a[leftPos++]);
else
tmpArray[tmpPos++] = std::move(a[rightPos++]);
while (leftPos <= leftEnd)
tmpArray[tmpPos++] = std::move(a[leftPos++]);
while (rightPos <= rightEnd)
tmpArray[tmpPos++] = std::move(a[rightPos++]);
for (int i = 0; i < numElements; ++i, --rightEnd)
a[rightEnd] = std::move(tmpArray[rightEnd]);
}
template <typename Comparable>
void mergeSort(vector<Comparable> & a, vector<Comparable> & tmpArray, int left, int right)
{
if (left < right) {
int center = (left + right) / 2;
mergeSort(a, tmpArray, left, center);
mergeSort(a, tmpArray, center + 1, right);
merge(a, tmpArray, left, center + 1, right);
}
}
template <typename Comparable>
void mergeSort(vector<Comparable> & a)
{
vector<Comparable> tmpArray(a.size());
mergeSort(a, tmpArray, 0, a.size() - 1);
}
#endif
#include "merge_sort.h"
#include <iostream>
using namespace std;
int main()
{
int x;
vector<int> v;
for (int i = 0; i < 10; i++) {
cin >> x;
v.push_back(x);
}
mergeSort(v);
for (auto & tmp : v)
cout << tmp << " ";
cout << endl;
return 0;
}
5.快速排序
快速排序的思路大致是取待排序数组
S
S
S中的任一元素为枢纽元,将除去枢纽元外的元素划分为小于等于枢纽元的集合
S
1
S1
S1以及大于等于枢纽元的集合
S
2
S2
S2,返回排序后的
S
1
S1
S1,后跟枢纽元,继而再跟
S
2
S2
S2。在这里,采用三数中值法选取枢纽元,选取
S
S
S中最左边,最右边以及中间的元素,取它们大小处于中间的值作为枢纽元,并把枢纽元与最后的元素交换位置。接着是分割策略,把
S
S
S分成三部分,这里用
i
i
i代表
S
S
S的最左边元素,
j
j
j代表右边枢纽元的前一个元素,将
i
i
i右移,移过那些小于枢纽元的元素,将
j
j
j左移,移过那些大于枢纽元的元素,当
i
i
i和
j
j
j停止时,如果
i
i
i在
j
j
j的左边,那么将这两个元素交换,重复该过程直到
i
i
i和
j
j
j彼此交错为止。
对于很小的数组,快速排序性能不如插入排序,故这里当元素个数小于10的时候用插入排序。
#ifndef QUICK_SORT_H
#define QUICK_SORT_H
#include <vector>
using namespace std;
template <typename Comparable>
void insertionSort(vector<Comparable> & a, int left, int right)
{
if (left == right)
return;
for (int p = left; p <= right; ++p) {
Comparable tmp = std::move(a[p]);
int j;
for (j = p; j > 0 && tmp < a[j - 1]; --j)
a[j] = std::move(a[j - 1]);
a[j] = std::move(tmp);
}
}
/*
* 返回 left center right 三项的中值
*/
template <typename Comparable>
const Comparable & median3(vector<Comparable> & a, int left, int right)
{
int center = (left + right) / 2;
if (a[center] < a[left])
std::swap(a[left], a[center]);
if (a[right] < a[left])
std::swap(a[left], a[right]);
if (a[right] < a[center])
std::swap(a[center], a[right]);
std::swap(a[center], a[right - 1]);
return a[right - 1];
}
/*
* 使用三中值分割法
*/
template <typename Comparable>
void quicksort(vector<Comparable> & a, int left, int right)
{
if (left + 10 <= right) {
const Comparable & pivot = median3(a, left, right);
int i = left, j = right - 1;
// 开始分割
for (; ;) {
while (a[++i] < pivot) {}
while (pivot < a[--j]) {}
if (i < j)
std::swap(a[i], a[j]);
else
break;
}
std::swap(a[i], a[right - 1]); // 恢复枢纽元
quicksort(a, left, i - 1); // 将小于等于枢纽元的元素排序
quicksort(a, i + 1, right); // 将大于等于枢纽元的元素排序
}
else // 若元素小于10个则进行插入排序
insertionSort(a, left, right);
}
template <typename Comparable>
void quicksort(vector<Comparable> & a)
{
quicksort(a, 0, a.size() - 1);
}
#endif
#include "quick_sort.h"
#include <iostream>
using namespace std;
int main()
{
int x;
vector<int> v;
for (int i = 0; i < 15; i++) {
cin >> x;
v.push_back(x);
}
quicksort(v);
for (auto & tmp : v)
cout << tmp << " ";
cout << endl;
return 0;
}
6.桶排序
桶排序中待排序的元素必须只由小于
M
M
M的正整数构成的,算法的思路大概是使用一个大小为
M
M
M的count数组,初始化为全0,当读入待排序元素
a
i
ai
ai时,count[
a
i
ai
ai]增1。在所有的输入数据读入后,扫描count数组,打印出排序后的数据,时间复杂度为
O
(
M
+
N
)
O(M + N)
O(M+N)。
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
int findMax(vector<int> & v, int N)
{
int Max = v[0];
for (int i = 1; i < N; i++) {
if (v[i] > Max)
Max = v[i];
}
return Max;
}
void bucketsort(vector<int> & v, int N)
{
int M, i;
M = findMax(v, N);
int count[M + 1];
memset(count, 0, sizeof(count));
for (i = 0; i < N; i++)
count[v[i]]++;
for (i = 0; i <= M; i++) {
while (count[i] > 0) {
cout << i << " ";
count[i]--;
}
}
cout << endl;
}
int main()
{
int x;
vector<int> v;
for (int i = 0; i < 13; i++) {
cin >> x;
v.push_back(x);
}
bucketsort(v, v.size());
return 0;
}
7.基数排序
对字符串排序:
void radixSortA(vector<string> & arr, int stringLen)
{
const int BUCKETS = 256;
vector<vector<string> > buckets(BUCKETS);
for (int pos = stringLen - 1; pos >= 0; --pos) {
for (string & s : arr)
buckets[s[pos]].push_back(std::move(s));
cout << endl;
int idx = 0;
for (auto & thisBucket : buckets) {
for (string & s : thisBucket)
arr[idx++] = std::move(s);
thisBucket.clear();
}
}
}
对字符串进行计数排序:
void countingRadixSort(vector<string> & arr, int stringLen)
{
const int BUCKETS = 256;
int N = arr.size();
vector<string> buffer(N);
vector<string> *in = &arr;
vector<string> *out = &buffer;
for (int pos = stringLen - 1; pos >= 0; --pos) {
vector<int> count(BUCKETS + 1);
for (int i = 0; i < N; ++i)
++count[(*in)[i][pos] + 1];
for (int b = 1; b <= BUCKETS; ++b)
count[b] += count[b - 1];
for (int i = 0; i < N; ++i)
(*out)[count[(*in)[i][pos]]++] = std::move((*in)[i]);
std::swap(in, out);
}
if (stringLen % 2 == 1)
for (int i = 0; i < arr.size(); ++i)
(*out)[i] = std::move((*in)[i]);
}
对长度不一致的字符串进行排序:
void radixSort(vector<string> & arr, int maxLen)
{
const int BUCKETS = 256;
vector<vector<string> > wordsByLength(maxLen + 1);
vector<vector<string> > buckets(BUCKETS);
for (string & s : arr)
wordsByLength[s.length()].push_back(std::move(s));
int idx = 0;
for (auto & wordList : wordsByLength)
for (string & s : wordList)
arr[idx++] = std::move(s);
int startingIndex = arr.size();
for (int pos = maxLen - 1; pos >= 0; --pos) {
startingIndex -= wordsByLength[pos + 1].size();
for (int i = startingIndex; i < arr.size(); ++i)
buckets[arr[i][pos]].push_back(std::move(arr[i]));
idx = startingIndex;
for (auto & thisBucket : buckets) {
for (string & s : thisBucket)
arr[idx++] = std::move(s);
thisBucket.clear();
}
}
}
8.冒泡排序
#include <vector>
#include <iostream>
using namespace std;
template <typename Comparable>
void bubblesort(vector<Comparable> & a, int n)
{
for (int i = 0; i < n - 1; ++i) {
for (int j = i; j < n; ++j)
if (a[i] > a[j])
std::swap(a[i], a[j]);
}
}
template <typename Comparable>
void bubblesort(vector<Comparable> & a)
{
bubblesort(a, a.size());
}
int main()
{
int x;
vector<int> v;
for (int i = 0; i < 2; i++) {
cin >> x;
v.push_back(x);
}
bubblesort(v);
for (auto & tmp : v)
cout << tmp << " ";
cout << endl;
return 0;
}
9.选择排序
从待排序数组的第一个元素开始比较,找出比当前元素小的并且是最小的元素和第一个元素互换,以此类推。
template <typename Comparable>
void selectionSort(vector<Comparable> & toSort)
{
int minIndex;
for (int i = 0; i < toSort.size(); ++i) {
minIndex = i;
for (int j = i + 1; j < toSort.size(); ++j) {
if (toSort[j] < toSort[minIndex])
swap(toSort[j], toSort[i]);
}
}
}