基础排序
在排序算法中,我们需要计算比较和交换的数量。对于不交换元素的算法,我们会计算访问数组的次数。在排序算法中额外的内存开销和运行时间是同等重要的。
排序算法分为两类:1、除了函数调用所需要的栈和固定数目的实例变量之外无需额外内存的原地排序算法。
2、需要额外内存空间来存储另一份数组副本的其他排序算法。
一、选择排序
过程:找到数组中最小的数,和首位元素交换(如果首元素最小就和本身交换),然后依次找最小的元素交换。
特点:运行时间和输入无关。数据移动是最少的。
代码:
template<typename Item>
void eaxh(Item &a, Item &b)//交换代码
{
Item t = a;
a = b;
b = t;
}
template <typename Item>
void selectsort(vector<Item> &a)//选择排序
{
for (int i = 0; i < a.size(); i++)
{
int min = i;
for (int j = i + 1; j < a.size(); j++)
if (a[j] < a[min]) min = j;
eaxh(a[i], a[min]);
}
}
二、插入排序
插入排序所需要的时间取决于输入中元素的初始顺序。对一个很大且其中的元素已经有序(或接近有序)的数组进行排序将会比随机排序的数组或是逆序数组进行排序要快得多。
部分有序的数组:
1.数组中每个元素距离它的最终位置都不远;
2.一个有序的大数组接一个小数组;
3.数组中只有几个元素的位置不正确。
总的来说,插入排序对于部分有序的数组十分高效,也很适合小规模数组。
代码:
template<typename Item>
void insertsort(vector<Item> &a)//不带哨兵
{
int N = a.size();
for (int i = 1; i < N; i++)
for (int j = i; j > 0 && a[j] < a[j - 1]; j--)
eaxh(a[j], a[j - 1]);
}
template <typename Item>
void insertsort_1(vector<Item> &a)//a[0]为哨兵
{
Item min = 0;
int N = a.size();
for (int i = 1; i < N; ++i)//寻找最小值,做为哨兵
if (a[i] < a[min])min = i;
eaxh(a[0], a[min]);
for (int i = 1; i < N; ++i)
for (int j = i; a[j] < a[j - 1]; --j)
eaxh(a[j], a[j - 1]);
}
template <typename Item>
void insertsort_2(vector<Item> &a)//哨兵为a[n]
{
a.push_back(INT_MAX);
int n = a.size();
for (int i = n - 2; i >= 0; --i)
for (int j = i; a[j] > a[j + 1]; ++j)
eaxh(a[j], a[j + 1]);
a.pop_back();
}
template<typename Item>
void insertsort_3(vector<Item> &a)//数组移位方式插入
{
int N = a.size();
for (int i = 1; i < N; ++i)
{
Item temp = a[i];
int j;
for (j = i; j > 0 && temp < a[j - 1]; --j)
a[j] = a[j - 1];
a[j] = temp;
}
}
template<typename Item>
void binaryinsertsort(vector<Item> &a)//折半插入排序
{//不停加入新元素,以折半查找的方式找到插入点,然后插入
int N = a.size();
for (int i = 1; i < N; ++i)
{
if (a[i] < a[i - 1])
{
Item temp = a[i];//哨兵
int low = 0, high = i - 1;
while (low <= high)//折半
{
int mid = (low + high) / 2;
if (a[mid] < temp)low = mid + 1;
else high = mid - 1;
}
for (int j = i; j > low; --j)//移位插入
a[j] = a[j - 1];
a[low] = temp;
}
}
}
三、冒泡排序
冒泡排序与插入排序及其相似,都是以交换为主的排序方式,不同点在于,插入排序是不停的新添加元素进行交换,而冒泡则是,一遍一遍的交换数组,使得元素“上沉下浮”。
代码:
template <typename Item>
void bubblesort(vector<Item> &a)
{
int N = a.size();
for (int i = 0; i < N - 1; ++i)
{
bool FF = false;//标志变量
for (int j = N - 1; j > i; --j)
if (a[j - 1] > a[j])
{
eaxh(a[j - 1], a[j]);
FF = true;
}
if (FF == false)//没有交换,即排序完成
return;
}
}
测试用例:
测试之后可以看出三种排序相差不大,其中插入排序稍微占优,又因为三种排序的空间需求都是1,所以在基础排序中选择插入为最佳。
int main()
{
uniform_int_distribution<int> u(0, 100000);//随机用例
default_random_engine e;
vector<int> a;
for (int i = 0; i < 10000; ++i)
a.push_back(u(e));
bubblesort(a);//被测试算法
//for (auto c : a)//遍历结果
// cout << c << " ";
//cout << endl;
cout << (double)clock() / CLOCKS_PER_SEC << endl;//记录时间
cout << endl;
system("pause");
return 0;
}
算法|量级 | 10 | 100 | 1000 | 10000 |
选择排序 | 0.105 | 0.107 | 0.177 | 6.374 |
插入排序 | 0.106 | 0.106 | 0.174 | 5.792 |
移位插入排序 | 0.104 | 0.105 | 0.156 | 3.889 |
折半插入排序 | 0.106 | 0.104 | 0.138 | 2.636 |
冒泡排序 | 0.105 | 0.106 | 0.201 | 8.461 |
测试之后可以看出三种排序相差不大,其中插入排序稍微占优,又因为三种排序的空间需求都是1,所以在基础排序中选择插入为最佳。
而优化后的两种排序提升更是及为明显,折半的效果更好,当然代码量也更高。