排序算法复习(一)
排序算法是最基本的算法之一,尽管大家都很熟悉了,只是鉴于其无处不在的重要性,不妨大家一起来复习一下,到底有哪些排序算法。本人将描述自己已经了解的排序算法,并配上相应的C++代码。
没有特别说明,所有的排序结果都是非递减的。
冒泡排序(bubble sort)
对于数组a[N],冒泡过程是这样的:比较元素a[k]和a[k+1](k从0到N - 2),如果a[k] > a[k+1],进行交换,这样进行N - 1次冒泡之后,a[N-1]将是数组中最大的数。如果我们要让整个数组都变得有序,我还需要对数组中前N - 1个元素进行冒泡,每一次都排序好一个元素,一直重复下去,直到整个数组都排好。
下面是一段对应的C++代码。
插入排序(insertion sort)
将一个元素插入到一个有序的数组中,形成一个新的有序数组,称之为插入排序。对于数组a[N]来说,我们将第一个元素a[0]视为初始有序数组,然后将接下来的第一个元素a[1]插入到之前的有序数组中,直到最后一个元素a[N-1]处理完,最终形成整个有序数组。
下面是一段对应的C++代码。
归并排序(merge sort)
将两个有序数组合并为一个新的有序的数组,称之为归并排序。对于数组a[N],我们考虑将其均分为两个数组b和c,假设b和c已经是有序数组,此时我们可以运用归并排序将两个有序数组合并。至于b和c,我可以嵌套运用同样的方法将其置为有序。
待续。
没有特别说明,所有的排序结果都是非递减的。
冒泡排序(bubble sort)
对于数组a[N],冒泡过程是这样的:比较元素a[k]和a[k+1](k从0到N - 2),如果a[k] > a[k+1],进行交换,这样进行N - 1次冒泡之后,a[N-1]将是数组中最大的数。如果我们要让整个数组都变得有序,我还需要对数组中前N - 1个元素进行冒泡,每一次都排序好一个元素,一直重复下去,直到整个数组都排好。
下面是一段对应的C++代码。
// bubble-up sort algorithm
// O(T) = n*n
template<class T>
void busort(T* sz, int n)
{
for (int i = 0; i < n - 1; i++)
for (int j = 0; j < n - i - 1; j++)
{
if (compare(sz[j], sz[j + 1]) > 0)
swap(sz[j], sz[j + 1]);
}
}
冒泡算法应用最多的应该是在面试题中,实际工程中应用很少,原因是效率太低了。其时间度是n*n,准确的说是(n-2)*(n-1)/2次循环,也就是最少需要(n-2)*(n-1)/2次比较,最坏的情况是还要加上(n-2)*(n-1)/2次交换(一次交换包括三次赋值操作)。不过冒泡算法并不需要额外的空间开销。// O(T) = n*n
template<class T>
void busort(T* sz, int n)
{
for (int i = 0; i < n - 1; i++)
for (int j = 0; j < n - i - 1; j++)
{
if (compare(sz[j], sz[j + 1]) > 0)
swap(sz[j], sz[j + 1]);
}
}
插入排序(insertion sort)
将一个元素插入到一个有序的数组中,形成一个新的有序数组,称之为插入排序。对于数组a[N]来说,我们将第一个元素a[0]视为初始有序数组,然后将接下来的第一个元素a[1]插入到之前的有序数组中,直到最后一个元素a[N-1]处理完,最终形成整个有序数组。
下面是一段对应的C++代码。
// insertion sort algorithm
// O(T) = n*n
template<class T>
void insort(T* a, int n)
{
for (int i = 2; i < n; i++)
{
// save it
T temp = a[i];
// insert it
for (int j = i - 1; j > 0 && compare(temp, a[j]) < 0; j--)
a[j + 1] = a[j];
// get the position
a[j + 1] = temp;
}
}
虽然其时间度也是n*n,但是相对于冒泡法,插入排序最坏的情况下才需要(n-2)*(n-1)/2次循环,最好的情况是n-2次循环。而且每次比较之后的交换操作相对于冒泡法仅需要一次赋值。空间上也没有什么额外的开销。// O(T) = n*n
template<class T>
void insort(T* a, int n)
{
for (int i = 2; i < n; i++)
{
// save it
T temp = a[i];
// insert it
for (int j = i - 1; j > 0 && compare(temp, a[j]) < 0; j--)
a[j + 1] = a[j];
// get the position
a[j + 1] = temp;
}
}
归并排序(merge sort)
将两个有序数组合并为一个新的有序的数组,称之为归并排序。对于数组a[N],我们考虑将其均分为两个数组b和c,假设b和c已经是有序数组,此时我们可以运用归并排序将两个有序数组合并。至于b和c,我可以嵌套运用同样的方法将其置为有序。
// merge sort algorithm
// merge an unordered array
// O(T) - nlog(n)
template<class T>
void merge(T* a, int m, int l, int n);
template<class T>
void mergesort(T* a, int m, int n)
{
if (m == n)
return ;
// divide it to two halves
int l = (m + n) / 2;
// sort them
mergesort(a, m, l);
mergesort(a, l + 1, n);
// merge them
merge(a, m, l, n);
}
// merge two odered arrays
template<class T>
void merge(T* a, int m, int l, int n)
{
if (!(m <= l && l < n))
return ;
T* b = new T[n - m + 1];
int k = 0, i = m, j = l + 1;
while (i <= l && j <= n)
{
if (compare(a[i], a[j]) <= 0)
b[k++] = a[i++];
else
b[k++] = a[j++];
}
while (i <= l)
b[k++] = a[i++];
while (j <= n)
b[k++] = a[j++];
for (int k = 0; k < n - m + 1; k++)
a[m + k] = b[k];
delete[] b;
}
其时间度是n*log(n),其时间虽然比上述排序办法快,但是在空间上要额外用到同样大小的数组作为临时空间,而且,不管是最好还是最坏情况,赋值操作的数量不会改变。// merge an unordered array
// O(T) - nlog(n)
template<class T>
void merge(T* a, int m, int l, int n);
template<class T>
void mergesort(T* a, int m, int n)
{
if (m == n)
return ;
// divide it to two halves
int l = (m + n) / 2;
// sort them
mergesort(a, m, l);
mergesort(a, l + 1, n);
// merge them
merge(a, m, l, n);
}
// merge two odered arrays
template<class T>
void merge(T* a, int m, int l, int n)
{
if (!(m <= l && l < n))
return ;
T* b = new T[n - m + 1];
int k = 0, i = m, j = l + 1;
while (i <= l && j <= n)
{
if (compare(a[i], a[j]) <= 0)
b[k++] = a[i++];
else
b[k++] = a[j++];
}
while (i <= l)
b[k++] = a[i++];
while (j <= n)
b[k++] = a[j++];
for (int k = 0; k < n - m + 1; k++)
a[m + k] = b[k];
delete[] b;
}
待续。