算法整理(二)---高速排序的两种实现方式:双边扫描和单边扫描

首先简单谈下高速排序的特点,时间复杂度O(nLog n),最差时间复杂度O(n^2),平均时间O(nLog n).由于用到了函数栈,空间复杂度为O(lg n),最差为O(n).是一种不稳定的排序方法。基本思想是分治法,这位大大的http://blog.csdn.net/morewindows/article/details/6684558 讲的很清楚了,分治法+挖坑法,我就不多说了。就是以某个数为參照,使得左边的都小于他,右边的数都大于他。然后对他的左右两个区间採取相同的方法进行递归。

就其总体实现而言,有两大种思路,一是双边扫描,二是单边扫描。以下分别来上程序:

一、双边扫描

双边扫描是谭浩强书中的方法,个人认为比以下的单边扫描更好理解,也是博文里採用的方法。以下看程序:

void quickSort1(int* x, int l, int r){

	if(l < r){
		int i = l, j = r, key = x[l];
		while(i < j){
			while( i < j && x[j] >= key){
				j--;
			}
			if(i < j){
				x[i++] = x[j];
			}
			while(i < j && x[i] <= key){
				i++;
			}
			if(i < j){
				x[j--] = x[i];
			}
		}
		cout<<"i = " <<i<<" j = "<<j<<endl;
		x[i] = key;
		quickSort1(x, l, i-1);
		quickSort1(x, i+1, r);
	}

}

双边扫描很直观,首先进到程序里推断是否l<r,当满足条件才进去。这也是用递归的一个必要条件,一定要让函数有尽头,有边界。然后进入大while循环,接着进入小while循环,先从右边找,仅仅要满足数字大于key就一直让j往左移。直到第一个不满足条件的,就是第一个小于key的数跳出while循环,将它放在左边挖的“坑”上。同一时候让坑的索引+1,接着从左边開始扫描,找到第一个大于key的数,再将它填到右边的坑上。右边的坑索引-1,接着再从右边扫描。直到最后跳出大while循环,此时i = j。也就是完毕了一次高速排序的扫描。之后将最初的key放到x[i],事实上放到x[j]也是一样的。由于i等于j么,此时!然后进行递归,对区间[l, i - 1], [i+1, r]进行相同的操作。

双边排序的要点: 1、最初的if一定要有,这是最后递归出来的标志位。2,为了找到一个数使它的左边都大于它,右边都小于它,要多次循环,这个循环就是大while循环。3、双边排序不须要swap,即无需交换。

二、单边扫描

上面的双边排序,出来一次大while循环,要从两边进行多次。单边扫描,则仅仅需从左走到右就能完毕一次 快排。

void quickSort2(int x[], int l, int r){
	if(l >= r)
		return;
	int m = l;
	for(int i = l + l; i <= r; i++ ){
		if(x[i] < x[l]){
			swap2(x[++m], x[i]);
		}
	}
	swap2(x[l], x[m]);
	quickSort2(x, l, m - 1);
	quickSort2(x, m + 1, r);

}
void swap2(int &a,int &b){
	if(a==b) return;//对同一地址的数据交换,会使其结果为0
	a=a^b;
	b=a^b;
	a=a^b;
}

代码是不是更简单了?程序先进行推断,假设l>=r直接return,这点跟双边扫描的if一个意思,都是为递归创造结束的标志。然后用m记录最左边的那个的索引,这里默认的是第一个,即x[l]的索引。[注,m的初始值不一定指向key!,仅仅是指向最左边的。]然后进入扫描,直接从l + 1開始,假设右边的小于key,就让x[++m]和x[i]交换。假设右边的大于key,则不进行不论什么操作。这里有个特例,假设l = 0, 则m = 0.假设x[1]小于x[0],则让x[1] 和x[1]进行交换,也就等于没交换。假设数组是5 4 3 2 1,则这里的交换就失效了。 再往后看,直到for循环结束,走出循环,让最后m指的位置的数和最初的key进行交换。如上面 5 4 3 2 1,则第一次快排的结果是 1 4 3 2 5,仅仅有for出来后的那次swap才起作用。这里的m有个特殊含义,即指向小于key的最右边的那个数。所以出来后才用它(x[m])和key(即x[l])进行交换。

单边扫描的特点:

1、程序须要交换;

2、更有冒泡法的色彩;冒泡的目的不是让最大的数沉到最右边,而是让小于key的都左移,找到分界索引m。使之和key进行交换。

3、此版本号的的单边扫描属于最基础的,还能够优化。

本想測出两个算法的时间 消耗差异,遗憾的是c++获得程序执行时间太费劲了,弄半天没弄成。以下附上完整程序:

//============================================================================
// Name        : QuikSort.cpp
// Author      : YanZi
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include <malloc.h>

using namespace std;
void swap1(int a, int b);
void printArray(int* in, int n);

void quickSort1(int* x, int l, int r);//双边扫描,高速排序
void quickSort2(int x[], int l, int r);//单边扫描,高速排序
void swap2(int &a,int &b); //交换,在MinGW上必须採用此方法,swap1无效

#define N 8 //数组的长度

int main() {
	int* input = NULL;
	input = (int*)malloc(N * sizeof(int));
	if(input == NULL){
		cout<<"内存溢出"<<endl;
	}
	for(int i = 0; i < N; i++){
		input[i] = rand();
	}
	//	int input[] = {55, 41, 59, 26, 53, 58, 97, 93};

	cout<<"原始数据:"<<endl;
	printArray(input, N);

	quickSort2(input, 0, N-1);
	printArray(input, N);

	return 0;
}
void swap1(int a, int b){
	int temp = a;
	a = b;
	b = temp;
}
void printArray(int * in, int n){
	if(in == NULL){
		return;
	}
	for(int i = 0; i<n; i++){
		cout<<" "<<in[i];
	}
	cout<<endl;

}

void quickSort1(int* x, int l, int r){

	if(l < r){
		int i = l, j = r, key = x[l];
		while(i < j){
			while( i < j && x[j] >= key){
				j--;
			}
			if(i < j){
				x[i++] = x[j];
			}
			while(i < j && x[i] <= key){
				i++;
			}
			if(i < j){
				x[j--] = x[i];
			}
		}
		cout<<"i = " <<i<<" j = "<<j<<endl;
		x[i] = key;
		quickSort1(x, l, i-1);
		quickSort1(x, i+1, r);
	}

}
void quickSort2(int x[], int l, int r){
	if(l >= r)
		return;
	int m = l;
	for(int i = l + l; i <= r; i++ ){
		if(x[i] < x[l]){
			swap2(x[++m], x[i]);
		}
	}
	swap2(x[l], x[m]);
	quickSort2(x, l, m - 1);
	quickSort2(x, m + 1, r);

}
void swap2(int &a,int &b){
	if(a==b) return;//对同一地址的数据交换,会使其结果为0
	a=a^b;
	b=a^b;
	a=a^b;
}

算法整理(二)---高速排序的两种实现方式:双边扫描和单边扫描

时间: 2024-08-27 10:17:40

算法整理(二)---高速排序的两种实现方式:双边扫描和单边扫描的相关文章

算法整理(二)---快速排序的两种实现方式:双边扫描和单边扫描

首先简单谈下快速排序的特点,时间复杂度O(nLog n),最差时间复杂度O(n^2),平均时间O(nLog n).因为用到了函数栈,空间复杂度为O(lg n),最差为O(n).是一种不稳定的排序方法.基本思想是分治法,这位大大的http://blog.csdn.net/morewindows/article/details/6684558 讲的非常清楚了,分治法+挖坑法,我就不多说了.就是以某个数为参照,使得左边的都小于他,右边的数都大于他.然后对他的左右两个区间采取同样的方法进行递归. 就其整

详谈排序算法之选择类排序(两种方法实现堆排序)

   今天我们再来讨论一下选择类排序,选择类排序分为:简单排序,树形选择排序和堆排序.但我们主要说的是简单和堆排序两个,因为树形选择排序使用了较多的辅助空间,以及和∞进行多余比较,为弥补树型选择排序的这些缺点, J.W.J.Williams 在 1964 年提出了进一步的改进方法,即堆排序.对于我个人而言..一开始并不是很理解它的算法思想,纠结了许久.在网上查找资料的时候发现这位大神的文章思路十分清晰,而且把创建堆以及堆化数组的算法讲解的十分详细.如果有不明白堆排序思路的,可以先看看这篇文章~堆

详谈排序算法之插入类排序(两种思路实现希尔排序)

1. 排序( sorting) 的功能是将一个数据元素的任意序列,重新排列成一个按关键字有序的序列.其确切的定义为: 假设有n个数据元素的序列{R1 , R2 , - , Rn},其相应关键字的序列是{K1 , K2 , - , Kn} ,通过排序要求找出下标 1 , 2 , - , n的一种排列p1 , p2 , - , pn,使得相应关键字满足如下的非递减(或非递增)关系Kp1 ≤ Kp2 ≤ - ≤ Kpn这样,就得到一个按关键字有序的纪录序列{ Rp1 , Rp2 , - , Rpn }

详谈排序算法之交换类排序(两种方法实现快速排序【思路一致】)

1.冒泡排序    起泡排序的思想非常简单.首先,将 n 个元素中的第一个和第二个进行比较,如果两个元素的位置为逆序,则交换两个元素的位置:进而比较第二个和第三个元素关键字,如此类推,直到比较第 n-1 个元素和第 n 个元素为止:上述过程描述了起泡排序的第一趟排序过程,在第一趟排序过程中,我们将关键字最大的元素通过交换操作放到了具有 n 个元素的序列的最一个位置上.然后进行第二趟排序,在第二趟排序过程中对元素序列的前 n-1 个元素进行相同操作,其结果是将关键字次大的元素通过交换放到第 n-1

jqGrid排序的两种实现方式:

原创lnst 实现方案一客户端实现排序: jqGrid属性 loadonce:true时,所有数据加载在客户端,点击列标题由jqGrid在客户端自动排序,不再从服务器取值. 参考文件:ccMxCxTjCc.js js设定: jqGrid的设定: colModel: [ { ..... name: 'zykj', sortable: true, },...... }], ......loadonce:true, //一次加载全部数据到客户端,由客户端进行排序. sortable: true, so

List排序的两种简便方式

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ListSort { class Program { static void Main(string[] args) { List<Customer> listCustomer = new List<Customer>(); listCustomer.Add(new Customer { na

JAVABEAN是什么和总结JAVABEAN的两种使用方式

看完这个后再也不纠结javabean是什么东西了,感谢博主,由于是Javablog不能收藏故在此转发. 以下内容转自:http://www.blogjava.net/flysky19/articles/88180.html 一. javabean 是什么? Javabean 就是一个类,这个类就定义一系列 get<Name> 和 set<Name> 方法. So simple ! Javabean 就是为了和 jsp 页面传数据化简交互过程而产生的. 自己的理解: 使用 javab

二、C++迭代器的两种实现方式 (Range for和C#、Java中的foreach)

一.迭代器概述 这个标题其实有点"标题党"的含义,因为C++在标准库中的实现迭代器的方式只有一种,也就是为类定义begin()和end()函数,C++11增加了range for语句,可以用来遍历迭代器中的元素.实现迭代器的第二种方式,就是用C++模拟C#和Java中的迭代器模式,并且我们可以定义出自己的foreach语句.除此之外,迭代器可能还有很多种实现的方法,各个库也会多自己的迭代器的实现有所定义,在这里只要明白迭代器的本质意义即可. 迭代器,也称作游标,是一种设计模式,我们可以

数据结构:图论:拓扑排序! 两种方法!

拓扑排序:(1)由偏序变成全序的过程!直观的说,偏序指集合中仅有部分成员之间可比较!而全序指集合中全体成员之间均可比较! (2)将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前. 数据结构中进行拓扑排序的方法: 方法一: (1)在有向图中选一个没有前驱的顶点且输出之! (2)从图中删除该顶点和所有以它为尾的弧. (3)重复上述两部,直至全部顶点均已输出,或者当前图中不存在无前驱的顶点为止.后一种情况说明有向图中存在环! 代码: #