排序算法很多,这里做个总结,最重要的是记住算法思想,而不是记住程序怎么写。程序过一段时间就会忘了,算法原理熟悉了现场写出来应该是很容易的。
时间复杂度为O(n^2)的三种排序算法:插入排序、选择排序、冒泡排序。
插入排序:插入排序是选定元素找位置。对于位置为p上的元素,假定0~p-1的元素已经排序,如果p比它前面的元素小,那么p上的元素应该向前移动,直到p前面所有的元素都比它小为止。
void InsertSort(int* a, int length)
{
if(a == NULL || length < 1)
return;
for(int i = 1; i < length; ++i)
{
int tmp = a[i];
int j;
for(j = i; a[j-1] > tmp && j > 0; --j)
a[j] = a[j-1];
a[j] = tmp;
}
}
选择排序:选择排序是选定位置找元素。第一个位置上应该是最小的元素,那么先把最小的元素找出来放在第一个位置上。然后再考虑第二个位置,它应该放上第二小的元素,所以遍历剩下的元素找出最小的放在第二个位置上,直到所有位置都放上了合适的元素。
void SelectSort(int* a, int length)
{
if(a == NULL || length < 1)
return;
for(int i = 0; i < length; ++i)
{
int min = a[i];
int indexOfMin = i;
for(int j = i + 1; j < length; ++j)
{
if(a[j] < min)
{
min = a[j];
indexOfMin = j;
}
}
a[indexOfMin] = a[i];
a[i] = min;
}
}
冒泡排序:冒泡排序也是选定位置找元素。冒泡排序的实现过程是两两交换,每次从最右边开始的元素开始比较,如果右边的元素比它左边一位的元素小,那么交换两个元素。例如3,2,1交换一次变成3,1,2,再交换一次变成1,3,2。这一过程总是使最小的元素移到最左边,最左边定义为0的时候最小的元素交换到位置0上,最左边定义为1时剩下的元素中最小的元素(也就是第二小)交换到位置1上,以此类推。
冒泡排序和选择排序过程很相似,但是冒泡排序的交换过程具有盲目性。冒泡排序只要右边元素比左边小都会发生交换,而选择排序只有当前元素小于最小值时记录最小值得位置(实际上等同于交换),内层循环结束后才发生一次交换。选择排序的交换次数比冒泡排序小,性能略优于冒泡排序。
void BubbleSort(int* a, int length)
{
if(a == NULL || length < 1)
return;
for(int i = 0; i < length; ++i)
{
for(int j = length - 1; j > i; --j)
{
if(a[j] < a[j-1])
{
int tmp = a[j];
a[j] = a[j-1];
a[j-1] = tmp;
}
}
}
}
时间复杂度为O(nlogn)的四种排序算法:希尔排序、堆排序、归并排序、快速排序。
希尔排序:希尔排序只需要记住一条:它是变间隔的插入排序!插入排序那里比较的元素都是相邻的两个元素,间隔为1。希尔排序通过设置比较间隔,然后间隔逐渐缩小直到为1,所以希尔排序的最后一趟,也就是间隔为1的时候完全等同于插入排序了。间隔序列h1,h2,h3…hn叫做增量序列,增量序列的选取也会对排序算法的性能有影响,由于希尔排序很少使用就不讨论了。
void ShellSort(int* a, int length)
{
if(a == NULL || length < 1)
return;
for(int increment = length / 2; increment > 0; increment /= 2) //希尔建议的增量序列,但是并不好
{
for(int i = increment; i < length; ++i) //<span style="color:#ff0000;">注意这里i的增量是1,而不是increment</span>
{
int tmp = a[i];
int j;
for(j = i; j >= increment && a[j-increment] > tmp; j -= increment)
a[j] = a[j-increment];
a[j] = tmp;
}
}
}
堆排序:堆排序根据最大堆的堆顶总是堆上最大数的性质,每次交换堆顶和堆最后一个元素,这样堆顶元素放在了正确的位置,而由于堆顶更换了新的元素,需要下滤维护堆序。下滤完成以后可以看成比第一部大小减一的新的堆,继续交换堆顶和最后一个元素,这样第二大的数也摆在正确的位置上,重复这个过程直到堆的大小为0,即所有元素都已经摆放到合适的位置上。
#define LeftChild(i) 2*i+1
void Swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
//最大堆下滤过程
void PercDown(int* a, int n, int i)
{
int tmp, child;
for(tmp = a[i]; LeftChild(i) < n; i = child)
{
child = LeftChild(i);
if(child != n-1 && a[child] < a[child+1]) //最小堆的时候改为“>"
++child;
if(tmp < a[child]) //最小堆的时候改为“>"
a[i] = a[child];
else
break;
}
a[i] = tmp;
}
void HeapSort(int* a, int n)
{
//建堆过程
for(int i = n/2 - 1; i >= 0; --i)
PercDown(a,n,i);
for(int i = n-1; i > 0; --i)
{
Swap(a[0], a[i]); //DeleteMax
PercDown(a, i, 0);
}
}
归并排序:归并排序假设存在两个已经排序的子数组A和B,申请一个额外的数组C,大小为n,每次从数组中A或B中取较小的数放入C中,直到其中一个子数组取完,另外一个子数组剩下的元素直接拷贝到数组C中。对于要排序的数组array,可以将array划分成两半,调用上述合并的过程,而对于各自部分的排序,递归的调用合并排序的函数。归并排序需要额外的大小为n的辅助空间,时间复杂度为O(nlogn)。
void Merge(int* array, int* TmpArray, int Lpos, int Rpos, int RposEnd)
{
int IndexL, IndexR, index, numOfElement;
IndexL = index = Lpos;
IndexR = Rpos;
numOfElement = RposEnd - Lpos + 1;
while(IndexL < Rpos && IndexR <= RposEnd)
{
if(array[IndexL] < array[IndexR])
TmpArray[index++] = array[IndexL++];
else
TmpArray[index++] = array[IndexR++];
}
while(IndexL < Rpos)
TmpArray[index++] = array[IndexL++];
while(IndexR <= RposEnd)
TmpArray[index++] = array[IndexR++];
for(int i = 0; i < numOfElement; ++i)
array[i+Lpos] = TmpArray[i+Lpos];
}
void MergeSortCore(int* array, int* TmpArray, int start, int end)
{
if(start < end)
{
int center = (start + end) / 2;
MergeSortCore(array, TmpArray, start, center);
MergeSortCore(array, TmpArray, center+1, end);
Merge(array, TmpArray, start, center+1, end);
}
}
void MergeSort(int* array, int length)
{
if(array == NULL || length <= 1)
return;
int* TmpArray = (int*) malloc(length*sizeof(int));
if(TmpArray != NULL)
{
MergeSortCore(array, TmpArray, 0, length-1);
free(TmpArray);
}
else
{
cout << "Out of memory!" << endl;
return;
}
}
快速排序:快速排序是实践中已知的最快的排序算法。快速排序的过程是一个分组的过程,选定基准值,从数组的前后两头同时开始遍历数组,如果左边的数小于基准值,滑过指针,如果右边的值大于或等于基准值,也滑过指针。当左边的值大于基准值而右边的值小于基准值时,交换两个数,直到两个指针交叉。这一过程使得数组中小于基准值的数都位于基准值的左边,大于基准值的数都位于基准值右边,基准值的位置已经确定不再改变,在分别排序基准值的左边和右边。
void Swap(int& p1, int& p2)
{
int temp = p1;
p1 = p2;
p2 = temp;
}
void Qsort(int* array, int start, int end)
{
if(start < end)
{
int pivot = array[start];
int pLeft = start + 1;
int pRight = end;
while(pLeft <= pRight)
{
while(pLeft <= pRight && array[pRight] >= pivot)
--pRight;
while(pLeft <= pRight && array[pLeft] < pivot)
++pLeft;
if(pLeft < pRight)
Swap(array[pRight], array[pLeft]);
}
Swap(array[start], array[pRight]);
Qsort(array, start, pRight - 1);
Qsort(array, pRight + 1, end);
}
}
void QuickSort(int* array, int length)
{
if(array == NULL || length <= 0)
return;
Qsort(array, 0, length - 1);
}
排序算法性能分析:http://blog.csdn.net/cjf_iceking/article/details/7953637