每次找工作都感觉要复习下这些东西,常常想,这些神奇的玩意怎么就被想出来的!
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1,常见排序算法:
排序的分类就不写了,各种分类,其实常用的就那么几种。
1.1 插入排序:
void insert_sort(int *a, int len) {
int tmp;
for (int i = 1; i < len; ++i) {
for (int j = i; j > 0; --j) {
if (a[j] < a[j-1]) {
tmp = a[j];
a[j] = a[j-1];
a[j-1] = tmp;
}
else {
break;
}
}
}
}
平均和最差时间代价均为θ(n²)
1.2 冒泡排序:
void bubblesort(int *a, int len) {
int tmp,j,k;
k = len;
int flag = true;
while (flag) {
flag = false;
for (j = 1; j < k; ++j) {
if (a[j-1] > a[j]) {
tmp = a[j-1];
a[j-1] = a[j];
a[j] = tmp;
flag = true;
}
}
--k;
}
}
该改进版来自:
点击打开链接
平均和最差时间代价均为θ(n²)
1.3 选择排序;
void selectsort(int *a, int len) {
int tmp,index;
for (int i = 0; i < len; ++i) {
index = i;
for (int j = i+1; j < len; ++j) {
if (a[j] < a[index]) {
index = j;
}
}
if (index != i) {
tmp = a[i];
a[i] = a[index];
a[index] = tmp;
}
}
}
平均和最差时间代价均为θ(n²)
备注:选择排序 是对 冒泡的改进,它使用了一个索引记录比较的结果,在一论遍历结束时才执行一次交换。
所以,对于交换代价比较大的问题,效果很明显。
1.4 shell排序
void insertsort_ex(int *a, int len, int incr) {
int tmp;
for (int i = incr; i < len; i += incr) {
for (int j = i; j >= incr; j -= incr) {
if (a[j] < a[j-incr]) {
tmp = a[j];
a[j] = a[j-incr];
a[j-incr] = tmp;
}
else {
break;
}
}
}
}
void shellsort(int *a, int len) {
for (int i = len/2; i > 2; i /= 2) {
for (int j = 0; j < i; j++) {
insertsort_ex(&a[j],len-j,i);
}
}
insertsort_ex(a,len,1);
}
shell排序利用了插入排序的最佳性能,首先将序列变成基本有序,最后再用插入排序。
shell排序 增量的选择很重要,参考点击打开链接
1.5 快速排序
int partition(int *a, int l, int r) {
int low = l;
int hi = r;
int pivot = a[low];
for (;low < hi;) {
while(low < hi && a[hi] >= pivot)
--hi;
a[low] = a[hi];
while(low < hi && a[low] <= pivot)
++low;
a[hi] = a[low];
}
a[low] = pivot;
return low;
}
void qsort(int *a, int low, int high) {
if (low < high) {
int p = partition(a,low,high);
qsort(a,low,p-1);
qsort(a,p+1,high);
}
}
最差 O(n²),平均:θ(nlogn)
ps:可以改成尾递归形式。
1.6 堆排序
void HeapAdjust(int *a,int i,int size) //调整堆
{
int lchild=2*i+1; //i的左孩子节点序号
int rchild=2*i+2; //i的右孩子节点序号
int max=i; //临时变量
if(i<=(size-1)/2) //如果i是叶节点就不用进行调整
{
if(lchild<size&&a[lchild]>a[max])
{
max=lchild;
}
if(rchild<size&&a[rchild]>a[max])
{
max=rchild;
}
if(max!=i)
{
swap(a[i],a[max]);
HeapAdjust(a,max,size); //避免调整之后以max为父节点的子树不是堆
}
}
}
void BuildHeap(int *a,int size) //建立堆
{
int i;
for(i=(size-1)/2;i>=0;i--) //非叶节点最大序号值为size/2
{
HeapAdjust(a,i,size);
}
}
void HeapSort(int *a,int size) //堆排序
{
int i;
BuildHeap(a,size);
for(i=size-1;i>=1;i--)
{
//cout<<a[1]<<" ";
swap(a[0],a[i]); //交换堆顶和最后一个元素,即每次将剩余元素中的最大者放到最后面
//BuildHeap(a,i-1); //将余下元素重新建立为大顶堆
HeapAdjust(a,0,i); //重新调整堆顶节点成为大顶堆
}
}
上述代码参考http://www.cnblogs.com/dolphin0520/archive/2011/10/06/2199741.html
不过我做了两处修改:修改为最大堆,升序输出;
修改根节点和孩子节点的对应关系,这样根节点保存在数组的第一个位置。
最佳、平均、最差时间代价都为O(n*logn);
引用:“堆排序其实也是一种选择排序,是一种树形选择排序。只不过直接选择排序中,为了从R[1...n]中选择最大记录,需比较n-1次,然后从R[1...n-2]中选择最大记录需比较n-2次。事实上这n-2次比较中有很多已经在前面的n-1次比较中已经做过,而树形选择排序恰好利用树形的特点保存了部分前面的比较结果,因此可以减少比较次数。对于n个关键字序列,最坏情况下每个节点需比较log2(n)次,因此其最坏情况下时间复杂度为nlogn。堆排序为不稳定排序,不适合记录较少的排序”
1.7 归并排序
//将有二个有序数列a[first...mid]和a[mid...last]合并。
void mergearray(int a[], int first, int mid, int last, int temp[])
{
int i = first, j = mid + 1;
int m = mid, n = last;
int k = 0;
while (i <= m && j <= n)
{
if (a[i] <= a[j])
temp[k++] = a[i++];
else
temp[k++] = a[j++];
}
while (i <= m)
temp[k++] = a[i++];
while (j <= n)
temp[k++] = a[j++];
for (i = 0; i < k; i++)
a[first + i] = temp[i];
}
void mergesort(int a[], int first, int last, int temp[])
{
if (first < last)
{
int mid = first + (last - first) / 2;
mergesort(a, first, mid, temp); //左边有序
mergesort(a, mid + 1, last, temp); //右边有序
mergearray(a, first, mid, last, temp); //再将二个有序数列合并
}
}
bool MergeSort(int a[], int n)
{
int *p = new int[n];
if (p == NULL)
return false;
mergesort(a, 0, n - 1, p);
delete[] p;
return true;
}
归并排序最佳、平均、最差时间代价都为O(n*logn),该排序不太适合内存排序,因为不仅要开辟额外的线性内存,而且还要来回拷贝数据,想想还是算了吧。。。。
但是,归并排序是稳定的哦。
但是,归并排序中的merge是很多外部排序的基础哦。