1. 概述
十种常见排序算法可以分为以下两类
- 比较类排序:冒泡排序、选择排序、插入排序、希尔排序、快速排序、堆排序、归并排序
- 非比较类排序:桶排序、计数排序、基数排序、
2. 时间复杂度
3. 算法
3.1 冒泡排序
冒泡排序每次能够获取序列中的最大元素,并将其交换到正确位置
3.1.1 算法描述
- 从第一个元素开始,比较相邻元素,若左边的大于右边的,则交换两个元素
- 对所有相邻元素执行以上操作,从第一个到最后一个,从而将最大的元素交换到了最后
- 重复步骤二,除了比较最后一个元素
- 重复步骤1 2 3,获取正确序列
3.1.2 算法实现
void bubble_sort(vector<int> &nums)
{
int len = nums.size();
if (len >= 2)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - 1 - i; j++)
{
if (nums.at(j) > nums.at(j + 1))
{
swap(nums.at(j), nums.at(j + 1));
}
}
}
}
}
3.2 选择排序
选择排序能够每次选中最大或最小的元素,将其交换到无序区的最后
3.2.1 算法描述
- 初始状态下,无序区集合为所有元素,有序区为空
- 第一次,从无序区选择最小(大)的元素,记录其位置location,将nums.at(location)放置到有序区的最后,并将该元素剔除出无序区
- 重复上述操作,直至有序区满,无序区为空
3.2.2 算法实现
void selection_sort(vector<int> &nums)
{
int len = nums.size();
for (int i = 0; i < len; i++)
{
int minLocation = i;
for (int j = i; j < len; j++)
{
if (nums.at(j) < nums.at(minLocation))
{
swap(nums.at(j), nums.at(minLocation));
}
}
}
}
3.3 插入排序
3.3.1 算法描述
- 初始状态,有序区中只有数组的第一个元素,无序区包含从第二个元素到最后一个元素
- 取出无序区的第一个元素,将该元素与有序区中的元素比较
- 从有序区最后一个元素开始比较,若该元素小于有序区最后的元素,将两者交换,记录该元素的新位置,继续与有序区其他元素进行比较
- 直到在有序区中找到一个位置,前一个元素小于新元素,后一个元素大于新元素,则此处为新元素的正确位置,终止循环
- 重复步骤2 3 4,直到无序区为空,则所有元素在有序区均已找到正确位置
3.3.2 算法实现
void insert_sort(vector<int> &nums)
{
int len = nums.size();
for (int i = 1; i < len; i++)
{
int temp = i;
for (int j = i - 1; j >= 0; j--)
{
if (nums.at(temp) >= nums.at(j))
{
break;
}
else
{
swap(nums.at(temp), nums.at(j));
temp = j;
continue;
}
}
}
}
3.4 希尔排序
3.4.1 算法描述
- 将待排序的数组元素分为若干个序列 T0+j1k, T1+j1k, … Ti+ji*k
- 选择一个增量序列 Ti, Ti+1k, Ti+2k, … Ti+jk,(i+jk<len)
- 对上述增量序列进行插入排序
- 改变序列的个数,重复2 3操作,直到只剩一个序列,则该数组已完成排序
3.4.2 算法实现
void shell_sort(vector<int> &nums)
{
int len = nums.size();
int loop = len / 2;
while (loop >= 1)
{
for (int i = 0; i < loop; i++)
{
for (int j = 1; i + j * loop < len; j++)
{
int current = i + j * loop;
for (int k = j; k > 0; k--)
{
if (nums.at(current) > nums.at(i + (k - 1) * loop))
{
break;
}
else
{
swap(nums.at(current), nums.at(i + (k - 1) * loop));
current = i + (k - 1) * loop;
}
}
// print(nums);
}
}
loop /= 2;
}
}
3.5 快速排序
3.5.1 算法描述
- 选择一个数作为基准(一般为第一个)
- 将基准与其他数进行比较,比基准小的数放在左侧,比基准大的数放在右侧。该分区结束后,基准就位于它的正确位置,这个操作称为分区(partition)
- 对基准左侧和右侧的子序列执行分区操作
3.5.2 算法实现
void partition(vector<int> &nums, int start, int end)
{
if (start >= end)
{
return;
}
int pivot = nums.at(start);
int i = start, j = end;
while (i != j)
{
while (i < j && nums.at(j) >= pivot)
{
j--;
}
while (i < j && nums.at(i) <= pivot)
{
i++;
}
if (i < j)
{
swap(nums.at(i), nums.at(j));
}
}
swap(nums.at(i), nums.at(start));
partition(nums, start, i - 1);
partition(nums, i + 1, end);
}
void quick_sort(vector<int> &nums)
{
int len = nums.size();
partition(nums, 0, len - 1);
}
3.6 堆排序
3.6.1 算法描述
- 将初始待排序的数据构成一个大顶堆,获取无序区T[1,n]
- 将堆顶元素与最后一个元素进行交换,获取新的无序区T[1,n-1]
- 交换后的新的堆可能不满足大顶堆的性质,将T[1,n-1]构造成新的大顶堆
- 重复操作2 3,直至有序区满,无序区为空
3.6.2 算法实现
void getHeap(vector<int> &nums, int start, int end)
{
if (end < start)
{
return;
}
int parent = (end - start + 1) / 2;
for (int i = parent; i >= 0; i--)
{
if (2 * i + 1 <= end && nums.at(i) < nums.at(2 * i + 1))
{
swap(nums.at(i), nums.at(2 * i + 1));
}
if (2 * i + 2 <= end && nums.at(i) < nums.at(2 * i + 2))
{
swap(nums.at(i), nums.at(2 * i + 2));
}
}
getHeap(nums, 2 * start + 1, end);
getHeap(nums, 2 * start + 2, end);
}
void heap_sort(vector<int> &nums)
{
int len = nums.size();
for (int i = len - 1; i >= 0; i--)
{
getHeap(nums, 0, i);
print(nums);
swap(nums.at(0), nums.at(i));
}
}
3.7 归并排序
3.7.1 算法描述
- 将一个长度为n的序列分割为两个长度为n/2的子序列
- 对两个子序列分别执行归并排序
- 将两个子序列合并成一个有序序列
3.7.2 算法实现
void merge(vector<int> &nums, int start, int end)
{
if (start >= end)
{
return;
}
if (end - start == 1)
{
if (nums.at(start) > nums.at(end))
{
swap(nums.at(start), nums.at(end));
}
//print(nums);
return;
}
int middle = (end - start) / 2 + start;
merge(nums, start, middle);
merge(nums, middle + 1, end);
vector<int> current = nums;
int i = start, j = middle + 1, k = start;
while (i <= middle | j <= end)
{
if (i <= middle && j <= end)
{
if (nums.at(i) <= nums.at(j))
{
current.at(k) == nums.at(i);
i++;
k++;
continue;
}
else
{
current.at(k) = nums.at(j);
j++;
k++;
}
}
else if (i <= middle)
{
current.at(k) = nums.at(i);
i++;
k++;
}
else
{
current.at(k) = nums.at(j);
j++;
k++;
}
// print(current);
}
nums = current;
//print(nums);
}
void merge_sort(vector<int> &nums)
{
int len = nums.size();
merge(nums, 0, len - 1);
}
3.8 桶排序
3.8.1 算法描述
- 设置一个指定大小的空桶(max - min) / 10+1
- 便利序列中的所有元素,将元素放入对应的空桶中(根据个位)
- 将非空桶中的元素进行排序
- 将所有非空桶中的元素进行拼接
3.8.2 算法实现
void bucket_sort(vector<int> &nums)
{
int max = *max_element(nums.begin(), nums.end());
int min = *min_element(nums.begin(), nums.end());
int bucket = (max - min) / 10 + 1;
vector<list<int>> vecBucket(bucket);
for (int i = 0; i < nums.size(); i++)
{
vecBucket.at(nums.at(i) / 10).push_back(nums.at(i));
}
int temp = 0;
while (temp < bucket)
{
for (auto &element : vecBucket)
{
element.sort();
}
temp++;
}
nums.clear();
for (auto element : vecBucket)
{
for (auto data : element)
{
nums.push_back(data);
}
}
}
3.9 计数排序
3.9.1 算法描述
- 找出序列元素中的最大值和最小值,构造count(max-min+1)的数组
- 统计min~max之间的元素出现的次数,存入数组count
- 反向填充目标数组:count.at(i)=c,将i+min放入原始序列,c–
3.9.2 算法实现
void count_sort(vector<int> &nums)
{
int min = *min_element(nums.begin(), nums.end());
int max = *max_element(nums.begin(), nums.end());
vector<int> count(max - min + 1, 0);
for (int i = 0; i < nums.size(); i++)
{
count.at(nums.at(i) - min)++;
// cout << nums.at(i) << " " << count.at(nums.at(i) - min) << " h" << endl;
}
// print(count);
int location = 0;
for (int i = 0; i < max - min + 1; i++)
{
int frequence = count.at(i);
while (frequence != 0)
{
nums.at(location) = i + min;
frequence--;
location++;
}
// location += frequence;
}
}
3.10 基数排序
3.10.1 算法描述
- 获取序列元素中的最大值,并获取该最大值的位数
- 从所有元素的个位开始,将个位相同的元素放入同一个数组base.at(i)中
- 将base数组中的所有元素按顺序放入原始数组中
- 对所有元素重复上述操作,十位、百位,直至最大值的位数,由此可以获得排好序的序列
3.10.2 算法实现
void base_sort(vector<int> &nums)
{
int max = *max_element(nums.begin(), nums.end());
int len = nums.size();
int div = 1;
vector<vector<int>> base(10);
while ((max / div) % 10 != 0)
{
cout << (max / div) % 10 << endl;
for (int i = 0; i < 10; i++)
{
base.at(i).clear();
}
for (int i = 0; i < len; i++)
{
int last = (nums.at(i) / div) % 10;
base.at(last).push_back(nums.at(i));
}
int i = len - 1;
while (i >= 0)
{
for (int j = 0; j < 10; j++)
{
while (!base.at(j).empty())
{
cout << "base.at(" << j << "): ";
//print(base.at(j));
int current = base.at(j).back();
base.at(j).pop_back();
nums.at(i) = current;
i--;
}
}
}
//print(nums);
div *= 10;
}
}