简单选择排序
简单选择排序是一种直观的排序算法,其思想是在未排序的序列中选出最小的元素和序列的首位元素交换,接下来在剩下的未排序序列中再选出最小元素与序列的第二位元素交换,依此类推,最后形成从小到大的已排序序列。
void swap (int& a, int& b) {
int temp{ a };
a = b;
b = temp;
}
void SimpleSelectionSort(vector<int>& v) {
for (int i = 0; i < v.size() - 1; ++i) {
for (int j = i + 1; j < v.size(); ++j) {
if (v[i] > v[j]) {
swap(v[i], v[j]);
}
}
}
}
简单排序无论在什么情况下,都需要比较N * (N - 1) / 2次,故其时间复杂度为 O ( N 2 ) O(N^2) O(N2)
堆排序
堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一种特殊的二叉树,可分为最小堆和最大堆,最大堆的每个子结点总是小于它的父结点,最小堆的每个子结点总是大于它的父结点。堆是一个完全二叉树,一般用数组(或者vector)实现。
考虑到待排序的元素可能会有大小相同的,我们需要在构建堆的时候稍微做一些改动,将 > 改为 >=(或者是 < 改为 <=)。
数据是从第 0 个单元开始存放的,对于第 i 个单元的结点,其左孩子的编号为 2i + 1,右孩子的编号为2i + 2;其父结点的编号是(i - 1) / 2。
void swap (int& a, int& b) {
int temp{ a };
a = b;
b = temp;
}
void PercDown(vector<int>& v, int p, int N)//下滤函数,N为待调整部分堆的规模大小
{
/*将容器v中以v[p]为根的子堆调整为最大堆*/
int X{ v[p] };//取出根节点存放的值
int Parent, Child;
for (Parent = p; Parent * 2 + 1 < N; Parent = Child) {
Child = Parent * 2 + 1;
if (Child != N - 1 && v[Child] < v[Child + 1])
++Child;//Child指向左右子结点的较大者
if (X >= v[Child])
break;
else
v[Parent] = v[Child];
}
v[Parent] = X;
}
void HeapSort(vector<int>& v)
{
for (int i = v.size() / 2 - 1; i >= 0; --i) {
PercDown(v, i, v.size());//建立最大堆,此时每一步待调整的堆都为完整的堆
}
for (int i = v.size() - 1; i > 0; --i) {
swap(v[0], v[i]);//交换一次后,待调整的堆的大小减一
PercDown(v, 0, i);
}
}
对堆排序而言,一次下滤操作(PercDown)的时间复杂度是 l o g 2 N log _2N log2N,建立最大堆需要N / 2 -1次下滤操作,故建立最大堆的时间复杂度为 O ( N l o g 2 N ) O(Nlog_{2}N) O(Nlog2N);额外的空间复杂度为 O ( 1 ) O(1) O(1)。
归并排序
以下代码是归并排序基于连续线性空间的递归实现,需要补充非递归实现版本,以及对链表结构写出递归版本和非递归版本。
#include <stdlib.h>
#include <vector>
#include <iostream>
using namespace std;
/*归并排序的递归实现*/
void Merge(vector<int> &v, vector<int> &v1, int L_start, int R_start, int R_end)
{
int L_end{ R_start - 1 }, number{ R_end - L_start + 1 }, temp{ L_start };
while (L_start <= L_end && R_start <= R_end)
{
if (v[L_start] <= v[R_start]) {
v1[temp++] = v[L_start++];
}
else {
v1[temp++] = v[R_start++];
}
}
while (L_start <= L_end)
v1[temp++] = v[L_start++];
while (R_start <= R_end)
v1[temp++] = v[R_start++];
for (int i = 0; i < number; ++i) {
v[R_end] = v1[R_end];
--R_end;
}
}
void MSort(vector<int> &v, vector<int> &v1, int start, int end)
{
int center;
if (start < end)
{
center = (start + end) / 2;
MSort(v, v1, start, center);
MSort(v, v1, center + 1, end);
Merge(v, v1, start, center + 1, end);
}
}
void MergeSort(vector<int> &v)
{
vector<int> v1{};
v1.resize(v.size());
MSort(v, v1, 0, v.size() - 1);
}
int main()
{
int N, X;
cin >> N;
vector<int> v{};
for (int i = 0; i < N; ++i)
{
cin >> X;
v.push_back(X);
}
MergeSort(v);
for (auto x : v)
cout << x << ' ';
system("pause");
return 0;
}
快速排序
快速排序也是交换排序的一种,但和冒泡排序不同的是,冒泡排序只比较相邻两个记录的顺序,而快速排序的原理是:将未排序元素根据一个作为基准的”主元“分为两个子序列,其中一个子序列的记录均大于主元,而另一个子序列均小于主元,然后递归地对这两个子序列用类似的方法进行排序。
//定义比大小函数,s1 < s2在当前题目环境下的新定义
bool isSmaller(string& s1, string& s2)
{
return (s1 + s2 < s2 + s1);
}
//left,right和mid的三者的排列顺序
string threeSort(vector<string>& ss, int left, int right)
{
int mid = (left + right) / 2;
if (isSmaller(ss[mid], ss[left])) {
swap(ss[mid], ss[left]);
}
if (isSmaller(ss[right], ss[left])) {
swap(ss[right], ss[left]);
}
if (isSmaller(ss[right], ss[mid])) {
swap(ss[right], ss[mid]);
}
swap(ss[mid], ss[right - 1]);
return ss[right - 1];
}
//快排的核心函数
void qSort(vector<string>& ss, int left, int right)
{
if (right - left > 1) {//最少3个
string sMid = threeSort(ss, left, right);
int low = left; int high = right - 1;
while(true) {
while(isSmaller(ss[++low], sMid));
while(isSmaller(sMid, ss[--high]));
if (low < high) {
swap(ss[low], ss[high]);
}
else
break;
}
swap(ss[low], ss[right - 1]);
qSort(ss, left , low - 1);
qSort(ss, low + 1, right);
}
if (right - left == 1 && isSmaller(ss[right], ss[left])) {
//只有两个元素且右边元素小于左边的
swap(ss[right], ss[left]);
}
}
//快排
void quickSort(vector<string>& ss)
{
qSort(ss, 0, ss.size() - 1);
}
快排典型示例:
void quickSort(vector<int>& nums, int l, int r) {// 手撕快排
if (l >= r)
return;
int L = l, R = r;
int pivot = nums[l];
while (l < r) {
while (l < r && nums[r] >= pivot) {
--r;
}
nums[l] = nums[r];
while (l < r && nums[l] <= pivot) {
++l;
}
nums[r] = nums[l];
}
nums[l] = pivot;
quickSort(nums, L, l - 1);
quickSort(nums, l + 1, R);
}
leetcode练习题 215. 数组中的第K个最大元素
class Solution {
public:
//手写快排
void quick_sort(vector<int>& nums, int l, int r) {
int left = l, right = r;
if (left < right) {
int X = nums[l];
while (left < right) {
while (left < right && nums[right] >= X)
--right;
nums[left] = nums[right];
while(left < right && nums[left] < X)
++left;
nums[right] = nums[left];
}
nums[left] = X;
quick_sort(nums, l, left - 1);
quick_sort(nums, left + 1, r);
}
}
int findKthLargest(vector<int>& nums, int k) {
int n = nums.size();
quick_sort(nums, 0, n - 1);
return nums[n - k];
}
};
本文详细讲解了简单选择排序、堆排序以及归并排序的基本原理和实现,包括它们的时间复杂度分析,并对比了快速排序的分治策略。通过实例演示,展示了这些排序算法在实际应用中的操作和效率提升。
615

被折叠的 条评论
为什么被折叠?



