[算法拾遗]系列文章主要用以归纳整理一些自己学过的主要算法。
本篇介绍几种主要的排序算法。
排序算法,顾名思义,就是用来对数组进行排序的算法,鉴于其在整个算法研究中的重要地位,很多介绍算法的书籍都是从排序算法开始,笔者也不能免俗。
1. 插入排序(Insert Sort)
这个算法的思路就跟我们玩扑克牌时整理手牌的方式差不多,一个数字一个数字读进来,把新读进来的数字按照其大小放在已读进来的数组中的合适位置。
这是一个对少量元素进行排序的有效算法,数据量较大时并不适用。
2. 归并排序(Merge Sort)
典型的“分而治之”的思想,通过不断的对数据拆半到单一元素后再将拆分后的数据按照大小进行归并整理来实现排序。运行速度比较快(O(nlogn)),但是不是原地排序,比较费内存。
3. 冒泡排序(Bubble Sort)这个算法,重复走访待排序的序列,每次只比较相邻的元素,让最小或者最大的元素逐步浮向序列的顶端,就跟气泡在水中不断往上冒出来一样。是原地排序,但是算法运行比较慢(O(n^2)),不适合大数据量。
4. 堆排序(Heapsort)这种排序算法利用了一种叫做堆数据结构,近似于完全二叉树。算法时间复杂度与归并排序一致(O(nlogn)),而且还是一种原地排序算法,运行不会太耗内存。
5. 臭皮匠排序(Stooge Sort)俗话说“三个臭皮匠,抵过一个诸葛亮”,这个算法就有这种味道,这是一种低效的排序算法,实践中不推荐使用。原理大概就是把数据分成三份,先看前两部分,把大的往后段挤,再对后两段执行同样的操作,最后再一次对前两段进行操作。
6. 快速排序(Quick Sort)
这种算法在实践中被广泛使用,同样采用“分而治之”的方法,不过是分的方法不同而已,就是每次取最后的元素作为分割点,将数据分为比之大和比之小两部分,再对各部分执行相同的操作,直至结束,是二叉查找树的空间优化版本。原地排序,最坏情况不理想(O(n^2)),但是期望速度快(O(nlogn)),实际运行过程常比归并排序和堆排序快。
其优化版本有随机快速排序,就是对上面介绍的分割点随机选取。
7. 计数排序(Counting Sort)
稳定排序算法,要用到额外的数组存取数据,运行速度非常快(O(n)),但是对数据类型有要求,为非负整数。
在这个算法基础上可以得到基数排序算法,这里不多做介绍。
下面是笔者自己写的一个类,包含了上面介绍的所有算法。
// Head file: libSort.h
// Edited by Sunshy.
// Home page: http://blog.csdn.net/longyindiyi
// 2014.03.16
// Introdution of LIBSORT
//
// Here, I edit a class called libSort, user can use the methods of this class to sort their own data.
// To define a libSort class, the typename of the data and its length must be given.
//
// i.e.
// If the data are in a array, A = [1.2, 1.6, 7.8, 9.0, 100, 0.78],
// then, you can write your code as below:
// libSort Sort;
// since A is a array including double numbers, and its length is 6, which can be represented by a unsigned int variable.
//
// The sort method contained in the library including:
//
// void insertSort(T *A, NUMT start, NUMT end);
// void mergeSort(T *A, NUMT start, NUMT end);
// void bubbleSort(T *A, NUMT start, NUMT end);
// void heapSort(T *A, NUMT start, NUMT end);
// void quickSort(T *A, NUMT start, NUMT end);
// void randomizedQuickSort(T *A, NUMT start, NUMT end);
// void stoogeSort(T *A, NUMT start, NUMT end);
// void countSort(T *A, NUMT k, NUMT start, NUMT end);
//
// the sorted data was save in your input array, namely, A.
// start and end represent the starting and ending index, defining the areas of A that you want to use a sorting operation.
// T and NUMT is the data typename given by you when you define a libSort class, whose meaning is introduced above.
// k in countSort is the maxmum value in your array A, to use this function, please make sure A contains usigned int number only.
//
// To print your data, using
// void printData(T *A, NUMT start, NUMT end);
#ifndef LIBSORT_EDITED_BY_SUN
#define LIBSORT_EDITED_BY_SUN
#include
#include
#include
#include
using namespace std;
#define PARENT(i) (i >> 1)
#define LEFT(i) (i << 1)
#define RIGHT(i) ((i << 1) + 1)
template
class libSort
{
private:
// Public internal function
// Exchage the vlaues of two elements
void libSort::exchange(T *a, T *b)
{
T tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
// Function used in merge sort
void libSort::merge(T *A, NUMT start, NUMT mid, NUMT end)
{
NUMT n1 = mid - start + 1;
NUMT n2 = end - mid;
T *L = new T[n1 + 1];
T *R = new T[n2 + 1];
for(NUMT i = 0; i < n1; i++)
L[i] = A[start + i];
for(NUMT j = 0; j < n2; j++)
R[j] = A[mid + j + 1];
L[n1] = (numeric_limits::max)();
R[n2] = (numeric_limits::max)();
NUMT i = 0;
NUMT j = 0;
for(NUMT k = start; k <= end; k++)
{
if(L[i] <= R[j])
{
A[k] = L[i];
i = i + 1;
}
else
{
A[k] = R[j];
j = j + 1;
}
}
delete []L;
delete []R;
}
// Functions uesd in heap sort
NUMT heapSize;
void libSort::maxHeapify(T *A, NUMT i, NUMT start, NUMT end)
{
NUMT largest;
i = i - start;
NUMT l = LEFT(i);
NUMT r = RIGHT(i);
if(l <= heapSize && A[l - 1 + start] > A[i - 1 + start])
{
largest = l;
}
else
{
largest = i;
}
if(r <= heapSize && A[r - 1 + start] > A[largest - 1 + start])
{
largest = r;
}
if(largest != i)
{
exchange(&A[i - 1 + start], &A[largest - 1 + start]);
maxHeapify(A, largest + start, start, end);
}
}
void libSort::buildMaxHeap(T *A, NUMT start, NUMT end)
{
heapSize = end - start + 1;
for(NUMT i = (end - start + 1)/2; i >= 1; i--)
{
maxHeapify(A, start + i, start, end);
}
}
// Function used in quickSort
NUMT libSort::partition(T *A, NUMT start, NUMT end)
{
T x = A[end];
NUMT i = start;
for(NUMT j = start; j < end; j++)
{
if(A[j] <= x)
{
exchange(&A[i], &A[j]);
i++;
}
}
exchange(&A[i], &A[end]);
return i;
}
// Functions used in randomized quickSort
T libSort::random(T start, T end)
{
return start+(end-start)*rand()/(RAND_MAX + 1.0);
}
NUMT libSort::randomizedPartition(T *A, NUMT start, NUMT end)
{
NUMT i = (NUMT)(random(start, end));
exchange(&A[end], &A[i]);
return partition(A, start, end);
}
public:
// Construct and deconstruct
libSort(){};
~libSort(){};
// User functions
/////////////////////////
// Insert sort //
/////////////////////////
// Time complexity O(n^2)
void libSort::insertSort(T *A, NUMT start, NUMT end)
{
if(start > end)
return;
NUMT n = end;
for(NUMT j = start + 1; j <= end; j++)
{
T key = A[j];
//Insert A[j] into the sorted sequence A[start + 1 .. j - 1]
NUMT i = j;
while(i > start && A[i - 1] > key)
{
A[i] = A[i - 1];
i--;
}
A[i] = key;
}
}
/////////////////////////
// Merge sort //
/////////////////////////
void libSort::mergeSort(T *A, NUMT start, NUMT end)
{
if(start < end)
{
int mid = (start + end) / 2;
mergeSort(A, start, mid);
mergeSort(A, mid + 1, end);
merge(A, start, mid, end);
}
}
///////////////////
// Bubble sort //
///////////////////
void libSort::bubbleSort(T *A, NUMT start, NUMT end)
{
if(start > end)
return;
for(NUMT i = start; i <= end; i++)
{
for(NUMT j = end; j >= i + 1; j--)
{
if(A[j] < A[j - 1])
{
exchange(&A[j], &A[j - 1]);
}
}
printData(A, 10);
}
}
//////////////////
// heap sort //
//////////////////
void libSort::heapSort(T *A, NUMT start, NUMT end)
{
if(start > end)
return;
buildMaxHeap(A, start, end);
for(NUMT i = end - start + 1; i > 1; i--)
{
exchange(&A[0 + start], &A[i - 1 + start]);
heapSize = heapSize - 1;
maxHeapify(A, 1 + start, start, end);
}
}
//////////////////
// quick sort //
//////////////////
void libSort::quickSort(T *A, NUMT start, NUMT end)
{
if(start < end)
{
NUMT key = partition(A, start, end);
if(key != 0)
quickSort(A, start, key - 1);
quickSort(A, key + 1, end);
}
}
/////////////////////////////
// randomized quick sort //
/////////////////////////////
void libSort::randomizedQuickSort(T *A, NUMT start, NUMT end)
{
if(start < end)
{
NUMT key = randomizedPartition(A, start, end);
if(key != 0)
randomizedQuickSort(A, start, key - 1);
randomizedQuickSort(A, key + 1, end);
}
}
/////////////////////
// stooge sort //
/////////////////////
void libSort::stoogeSort(T *A, NUMT start, NUMT end)
{
if(A[start] > A[end])
{
exchange(&A[start], &A[end]);
}
if(start + 1 >= end)
{
return;
}
NUMT key = (end - start + 1) / 3; // Round down
stoogeSort(A, start, end - key); // First two-third
stoogeSort(A, start + key, end); // Last two-third
stoogeSort(A, start, end - key); // First two-third again
}
//////////////////
// count sort //
//////////////////
void libSort::countSort(NUMT *A, NUMT k, NUMT start, NUMT end)
{
if(start >= end)
return;
NUMT *C = new NUMT[k + 1];
NUMT len = end - start + 1;
NUMT *B = new NUMT[len];
for(NUMT i = 0; i <= k; i++)
{
C[i] = 0;
}
for(NUMT i = 0; i < len; i++)
{
B[i] = 0;
}
for(NUMT j = start; j <= end; j++)
{
C[A[j]]++;
}
for(NUMT i = 1; i <= k; i++)
{
C[i] = C[i] + C[i - 1];
}
for(NUMT j = len; j >= 1; j--)
{
B[C[A[j - 1 + start]] - 1] = A[j - 1 + start];
C[A[j - 1 + start]]--;
}
for(NUMT i = 0; i <= end - start; i++)
{
A[start + i] = B[i];
}
delete []C;
delete []B;
}
//////////////////
// Print data //
//////////////////
void libSort::printData(const T *A, NUMT start, NUMT end)
{
if(start > end)
return;
for(NUMT i = start; i <= end; i++)
{
cout<
测试代码如下:
#include
#include "libSort.h"
using namespace std;
#define length(x) (sizeof(x)/sizeof(x[0]))
int main()
{
double A[] = {16, 14, 10, 8, 7, 9, 3, 2, 4, 1};
libSort B;
B.insertSort(A, 2, length(A) - 1);
B.printData(A, 0, length(A) - 1);
libSort E;
unsigned int D[] = {2, 3, 4, 1, 0, 1, 2, 3, 4};
unsigned int *C = new unsigned int[length(D)];
E.countSort(D, 4, 3, length(D) - 1);
E.printData(D, 0, length(D) - 1);
delete []C;
return 0;
}