1.归并排序
归并排序用的是分治的思想,1个元素一定是有序的,2个元素分别对1个元素使用Merge()将其变成有序序列。4个元素可以分别对2个有序的元素使用Merge()将其变成有序的序列…
首先是重要的Merge()的实现,改函数完成的是[i……m]和[m+1……n]区间的归并(注意是闭区间,因此判断的时候都有等号的):
void Merge(int*r,int* rf,int i,int m,int n)
{
if(r == NULL || rf == NULL || i > n) return;
int pos = i;//记录本次归并的起点
int k=i;
int j = m+1;
while(i <= m && j <= n)
{
if(r[i] < r[j]) rf[k++] = r[i++];
else rf[k++] = r[j++];
}
while(i <= m) rf[k++] = r[i++];
while(j <= n) rf[k++] = r[j++];
//上面完成的操就就是把r数组的[i……m]和[m+1……n]区间的元素归并到rf中。
for(;pos<=n;++pos)//这个赋值很重要,将归并好的元素重新赋值给r,
//为下次归并做准备(最开始就是因为没有考虑到这一点,导致算法错误)
r[pos] = rf[pos];
}
下面是归并排序算法的非递归版本:
void MergeSort(int*r,int*rf,int length)
{
//非递归版本就是按照正常的思路来归并
//1.把两个元素归并;
//2.根据前一步把四个元素归并;
//3....直至全部数据归并
//注意要考虑不能序列尾部多出的元素。
if(r==NULL || rf == NULL || length < 0 ) return;
int len = 1;//元素归并的步长,一个元素是有序
while( len < length)
{
int s= len;
len = s*2;//每次二倍的增长,这是算法时间复杂度中log2n的来源
int i = 0;//每次归并处理从序列的起始位置开始
while(i+len <= length)
{
Merge(r,rf,i,i+s-1,i+len-1);//将序列组归并
i = i+len;//在序列上移动归并窗口
}
if(i+s <= length)//处理尾部多出来的元素,因为分组是在前次的基础上完成
//的(2倍的关系)因此多出来的元素一定不小于i+s个。
Merge(r,rf,i,i+s-1,length-1);
}
//由于在Merge中做了r和rf的交换操作,因此这里无需再有任何交换操作,
//最后有序的数据会保存在r中。
}
下面是归并排序的递归实现:
void MergeSortRec(int* r,int* rf,int start,int length)
{
//递归实现的思路是,现将序列2分,接着在2分,。。。直至划分好的序列只有一个元素
//(一个元素是有序的),在调用Merge函数,归并。(有点类似于二叉树的后续便利)
if(r==NULL || rf == NULL || length < 0 ) return;
if(start < length)
{
int m = (start+length)/2;
MergeSortRec(r,rf,start,m);//序列前半段二分
MergeSortRec(r,rf,m+1,length);//序列后半段二分
Merge(r,rf,start,m,length);//归并分段序列
}
}
算法思路代码注释说的很清楚了,下面看看算法的特性:
1.算法是稳定的,因为在归并函数Merge中是按照元素的位置比较的,因此在相等元素的相对位置不会被改变。
2.时间复杂度为O(nlog2n),从非递归的实现版本可以简单推算时间算法的时间复杂度,内层循环不断的/2因此为log2n,完成循环当然是n啦。大概能够看出算法的时间富复杂度是O(nlog2n)。而且这种复杂度也是稳定的,不管序列是否有序都需要完成算法的所有操作。
3.空间复杂度,这个比较明细,在归并函数中需要使用与序列一样大的空间来保存归并结果,因此空间复杂度自然为O(n)了。
2.基数排序
基数排序是一种基于桶排序变种,感觉思路清晰了,实现起来不是很难,先看代码:
//链式基数排序的实现
//链式结构体
struct node
{
int data;
node* next;
};//存储带排元素结构体
node* f[10];
node* e[10];
int keyNum = 3;//最长关键字的长度
int getKeyPosNum(int num,int keyPos)
{
//获得关键字每个位上的数字
if(keyPos == 0) return -1;
if(keyPos == 1) return num%10;
if(keyPos == keyNum)
{
int n = 1;
for(int i=1;i<keyNum;++i)
n *= 10;
return num/n;
}
for(int i=1;i<keyPos;++i)
num /= 10;
return num%10;
}
void Distribute(node* l,int keyPos)
{
//分配
if(l == NULL || keyPos < 0 || keyPos > keyNum) return;
node* p = l->next;
while(p)
{
int key = getKeyPosNum(p->data,keyPos);
node* q = p->next;
e[key]->next->next = p;
e[key]->next = p;
p->next = NULL;
p = q;
}
}
void Collect(node* l)
{
//收集
if(l == NULL) return;
node *p = l;
for(int i=0;i<10;++i)
{
node* q = f[i]->next;
while(q)
{
p->next = q;
p = q;
q = q->next;
}
e[i]->next = f[i];
f[i]->next = NULL;
}
}
void RadixSort(node* l)
{
if(l == NULL)
return;
for(int i=1;i<=keyNum;++i)
{
Distribute(l,i);
Collect(l);
}
}
int main()
{
cout<<"输入排序元素:";
int data;
node* header = new node();
header->data = -1;
header->next = NULL;
node* p = header;
while(cin >> data)//建立链表
{
node* q = new node();
q->data = data;
q->next = NULL;
p->next = q;
p = q;
}
//初始化f和e链表
for(int i=0;i<10;++i)
{
f[i] = new node();
e[i] = new node();
f[i]->next = NULL;
e[i]->next = f[i];
}
//排序
RadixSort(header);
//输出排序结果
p = header->next;
while(p)
{
cout<<p->data<<' ';
p = p->next;
}
cout<<endl;
}
上诉算法是按照严蔚敏《数据结构》书上,链式基数排序的思路实现的。
具体过程是一种最低位优先(LSD)方法:
1.先按低位关键字,分配,在排序收集。
2.接着按照关键字的优先级不断的分配收集实现最终的排序。
算法特性:
1.基数排序是稳定的,在分配收集的过程中不影响相同元素的相对位置。
2.时间复杂度O(d(n+rd)),d是关键字的个数,rd关键字的取值范围。
3.空间复杂度O(rd)。