排序算法是必须要掌握的基础知识,针对于各种排序算法,下面是基础的C++实现:
排序方法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
简单选择排序(键值交换为O(n)) | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
直接插入排序(基本有序时表现最好) | O(n^2) | O(n)(升序) | O(n^2)(降序) | O(1) | 稳定 |
希尔排序(插入排序的增强版) | O(nlogn)~O(n^2) | O(n^1.3) | 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)~O(n)(因为迭代) | 不稳定 |
1、选择排序:
选择排序的优势在于键值交换仅为O(n)
void SelectionSort(int (&A)[N])
{
for (int i = 0; i < N - 1; i++) //从0开始,共操作到N-2下标,操作N-1次。即可完成排序
{
int min = i;
for (int j = i+1; j < N; j++)
if (A[j] < A[min])
min = j;
swap(A[min], A[i]);
}
cout << "Selection Sort:\n"<<"Time: Avg-N2 不稳定\n";
for (auto &a : A)
cout << a << " ";
cout << endl << endl << endl;
}
2、冒泡排序
稳定是其优点!!
void BubbleSort(int(&A)[N])
{
for (int i = 0; i < N - 1; i++) //记录次数 共需操作N-1次
for (int j = 0; j < N - 1 - i; j++)
if (A[j]>A[j + 1])
swap(A[j], A[j + 1]);
cout << "Bubble Sort:\n" << "Time: Avg-N2 Best-N 稳定\n";
for (auto &a : A)
cout << a << " ";
cout << endl << endl << endl;
}
3、插入排序
插入排序在遇到基本有序的数组时表现是最好的,而且是稳定的
void InsertSort(int(&A)[N])
{
for (int i = 0; i < N; i++)
{
int val = A[i];
int j = i; //从后往前进行寻找
while (j > 0 && A[j - 1] > val)
{
A[j] = A[j - 1];
j--;
}
A[j] = val; //找到合适位置之后插入
}
cout << "Insert Sort:\n" << "Time: Avg-N2 Best-N 稳定\n"<<"注意:插入排序对于基本有序的序列表现很好,接近N"<<endl;
for (auto &a : A)
cout << a << " ";
cout << endl << endl << endl;
}
4、合并排序
优点是稳定,缺点是需要额外空间
void Merge(int start, int end, int(&A)[N], int(&res)[N])
{
int left = (end - start + 1) / 2;
int left_ind = start;
int right_ind = start + left;
int res_ind = start;
while (left_ind < start + left && right_ind<=end)
{
if (A[left_ind] <= A[right_ind])
res[res_ind++] = A[left_ind++];
else
res[res_ind++] = A[right_ind++];
}
while (left_ind < start + left)
res[res_ind++] = A[left_ind++];
while (right_ind <= end)
res[res_ind++] = A[right_ind++];
}
void MergeSort(int start, int end, int(&A)[N], int(&res)[N])
{
if (end - start == 1&&A[start]>A[end])
{
swap(A[end], A[start]);
return;
}
else if (end == start)
return;
else
{
MergeSort(start, (end - start + 1) / 2 + start - 1, A, res);
MergeSort((end - start + 1) / 2 + start, end, A, res);
Merge(start, end, A, res);
for (int i = start; i <= end; i++) //元素复制,必不可少的一步
A[i] = res[i];
}
}
5、快速排序
表现较好,但是遇到有序序列排序的时候会退化为冒泡
int Partition(int low, int high, int(&A)[N])
{
int key = A[low];
while (high > low)
{
while (high > low&&A[high] >= key) high--;
swap(A[high], A[low]);
while (high > low&&A[low] <= key) low++;
swap(A[low], A[high]);
}
return low;
}
void QuickSort(int low, int high, int(&A)[N])
{
if (high > low)
{
int p = Partition(low, high, A);
QuickSort(low, p - 1, A);
QuickSort(p + 1, high, A);
}
}
6、堆排序:构造堆——>堆排序
在堆中删除根键是:O(logN)
在叶子上插入键也是:O(logN)
堆排是:构造堆O(N)+n-1次的删除操作O(NlogN),所以最终时间复杂度为O(NlogN)
void HeapBottomTop(int(&A)[N+1]) //自底向上构造堆
{
for (int i = N / 2; i > 0; i--)
{
int k = i, j = 2 * k;
while (j <= N )
{
if (j < N && A[j + 1] > A[j])
j = j + 1;
if (A[j] > A[k])
{
swap(A[k], A[j]);
k = j;
j = 2 * k;
}
else
break;
}
}
}
void makeHeap(int end, int(&A)[N + 1])
{
int k = 1, j = 2 * k;
while (j <= end)
{
if (j < end && A[j + 1] > A[j])
j = j + 1;
if (A[j] > A[k])
{
swap(A[k], A[j]);
k = j;
j = 2 * k;
}
else
break;
}
}
void HeapSort(int(&A)[N + 1])
{
HeapBottomTop(A);
for (int i = 1; i < N ; i++)
{
swap(A[1], A[N + 1 - i]); //相当于删除最大的键
makeHeap(N - i, A); //重新堆化
}
cout << "Heap Sort:\n" << "Time: Avg-NlogN 不稳定" << endl;
for (int i = 1; i < N + 1;i++)
cout << A[i] << " ";
cout << endl << endl << endl;
}
7.拓扑排序
用于课表选择等等,感觉上是一个O(N2)
//拓扑排序
//1. Kahn算法,BFS统计入度vector<vector<int>> edges为边连通的情况;
void TopologicalSort()
{
int N;//顶点数
vector<vector<int>> edges;
vector<int> res;
vector<int> indegree(N, 0);
for (int i = 0; i < edges.size(); i++)
for (int j = 0; j < edges[i].size(); j++)
indegree[j]++;
queue<int> q;
for (int i = 0; i < N; i++)
if (indegree[i] == 0)
q.push(i);
int cnt = 0;
while (!q.empty())
{
int tmp = q.front();
q.pop();
cnt++;
res.push_back(tmp);
for (int i = 0; i < edges[tmp].size(); i++)
{
int tmpnode = edges[tmp][i];
if (--indegree[tmpnode] == 0)
q.push(tmpnode);
}
}
if (cnt == N)
cout << "可以进行拓扑排序,结果存储在res" << endl;
else
cout << "无法进行拓扑排序,存在环" << endl;
}
//2.使用DFS的算法
void TopologicalSort()
{
vector<vector<int>> edges;
vector<int> res;
vector<int> visit(N, 0);
for (int i = 0; i < N;i++)
if (!DFS(edges, visit, res, i))
res.clear(); //无法进行拓扑排序
if (res.size()==N) //如果可以进行拓扑排序
reverse(res.begin(), res.end()); //然后去反得到拓扑序
}
bool DFS(vector<vector<int>> &edges, vector<int> &visit, vector<int> &res, int node)
{
if (visit[node] == -1)
return false; //说明成环,无法进行拓扑排序
else if (visit[node] == 1)
return true;
visit[node] = -1;
for (int i = 0; i < edges[node].size(); i++)
if (!DFS(edges, visit, res, edges[node][i]))
return false;
visit[node] = 1;
res.push_back(node); //记录其出栈的顺序(关键)!!!
return true;
}