返回分类:全部文章 >> 基础知识
返回上级:算法 - 查找与排序 (Searching and Sorting)
常用交换排序:
冒泡排序(Bubble Sort),也称起泡排序;
快速排序(Quick Sort),也称分区排序,它也是一个递归的过程。
本文将用C++实现插入冒泡排序和快速排序算法。
在查看本文之前,需要一些程序语言的基础。
所有排序方法中,统一使用如下库与结构:
// Author: https://blog.csdn.net/DarkRabbit
// Sorted Dependency
#pragma once
#include
#include
#include
template<typename T>
struct MyItem
{
int key;
T data;
MyItem(){ key = -1; }
MyItem(const int& k) : key(k){ }
MyItem(const int& k, const T& d) : key(k), data(d){ }
};
数据表统一使用了std::vector
,如果你使用静态数组MyItem
或指针数组MyItem
,那么还要传入元素数量size
。
示例所用数据:
元素数量为size = 15
;
关键字为key = { 123, 122, 565, 22, 3, 64, 73, 44, 287, 6, 9, 83, 25, 42, 13 }
。
交换排序基本思路:根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置。
常见交换排序:
冒泡排序(Bubble Sort),也称起泡排序
快速排序(Quick Sort),也称分区排序,它也是一个递归的过程
假设元素数量为 n ,交换过程:
冒泡排序:通过 n-1 次冒泡进行排序,检测到第 i 个关键字时,从 j = {n-1, n-2, n-3 …} 开始依次两两逆序比较(也可以从0开始正序比较),如果左边大则交换。
快速排序:起始 i = 0
, j = n-1
key = item[i]
;item[j]
于item[i]
交换;item[i]
与item[j]
交换;具体过程:
key = { 123, 122, 565, 22, 3, 64, 73, 44, 287, 6, 9, 83, 25, 42, 13 }
冒泡排序:
第一次搜索:
从13开始,比42小,交换,比25小,交换..直到比9大
从9开始,比6大
从6开始...
直到3比较完,结束
重复过程。
冒泡排序详细过程:
1: 3 123 122 565 22 6 64 73 44 287 9 13 83 25 42
2: 3 6 123 122 565 22 9 64 73 44 287 13 25 83 42
3: 3 6 9 123 122 565 22 13 64 73 44 287 25 42 83
4: 3 6 9 13 123 122 565 22 25 64 73 44 287 42 83
5: 3 6 9 13 22 123 122 565 25 42 64 73 44 287 83
6: 3 6 9 13 22 25 123 122 565 42 44 64 73 83 287
7: 3 6 9 13 22 25 42 123 122 565 44 64 73 83 287
8: 3 6 9 13 22 25 42 44 123 122 565 64 73 83 287
9: 3 6 9 13 22 25 42 44 64 123 122 565 73 83 287
10: 3 6 9 13 22 25 42 44 64 73 123 122 565 83 287
11: 3 6 9 13 22 25 42 44 64 73 83 123 122 565 287
12: 3 6 9 13 22 25 42 44 64 73 83 122 123 287 565
13: 3 6 9 13 22 25 42 44 64 73 83 122 123 287 565
key = { 123, 122, 565, 22, 3, 64, 73, 44, 287, 6, 9, 83, 25, 42, 13 }
快速排序:
第一次划分: key=123 i=0 j=14
从 j 逆序查找,直到找到 13 ,交换 123 和 13
从 i 正序查找,直到找到 565 ,交换 123 和 565
继续从 j 逆序查找,直到找到 42 ,交换 123 和 42
继续从 i 正序查找,直到找到 287 ,交换 123 和 287
继续从 j 逆序查找,直到找到 25 ,交换 123 和 25
继续从 i 正序查找,与 j 碰撞,得到第一次搜索后的表
然后以 i 或 j 为分割点,将表分成左右两份
结束第一次划分
分别对两份表进行同样操作,直到不可再分。
快速排序详细过程:
1:
(1): 13 122 123 22 3 64 73 44 287 6 9 83 25 42 565
(2): 13 122 42 22 3 64 73 44 123 6 9 83 25 287 565
(3): 13 122 42 22 3 64 73 44 25 6 9 83 123 287 565
2:
(4): 9 13 42 22 3 64 73 44 25 6 122 83 123 287 565
(5): 9 6 13 22 3 64 73 44 25 42 122 83 123 287 565
(6): 9 6 3 13 22 64 73 44 25 42 122 83 123 287 565
(7): 9 6 3 13 22 64 73 44 25 42 122 83 123 287 565
3:
(8): 3 6 9 13 22 64 73 44 25 42 122 83 123 287 565
4:
(9): 3 6 9 13 22 64 73 44 25 42 122 83 123 287 565
5:
(10): 3 6 9 13 22 64 73 44 25 42 122 83 123 287 565
6:
(11): 3 6 9 13 22 42 64 44 25 73 122 83 123 287 565
(12): 3 6 9 13 22 42 25 44 64 73 122 83 123 287 565
7:
(13): 3 6 9 13 22 25 42 44 64 73 122 83 123 287 565
8:
(14): 3 6 9 13 22 25 42 44 64 73 122 83 123 287 565
9:
(15): 3 6 9 13 22 25 42 44 64 73 83 122 123 287 565
10:
(16): 3 6 9 13 22 25 42 44 64 73 83 122 123 287 565
排序方法 | 最好时间 | 最差时间 | 平均时间 | 辅助空间 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n) | O(n2) | O(n2) | O(1) | 稳定 |
快速排序 | O(n log2n) | O(n2) | O(n log2n) | O(n log2n) 至 O(n2) | 不稳定 |
// Author: https://blog.csdn.net/DarkRabbit
// Bubble Sort
template<typename T>
void BubbleSort(std::vector<MyItem<T>>& list)
{
int size = list.size();
for (int k = 1; k < size; k++) // size-1趟冒泡
{
bool swaped = false;
for (int i = size - 1; i >= k; i--) //逆序
{
if (list[i - 1].key > list[i].key)
{
std::swap(list[i - 1], list[i]); // 交换
swaped = true;
}
}
if (!swaped) // 没有产生交换,证明表已经排序完成
{
break;
}
}
}
// Author: https://blog.csdn.net/DarkRabbit
// Quick Sort
template<typename T>
void QuickSort(std::vector<MyItem<T>>& list)
{
if (list.empty())
{
return;
}
std::stack<int> indexStack;
indexStack.push(list.size() - 1);
indexStack.push(0);
int keyIndex; // 每次查找的子表关键字
int begin; // 每次查找的子表开始下标
int end; // 每次查找的子表结束下标
int left; // 用于左下标移动
int right; // 用于右下标移动
while (!indexStack.empty())
{
begin = indexStack.top();
indexStack.pop();
end = indexStack.top();
indexStack.pop();
if (begin >= end) // 子表不可再分
{
continue;
}
left = begin;
right = end;
keyIndex = left; // 设置子表第一个元素下标为关键字下标
while (left < right)
{
// 逆向查找比key小的元素的下标
while (left < right && list[keyIndex].key < list[right].key)
{
right--;
}
if (left < right)
{
std::swap(list[keyIndex], list[right]);
keyIndex = right;
}
// 正向查找比key大的元素的下标
while (left < right && list[keyIndex].key > list[left].key)
{
left++;
}
if (left < right)
{
std::swap(list[left], list[keyIndex]);
keyIndex = left;
}
}
// 将此子表再分成两份
indexStack.push(end); // 右子表结束下标
indexStack.push(left + 1); // 右子表开始下标
indexStack.push(right - 1); // 左子表结束下标
indexStack.push(begin); // 左子表开始下标
}
}