快排
快排基础是在一个序列中选一个基准数x,把小于x的数放左边,其余放右边。然后分别对左右两边序列做该操作,直到序列是有序的(序列只有一个数时肯定有序)。
void quick_sort(int l,int r)
{
if (l >= r) return;
int i=l,j=r, key = a[l];
while (i < j)
{
while (i < j && a[j]>=key) j--;
a[i] = a[j];
while (i < j && a[i]<=key) i++;
a[j] = a[i];
}
a[i] = key;// 关键在于key的最后位置
quick_sort(l,i-1);
quick_sort(i+1,r);
}
而非递归快排就是模拟栈的过程
void w_qsort(int l, int r) {
stack<int> s;
s.push(l);
s.push(r);
while(!s.empty()) {
int L,R,i,j,key;
j = R = s.top();
s.pop();
i = L = s.top();
s.pop();
if (L >= R) continue;
key = a[L];
while (i < j) {
while (i<j && a[j] >= key) j--;
a[i] = a[j];
while (i<j && a[i] <= key) i++;
a[j] = a[i];
}
a[i] = key;
s.push(L);
s.push(i-1);
s.push(i+1);
s.push(R);
}
}
快排优化待续吧
归并排序
归并排序基础是把一个序列划分为有序的子序列,然后把相邻的子序列不断合并为新的有序序列。
void merge_sort(int left,int right)
{
if (left>=right) return;
int mid = (left+right)>>1;
merge_sort(left,mid);//mid,和mid-1的区别
merge_sort(mid+1,right);
int i=left,j=mid+1,k=left;
while (i<=mid || j<=right)//两个list并到一起i<=mid 对左边的list|| j<=right对右边的list
{
if (i<=mid)//对左边的list
{
if (j<=right)//对右边的list
{
if (a[i]<a[j])//把小的数放到前面存到tmp里
tmp[k++]=a[i++];
else tmp[k++]=a[j++];
}
else tmp[k++] = a[i++];//j指针已指到right,,,只需把i到mid之间的数放到tmp里
}
else tmp[k++] = a[j++];//i指针已经知道left,,只需把j到right的数放到tmp里
}
for (k=left; k<=right; k++)//把tmp赋回a
a[k]=tmp[k];
}
非递归归并排序是自底向上合并,省去分裂过程
// merge_sort(): 非递归实现-自底向上
// 将原数组划分为left[min...max] 和 right[min...max]两部分
//这里r-l为数组长,所以l=0,r=n
void wmerge_sort(int *a, int *b, int l, int r) {
int i,lmin,lmax,rmin,rmax,cnt;
for (i = 1; i<(r-l); i*=2) { // i为步长,1,2,4,8……
for (lmin = l; lmin <(r-l-i); lmin = rmax) {
rmin = lmax = lmin + i; //注意rmin,lmax边界
rmax = min(rmin + i, (r-l)); //由于lmin<(r-l-i), lmax不可能越界,但要注意rmax
cnt = 0;
while (lmin < lmax && rmin < rmax)
b[cnt++] = a[lmin] > a[rmin] ? a[rmin++] : a[lmin++]; //这里和递归写法有些区别
//当左区间有部分[k..lmax)不大于右区间时,这时lmin==lmax,rmin<rmax,
//因为此时[k+i..rmax)是最大有序的,没必要动
while (lmin < lmax) //而当左区间有部分[k..lmax)不小于右区间时,此时lmin<lmax,rmin==rmax
a[--rmin] = a[--lmax]; //直接把左区间的[k..lmax)放到右区间最后
while (cnt > 0) a[--rmin] = b[--cnt]; //最后把cnt放到[lmin,rmax)的前cnt个即可
}
}
}
堆排序
设定,对一个序列a[],对于第i个数有:2*i+1为左儿子,2*i+2为右儿子。这样就可以构建一个二叉树并且是一个满二叉树。对于大顶堆,每个节点的值都不小于左右儿子的值,基于此可构建一个大顶堆。首先从非叶子节点开始构建堆,之后交换堆顶堆尾,对除堆尾外的n-1个元素再次调整堆直到堆只剩下1元素,出来结果就是升序。小顶堆则相反
void adjust_heap(int *a, int k, int n) {
int tmp = a[k];
for (int i=((k<<1)+1); i<n; i=(i<<1)+1) {
if (i+1 < n && a[i+1] > a[i]) i++; //节点的值都不小于左右儿子的值
if (a[i] > tmp) {
a[k] = a[i];
k = i;
}
else break;
}
a[k] = tmp;
}
void head_sort(int *a, int n) {
for (int i=((n>>1)-1); i>=0; i--)
adjust_heap(a, i, n);
for (int i=(n-1); i>0; i--) {
int tmp = a[0];
a[0] = a[i];
a[i] = tmp;
adjust_heap(a, 0, i);
}
}