排序算法之交换排序
2011-12-06 09:31
169 查看
交换排序的基本思想是:两两比较待排序记录的关键字,发现两个记录的次序相反时即进行交换,直到没有反序的记录为止。
应用交换排序基本思想的主要排序方法有:冒泡排序(Bubble sort)和快速排序(Quick sort)。
1. 冒泡排序
(1) 冒泡排序思想:第一趟排序:首先将第一个记录的关键字和第二个记录的关键字比较,若为逆序,则将两个记录交换之,然后比较第二个和第三个的关键字。以此类推,直至第n-1个记录和第n个记录关键字比较为止。该过程为第一趟排序,使得最大的关键字排到了最后面。第二趟排序:对前n-1个记录进行相同操作,完成后使得次大的关键字排在n-1位置上。以此类推,进行第三、四次排序直到排序结束。判断排序结束条件是:在一趟排序过程中没有发生过交换记录的操作。一般第i趟排序是从第1个元素到(n-i+1)个记录依次比较相邻两个记录关键字,并在逆序时交换记录。
(2) 冒泡排序算法:
[cpp] view
plaincopy
void bubble_sort(sqlist *s)
{
int i, j, n, flag = 1;
datatype tmp;
n = s->len;
for (i = 0; i < n-1 && flag; ++i) {
flag = 0;
for (j = 0; j < n-i-1; ++j) {
if (s->data[j].key > s->data[j+1].key) {
tmp = s->data[j];
s->data[j] = s->data[j+1];
s->data[j+1] = tmp;
flag = 1;
}
}
output_list(*s); /* 测试用 */
}
}
(3) 算法效率分析
最好情况:初始序列为正序序列,只需进行一次排序,在此过程中进行n-1次比较,不移动数据。
最差情况:初始序列为逆序序列,需要进行n-1趟排序,需要进行内n(n-1)/2次比较,并且移动数据,每次比较都必须移动记录三次来达到交换记录位置。因此总的时间复杂度为O(n2)。
冒泡排序是就地排序,是稳定的。
2. 快速排序
(1) 快速排序的基本思想
通过一趟排序将待排记录分割成独立两部分,其中一部分记录的关键字均比另一部分记录的关键小,则可以分别对这两部分记录继续进行排序,以达到这个序列有序。
设当前待排序的无序区为R[low..high], 在R[low..high]中任选一个记录作为基准(Pivot),以此基准将当前无序区划分为左、右两个较小的子区间R[low..pivotpos-1)和R[pivotpos+1..high],并使左边子区间中所有记录的关键字均小于等于基准记录(不妨记为pivot)的关键字pivot.key,右边的子区间中所有记录的关键字均大于等于pivot.key,而基准记录pivot则位于正确的位置(pivotpos)上,它无须参加后续的排序。通过递归调用快速排序对左、右子区间R[low..pivotpos-1]和R[pivotpos+1..high]快速排序。两个递归调用结束时,其左、右两个子区间已有序。
(2) 快速排序算法
[cpp] view
plaincopy
int partition(sqlist *s, int low, int high)
{
keytype pivotkey;
datatype t;
t = s->data[low];
pivotkey = s->data[low].key;
while (low < high) {
while (low<high && s->data[high].key >= pivotkey)
--high;
if (low < high)
s->data[low++] = s->data[high];
while (low<high && s->data[low].key <= pivotkey)
++low;
if (low < high)
s->data[high--] = s->data[low];
}
s->data[low] = t;
return low;
}
[cpp] view
plaincopy
void quick_sort(sqlist *s, int low, int high)
{
int pivotloc;
static int count = 1;
if (low < high) {
pivotloc = partition(s, low, high);
output_list2(*s, pivotloc, count);/* 测试用 */
++count; /* 测试用 */
quick_sort(s, low, pivotloc-1);
quick_sort(s, pivotloc+1, high);
}
}
(3) 算法分析
1) 最坏时间复杂度:
最坏情况是每次划分选取的基准都是当前无序区中关键字最小(或最大)的记录,划分的结果是基准左边的子区间为空(或右边的子区间为空),而划分所得的另一个非空的子区间中记录数目,仅仅比划分前的无序区中记录个数减少一个。
因此,快速排序必须做n-1次划分,第i次划分开始时区间长度为n-i+1,所需的比较次数为n-i(1≤i≤n-1),故总的比较次数达到最大值:n(n-1)/2=O(n2)。
如果按上面给出的划分算法,每次取当前无序区的第1个记录为基准,那么当文件的记录已按递增序(或递减序)排列时,每次划分所取的基准就是当前无序区中关键字最小(或最大)的记录,则快速排序所需的比较次数反而最多。
2) 最好时间复杂度
在最好情况下,每次划分所取的基准都是当前无序区的"中值"记录,划分的结果是基准的左、右两个无序子区间的长度大致相等。总的关键字比较次数:0(nlgn)
因为快速排序的记录移动次数不大于比较的次数,所以快速排序的最坏时间复杂度应为0(n2),最好时间复杂度为O(nlgn)。
3) 平均时间复杂度
尽管快速排序的最坏时间为O(n2),但就平均性能而言,它是基于关键字比较的内部排序算法中速度最快者,快速排序亦因此而得名。它的平均时间复杂度为O(nlgn)。
4) 空间复杂度
快速排序在系统内部需要一个栈来实现递归。若每次划分较为均匀,则其递归树的高度为O(lgn),故递归后需栈空间为O(lgn)。最坏情况下,递归树的高度为O(n),所需的栈空间为O(n)。
5) 快速排序是非稳定的。
(2) 基准关键字的选择
在当前无序区中选取划分的基准关键字是决定算法性能的关键。
1) "三者取中"的规则
"三者取中"规则,即在当前区间里,将该区间首、尾和中间位置上的关键字比较,取三者之中值所对应的记录作为基准,在划分开始前将该基准记录和该区伺的第1个记录进行交换,此后的划分过程与上面所给的Partition算法完全相同。
2) 取位于low和high之间的随机数k(low≤k≤high),用R[k]作为基准
选取基准最好的方法是用一个随机函数产生一个取位于low和high之间的随机数k(low≤k≤high),用R[k]作为基准,这相当于强迫R[low..high]中的记录是随机分布的。用此方法所得到的快速排序一般称为随机的快速排序。
3. 算法实现源码
http://download.csdn.net/detail/algorithm_only/3856415
应用交换排序基本思想的主要排序方法有:冒泡排序(Bubble sort)和快速排序(Quick sort)。
1. 冒泡排序
(1) 冒泡排序思想:第一趟排序:首先将第一个记录的关键字和第二个记录的关键字比较,若为逆序,则将两个记录交换之,然后比较第二个和第三个的关键字。以此类推,直至第n-1个记录和第n个记录关键字比较为止。该过程为第一趟排序,使得最大的关键字排到了最后面。第二趟排序:对前n-1个记录进行相同操作,完成后使得次大的关键字排在n-1位置上。以此类推,进行第三、四次排序直到排序结束。判断排序结束条件是:在一趟排序过程中没有发生过交换记录的操作。一般第i趟排序是从第1个元素到(n-i+1)个记录依次比较相邻两个记录关键字,并在逆序时交换记录。
(2) 冒泡排序算法:
[cpp] view
plaincopy
void bubble_sort(sqlist *s)
{
int i, j, n, flag = 1;
datatype tmp;
n = s->len;
for (i = 0; i < n-1 && flag; ++i) {
flag = 0;
for (j = 0; j < n-i-1; ++j) {
if (s->data[j].key > s->data[j+1].key) {
tmp = s->data[j];
s->data[j] = s->data[j+1];
s->data[j+1] = tmp;
flag = 1;
}
}
output_list(*s); /* 测试用 */
}
}
(3) 算法效率分析
最好情况:初始序列为正序序列,只需进行一次排序,在此过程中进行n-1次比较,不移动数据。
最差情况:初始序列为逆序序列,需要进行n-1趟排序,需要进行内n(n-1)/2次比较,并且移动数据,每次比较都必须移动记录三次来达到交换记录位置。因此总的时间复杂度为O(n2)。
冒泡排序是就地排序,是稳定的。
2. 快速排序
(1) 快速排序的基本思想
通过一趟排序将待排记录分割成独立两部分,其中一部分记录的关键字均比另一部分记录的关键小,则可以分别对这两部分记录继续进行排序,以达到这个序列有序。
设当前待排序的无序区为R[low..high], 在R[low..high]中任选一个记录作为基准(Pivot),以此基准将当前无序区划分为左、右两个较小的子区间R[low..pivotpos-1)和R[pivotpos+1..high],并使左边子区间中所有记录的关键字均小于等于基准记录(不妨记为pivot)的关键字pivot.key,右边的子区间中所有记录的关键字均大于等于pivot.key,而基准记录pivot则位于正确的位置(pivotpos)上,它无须参加后续的排序。通过递归调用快速排序对左、右子区间R[low..pivotpos-1]和R[pivotpos+1..high]快速排序。两个递归调用结束时,其左、右两个子区间已有序。
(2) 快速排序算法
[cpp] view
plaincopy
int partition(sqlist *s, int low, int high)
{
keytype pivotkey;
datatype t;
t = s->data[low];
pivotkey = s->data[low].key;
while (low < high) {
while (low<high && s->data[high].key >= pivotkey)
--high;
if (low < high)
s->data[low++] = s->data[high];
while (low<high && s->data[low].key <= pivotkey)
++low;
if (low < high)
s->data[high--] = s->data[low];
}
s->data[low] = t;
return low;
}
[cpp] view
plaincopy
void quick_sort(sqlist *s, int low, int high)
{
int pivotloc;
static int count = 1;
if (low < high) {
pivotloc = partition(s, low, high);
output_list2(*s, pivotloc, count);/* 测试用 */
++count; /* 测试用 */
quick_sort(s, low, pivotloc-1);
quick_sort(s, pivotloc+1, high);
}
}
(3) 算法分析
1) 最坏时间复杂度:
最坏情况是每次划分选取的基准都是当前无序区中关键字最小(或最大)的记录,划分的结果是基准左边的子区间为空(或右边的子区间为空),而划分所得的另一个非空的子区间中记录数目,仅仅比划分前的无序区中记录个数减少一个。
因此,快速排序必须做n-1次划分,第i次划分开始时区间长度为n-i+1,所需的比较次数为n-i(1≤i≤n-1),故总的比较次数达到最大值:n(n-1)/2=O(n2)。
如果按上面给出的划分算法,每次取当前无序区的第1个记录为基准,那么当文件的记录已按递增序(或递减序)排列时,每次划分所取的基准就是当前无序区中关键字最小(或最大)的记录,则快速排序所需的比较次数反而最多。
2) 最好时间复杂度
在最好情况下,每次划分所取的基准都是当前无序区的"中值"记录,划分的结果是基准的左、右两个无序子区间的长度大致相等。总的关键字比较次数:0(nlgn)
因为快速排序的记录移动次数不大于比较的次数,所以快速排序的最坏时间复杂度应为0(n2),最好时间复杂度为O(nlgn)。
3) 平均时间复杂度
尽管快速排序的最坏时间为O(n2),但就平均性能而言,它是基于关键字比较的内部排序算法中速度最快者,快速排序亦因此而得名。它的平均时间复杂度为O(nlgn)。
4) 空间复杂度
快速排序在系统内部需要一个栈来实现递归。若每次划分较为均匀,则其递归树的高度为O(lgn),故递归后需栈空间为O(lgn)。最坏情况下,递归树的高度为O(n),所需的栈空间为O(n)。
5) 快速排序是非稳定的。
(2) 基准关键字的选择
在当前无序区中选取划分的基准关键字是决定算法性能的关键。
1) "三者取中"的规则
"三者取中"规则,即在当前区间里,将该区间首、尾和中间位置上的关键字比较,取三者之中值所对应的记录作为基准,在划分开始前将该基准记录和该区伺的第1个记录进行交换,此后的划分过程与上面所给的Partition算法完全相同。
2) 取位于low和high之间的随机数k(low≤k≤high),用R[k]作为基准
选取基准最好的方法是用一个随机函数产生一个取位于low和high之间的随机数k(low≤k≤high),用R[k]作为基准,这相当于强迫R[low..high]中的记录是随机分布的。用此方法所得到的快速排序一般称为随机的快速排序。
3. 算法实现源码
http://download.csdn.net/detail/algorithm_only/3856415
相关文章推荐
- 排序算法-heap排序-改良的交换排序算法
- 交换排序:冒泡排序、选择排序【排序算法】
- 排序算法系列-交换之快速排序
- 【数据结构】排序算法(二)之交换排序之快速排序(QuickSort)
- 排序算法五:交换排序之快速排序
- 排序算法_C++(四)交换排序之冒泡排序
- 软考二进宫-排序算法-交换排序
- 排序算法之四 —— 直接选择排序及交换二个数据的正确实现
- 8)排序①排序算法之交换排序[2]快速排序
- 排序算法-交换排序之快速排序
- 【算法】排序算法第三讲:交换排序
- 排序算法--交换排序之快速排序
- 经典排序算法系列之三:交换排序
- 排序算法(三)、交换排序 —— 冒泡排序 和 快速排序
- 排序算法-交换排序_冒泡排序
- 排序算法(二)交换排序
- 排序算法----交换排序(冒泡排序,快速排序)
- 算法 排序算法之交换排序--冒泡排序和快速排序
- 排序算法-交换排序_快速排序
- 排序算法---交换排序( java)