您的位置:首页 > 其它

排序算法-------交换排序

2018-03-12 16:36 323 查看

1. 冒泡排序

冒泡排序之前已经熟悉其过程,不在画图,直接上代码

void Bubble(DataType* a,size_t n)
{
assert(a != NULL && n > 0);
for(int end = n;end > 0;--end)
{
int flag = 0;
for(int i = 1;i < end;++i)
{
if(a[i-1] > a[i])
{
Swap(&a[i-1],&a[i]);
flag=1;
}
}
if(flag == 0)
{
break;
}
}
}


2.快速排序

三种方法

1.左右指针法

(1)从数组当中选取一个关键元素key;



(2)begin在数组最左边,从数组的左边走,直到找到比key大的数

(3)end在数组最右边,从右向左走,直到找到比key小的数

(4)交换begin和end所在位置的值



(5)当begin和end走到相同位置时,交换关键元素key和begin位置的元素



(6)此时一趟排序完成,以begin为分界线,划分为左区间和右区间,在进行左区间和右区间的排序

(7)当左区间右区间任意一个不存在或者只剩一个元素时,排序即可完成

代码:

//左右指针法
int PartSort1(DataType* a1, int left, int right)
{
assert(a);
DataType key = a[right];
int begin = left, end = right;

while (begin < end)
{
//选出比key大的数据
while (begin < end && a[begin] <= key)
++begin;

//选出比key小的数
while (begin < end && a[end] >= key)
--end;

//交换begin和end两个位置的值
Swap(&a[begin], &a[end]);
}

//注意这里是换a[right]和a[begin]的值
Swap(&a[begin], &a[right]);
return begin;
}

//快速排序
void QuickSort(DataType* a, int left, int right)
{
assert(a);

if (right - left > 10)
{
InsertSort(a, right - left + 1);
}
else
{
//划分区间返回条件,当左区间或右区间不存在,或只有一个数时,开始返回
if (left >= right)
return;
int div = PartSort1(a, left, right);

//去左区间排序
QuickSort(a, left, div - 1);
//去右区间排序
QuickSort(a, div + 1, right);

}
}


2.挖坑法

(1)从数组当中选取一个关键元素key,以key的位置作为第一个坑index



(2)begin在数组最左边,从数组的左边走,直到找到比key大的数,将index的元素付给begin,a[index]=a[begin],此时begin作为新的坑,index=begin



(3)end在数组最右边,从右向左走,直到找到比key小的数a[end],将end的元素给坑index,a[index]=a[end],此时end作为新的坑



(4)当begin和end走到相同位置,此时坑就在begin位置,将key填入坑



(5)此时一趟排序完成,以begin为分界线,划分为左区间和右区间,在进行左区间和右区间的排序

(6)当左区间右区间任意一个不存在或者只剩一个元素时,排序即可完成

代码:

//填坑法
int PartSort2(DataType* a1, int left, int right)
{
assert(a);
DataType key = a[right];

int begin = left, end = right;

while (begin < end)
{
//坑在end,找到比key大的值,将数据填入坑
while (begin < end && a[begin] <= key)
++begin;
a[end] = a[begin];

//此时坑在begin处,找到比key小的值,将数据填入坑
while (begin < end && a[end] >= key)
--end;
a[begin] = a[end];
}
//当begin和end在同一位置处,将key填在坑处
a[begin] = key;
return begin;
}

//快速排序
void QuickSort(DataType* a, int left, int right)
{
assert(a);

if (right - left > 10)
{
InsertSort(a, right - left + 1);
}
else
{
//划分区间返回条件,当左区间或右区间不存在,或只有一个数时,开始返回
if (left >= right)
return;
int div = PartSort2(a, left, right);

//去左区间排序
QuickSort(a, left, div - 1);
//去右区间排序
QuickSort(a, div + 1, right);

}
}


3.前后指针法

(1)选取一个关键元素可以,并定义两个指针,int cur=left;int prev=cur-1;



(2)a[cur] >= key,cur++



(3)a[cur] < key,cur停下来,prev++,如果prev != cur,则交换两位置的数值;





(4)当cur走到数组的最后,表示一趟已经结束,将cur的值和prev的后一位置进行交换



(5)以prev为界限,划分为左区间和右区间

(6)当左区间或右区间不存在,或只有一个数时,排序即完成

代码

//前后指针
int PartSort3(DataType* a1, int left, int right)
{
assert(a);
DataType key = a[right];
int cur = left;
int prev = left - 1;

while (cur < right)
{

//当a[cur]比key小,prev走,并且当prev不在同一位置上时,进行交换
if ( a[cur] < key)
{
++prev;
if (cur!=prev)
Swap(&a[cur], &a[prev]);
}

//不管比key小还是大,cur继续向后走
++cur;
}
//当cur走到数组的最后,表示一趟已经结束,将cur的值和prev的后一位置进行交换
Swap(&a[++prev], &a[right]);
return prev;//以prev为界限,划分为左区间和右区间
}

//快速排序
void QuickSort(DataType* a1, int left, int right)
{
assert(a);

if (right - left > 10)
{
InsertSort(a, right - left + 1);
}
else
{
//划分区间返回条件,当左区间或右区间不存在,或只有一个数时,开始返回
if (left >= right)
return;
int div = PartSort2(a, left, right);

//去左区间排序
QuickSort(a, left, div - 1);
//去右区间排序
QuickSort(a, div + 1, right);
}
}


快速排序的优化

1. 小区间优化

当区间中元素数量较小时在用递归反而会使效率降低,当区间元素数据较小,就越接近排序,此时,我们采用直接插入排序进一步提高效率

2. 随机值法

在选key时,在数组中随机选取一个数作为key进行排序;

代码:

sand(time(0));
int  index = rand()%(right - left + 1);//随机产生下标
int   key = a[index];


注意:这种方法并不实用,随机产生的key是大还是小并不确定,key选取的不合适反而会降低效率

3. 三数取中法

 选取数组的首元素a[0]、尾元素a[n-1]以及中间元素a[(0+n-1)/2]选出三者的中位数作为key值。这时候选出来的key可以保证绝对不是最大数或最小数(如果三个数都是相同的,那key还是最大或最小值,不过出现这种情况概率及其低)

int Getkey(DataType* a, int left, int right)
{

assert(a);
DataType mid = left + ((right - left) >> 1);

// mid恰好为key
//情况1:left <mid ,mid <right
//情况2:left>mid,mid > right
if ((a[left] < a[mid] && a[mid] < a[right])
|| (a[right] < a[mid] && a[mid] < a[left]))
{
return mid;
}

//left为key
//情况1:left > right,left < mid
//情况2:left >mid, left < right
else if ((a[right] < a[left] && a[left] < a[mid])
|| (a[mid] < a[left] && a[left] < a[right]))
{
return left;
}
else
{
//right为key
//情况1:right > left ,right < mid
//情况2:right < left,right >mid
return right;
}
}


快速排序的非递归

递归是依赖于函数栈帧,那么我们可以用我们的栈去模拟实现函数的递归调用

void QuickSortNonR(DataType* a, int left, int right)
{
assert(a);
Stack s;
StackInit(&s);

//先将左右区间进行入栈
StackPush(&s, left);
StackPush(&s, right);

while (left < right && StackEmpty(&s) != 0)
{
//从栈中取出区间坐标
int end = StackTop(&s);
StackPop(&s);
int begin = StackTop(&s);
StackPop(&s);

//计算下一次区间的临界值
int div = PartSort1(a, begin, end);

//如果区间内有两个数以上,进行入栈
if (begin < div-1 )
{
StackPush(&s, begin);
StackPush(&s, div-1 );
}

if (div+1  < end)
{
StackPush(&s, div+1 );
StackPush(&s, end);
}

}
}


两种算法分析

稳定性

冒泡排序:稳定算法,在单趟排序和多趟排序的过程中,元素的相对位置都不会发生变化

快速排序:不稳定算法,三种方法实现过程中都存在元素交换,相对位置会发生改变

时间复杂度

冒泡排序:O(n2n2)

快速排序:经优化过后为O(nlgn),最坏的情况为O(n2n2)

空间复杂度

冒泡排序:O(1)

快速排序:O(lgn)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  排序算法