交换排序
1.交换排序的思想
交换排序的思想为两两比较关键字的大小,若关键字次序相反,则这两个元素就进行交换,直到数组中没有出现反序的元素为止。
在这里主要介绍两种算法:
- 冒泡排序
- 快速排序
2.冒泡排序
冒泡排序的思想为在无序区中相邻两个元素比较,不断调整元素的位置,使关键字最小的元素像气泡一样冒出水面。
例:每经历一轮有效排序,都有一个元素加入有序区(冒泡到数组的一端)
数组下标 | 原始 | 第一轮 | 第二轮 | 第三轮 | 第四轮 |
---|---|---|---|---|---|
0 | 4 | 0 | 0 | 0 | 0 |
1 | 2 | 4 | 1 | 1 | 1 |
2 | 3 | 2 | 4 | 2 | 2 |
3 | 1 | 3 | 2 | 4 | 3 |
4 | 0 | 1 | 3 | 3 | 4 |
冒泡排序是相邻元素的比较,是一个稳定的排序算法。
代码:
/*冒泡排序 o(n^2) 稳定*/
void BubbleSort(Elem elem[], int len)
{
bool flag;
for (int i = 0; i < len - 1; i++)
{
flag = false;
for (int j = len - 1 ; j > i; j--)
{
if (elem[j].key < elem[j-1].key)
{
swap(elem[j - 1], elem[j]);
flag = true;
}
}
if (!flag)
break;
}
}
3.快速排序法
快速排序法是冒泡算法的改进,也属于交换排序。
区别:
- 冒泡排序每经历一次排序,就有一个元素冒泡到数组的一端
- 快速排序是每一次排序都按照一个基准值将数组分为两个部分,其中一部分的值都比基准值大,另外一部分都比基准值小
快速排序法的思想是分治(分而治之),按照以上的方法,每次都将数组划分为两部分的方法进行排序。
如何实现以上说的按照一个基准值划分成两部分?
有两种方法:双边循环法和单边循环法
双边循环法
①假设以数组的第一个元素为基准值,使用两个指针left和right分别指向数组的左、右端:
5 | 3 | 2 | 6 | 2 | 7 |
---|---|---|---|---|---|
pivot,start,left | right,end |
②然后right指针从右向左找到第一个比基准值小的元素,找到后停下来:
5 | 3 | 2 | 6 | 2 | 7 |
---|---|---|---|---|---|
pivot,start,left | right | end |
③left指针从左向右找到第一个比基准值大的元素:
5 | 3 | 2 | 6 | 2 | 7 |
---|---|---|---|---|---|
pivot,start | left | right | end |
④交换left,right指针指向的值
5 | 3 | 2 | 2 | 6 | 7 |
---|---|---|---|---|---|
pivot,start | left | right | end |
⑤重复步骤②、③、④直到左右指针相同
5 | 3 | 2 | 2 | 6 | 7 |
---|---|---|---|---|---|
pivot,start | left,right | end |
⑥交换基准值与left指向的值
2 | 3 | 2 | 5 | 6 | 7 |
---|---|---|---|---|---|
pivot,start | left,right | end |
可以看到此时基准值5的左边都比5小,右边都比5大
单边循环法这里暂时不介绍
参考代码:
/*以elem[start]为基准值,将数组划分为左右两部分,左边都比基准值小,右边都比基准值大,并返回基准值的位置*/
int partition(Elem elem[],int start,int end)
{
int left = start; //左指针,从左向右
int right = end; //右指针,从右向左
Elem pivot = elem[start];
while (left != right)
{
//指针从右向左扫描,找到第一个比基准值小的
while (left < right && elem[right].key >= pivot.key)
{
right--;
}
//指针从左向右扫描,找到第一个比基准值大的
while (left < right && elem[left].key <= pivot.key)
{
left++;
}
//交换左右指针
if (left < right)
{
swap(elem[left], elem[right]);
}
}
//基准值与循环截止位置的值交换
elem[start] = elem[left];
elem[left] = pivot;
return left;
}
/*快速排序 o(nlog2(n)) 不稳定*/
void QuickSort(Elem elem[], int start, int end)
{
if (start < end)
{
int pivot = partition(elem, start, end); //确定基准值pivot
QuickSort(elem, start, pivot - 1); //从start,pivot-1再次进行快速排序
QuickSort(elem, pivot + 1, end); //从pivot+1,end再次进行快速排序
}
}
优化交换次数版本:
/*以elem[start]为基准值,将数组划分为左右两部分,左边都比基准值小,右边都比基准值大,并返回基准值的位置*/
int partition(Elem elem[],int start,int end)
{
int left = start; //左指针,从左向右
int right = end; //右指针,从右向左
Elem pivot = elem[start];
while (left != right)
{
//指针从右向左扫描,找到第一个比基准值小的
while (left < right && elem[right].key >= pivot.key)
{
right--;
}
elem[left] = elem[right];
//指针从左向右扫描,找到第一个比基准值大的
while (left < right && elem[left].key <= pivot.key)
{
left++;
}
elem[right] = elem[left];
}
//基准值与循环截止位置的值交换
elem[left] = pivot;
return left;
}
来源:CSDN
作者:Ich / liebe / dich
链接:https://blog.csdn.net/weixin_43894577/article/details/103474509