三. 快速排序(Quick Sort)及其简单改进(严蔚敏奶奶)

1 快速排序

1.1 算法描述(文字)

快速排序的基本思想是,通过一趟排序将待排序记录分割成独立的两个部分,一部分记录均比另一部分小,然后分别对两个部分进行排序.

它是对冒泡排序的一种改进.关键在于分割的过程,它遵循下面3个条件对数组a[N]重组

  • a[i]应该位于它最终的位置上
  • a[0]至a[i-1]应该不大于a[i]
  • a[i+1]至a[N-1]应该不小于a[i]

1.2 算法描述(草图)

1.3 代码实现(c语言)

1.3.1 原始版本v1(交换)

1.3.1.1 算法草图

快速排序一次分割-双向扫描交换

1.3.1.2 代码实现(c语言 严蔚敏奶奶)

/*
   v1: 分割函数,关键逻辑 
       双向扫描
       严蔚敏奶奶-数据结构page274
*/
int Partition(SqList &L, int low, int high){
	// 交换顺序表L中子表L.r[low..high]的记录,使枢轴记录到位,并返回其所在位置,此时
	// 在它之前的记录均不大于它,在它之后的记录均不小于它
	KeyType pivotKey = L.r[low].key;      // 用子表的第一个记录作枢轴记录 
	while (low < high){           // 从表的两端交替地向中间扫描 
		while (low < high && L.r[high].key >= pivotKey) {
			--high;
		}
		
		swap(&L.r[low], &L.r[high]);     // 将比枢轴记录小的记录交换到低端 
		
		while (low < high && L.r[low].key <= pivotKey) {
			++low;
		}
		
		swap(&L.r[low], &L.r[high]);     // 将比枢轴记录大的记录交换到高端 
	} 
	return low;                        // 返回枢轴位置 
} 

void QSort(SqList &L, int low, int high){
	// 对顺序表L中的子序列L.r[low..high]作快速排序
	if (low < high){                 // 长度大于1 
		int pivotloc = Partition(L, low, high);  //版本v1 将L.r[low..high]一分为二 
		QSort(L, low, pivotloc-1);                  // 对低子表递归排序,pivotloc是枢轴位置 
		QSort(L, pivotloc+1, high);                 // 对高子表递归排序 
	} 
}  // QSort

1.3.2 改进版本v2(赋值)

改进点:版本v1具体实现上,每交换一对记录需进行3次记录移动的操作,而实际上,在排序过程中对,枢轴记录的赋值是多余的,因为只有在一趟排序结束时,枢轴才是最后的位置.

1.3.2.1 算法草图

快速排序一次分割-双向扫描移动

1.3.2.2 代码实现(c语言 严蔚敏奶奶)

/*
      v1: 分割函数,关键逻辑 
       双向扫描-移动 
       严蔚敏奶奶-数据结构page274
*/
int Partition2(SqList &L, int low, int high){
	// 交换顺序表L中子表L.r[low..high]的记录,使枢轴记录到位,并返回其所在位置,此时
	// 在它之前的记录均不大于它,在它之后的记录均不小于它
	L.r[0] = L.r[low];
	KeyType pivotKey = L.r[low].key;      // 用子表的第一个记录作枢轴记录 
	while (low < high){           // 从表的两端交替地向中间扫描 
		while (low < high && L.r[high].key >= pivotKey) {
			--high;
		}
		
		L.r[low] = L.r[high];                // (改进点)将比枢轴记录小的记录移到低端 
		
		while (low < high && L.r[low].key <= pivotKey) {
			++low;
		}
		
		L.r[high] = L.r[low];             // (改进点)将比枢轴记录大的记录交换到高端 
	} 
	L.r[low] = L.r[0]; 
	return low;                        // 返回枢轴位置 
} 


void QSort(SqList &L, int low, int high){
	// 对顺序表L中的子序列L.r[low..high]作快速排序
	if (low < high){                 // 长度大于1 
		int pivotloc = Partition2(L, low, high);    //版本v2 将L.r[low..high]一分为二 
		QSort(L, low, pivotloc-1);                  // 对低子表递归排序,pivotloc是枢轴位置 
		QSort(L, pivotloc+1, high);                 // 对高子表递归排序 
	} 
}  // QSort

1.4 Demo实例

1.4.1 完整代码

#include <iostream>
#include <stdio.h> 

/*
   快速排序
   严奶奶 数据结构  
   实现: 参见严奶奶 Page264 274
*/

#define MAXSIZE 20    // 顺序表的最大长度 

typedef int KeyType;  // 关键字类型为整数类型

typedef int  InfoType; //

typedef struct {
	KeyType key;        // 关键字项 
	InfoType otherinfo; // 其他数据项 
} RedType;

typedef struct {
	RedType r[MAXSIZE + 1]; // r[0]闲置或用作哨兵单元 
	int length;             // 顺序表长度 
}  SqList;                  // 顺序表类型 

/*
  交换变量值 
*/
void swap(RedType* x, RedType* y){
    RedType t = *x;
    *x = *y;
	*y =  t; 
}


/*
     v1: 分割函数,关键逻辑 
       双向扫描-交换 
       严蔚敏奶奶-数据结构page274
*/
int Partition(SqList &L, int low, int high){
	// 交换顺序表L中子表L.r[low..high]的记录,使枢轴记录到位,并返回其所在位置,此时
	// 在它之前的记录均不大于它,在它之后的记录均不小于它
	KeyType pivotKey = L.r[low].key;      // 用子表的第一个记录作枢轴记录 
	while (low < high){           // 从表的两端交替地向中间扫描 
		while (low < high && L.r[high].key >= pivotKey) {
			--high;
		}
		
		swap(&L.r[low], &L.r[high]);     // 将比枢轴记录小的记录交换到低端 
		
		while (low < high && L.r[low].key <= pivotKey) {
			++low;
		}
		
		swap(&L.r[low], &L.r[high]);     // 将比枢轴记录大的记录交换到高端 
	} 
	return low;                        // 返回枢轴位置 
} 



/*
      v1: 分割函数,关键逻辑 
       双向扫描-移动 
       严蔚敏奶奶-数据结构page274
*/
int Partition2(SqList &L, int low, int high){
	// 交换顺序表L中子表L.r[low..high]的记录,使枢轴记录到位,并返回其所在位置,此时
	// 在它之前的记录均不大于它,在它之后的记录均不小于它
	L.r[0] = L.r[low];
	KeyType pivotKey = L.r[low].key;      // 用子表的第一个记录作枢轴记录 
	while (low < high){           // 从表的两端交替地向中间扫描 
		while (low < high && L.r[high].key >= pivotKey) {
			--high;
		}
		
		L.r[low] = L.r[high];                // 将比枢轴记录小的记录移到低端 
		
		while (low < high && L.r[low].key <= pivotKey) {
			++low;
		}
		
		L.r[high] = L.r[low];             // 将比枢轴记录大的记录交换到高端 
	} 
	L.r[low] = L.r[0]; 
	return low;                        // 返回枢轴位置 
} 


void QSort(SqList &L, int low, int high){
	// 对顺序表L中的子序列L.r[low..high]作快速排序
	if (low < high){                 // 长度大于1 
		// int pivotloc = Partition(L, low, high);  //版本v1 将L.r[low..high]一分为二 
		int pivotloc = Partition2(L, low, high);    //版本v2 将L.r[low..high]一分为二 
		QSort(L, low, pivotloc-1);                  // 对低子表递归排序,pivotloc是枢轴位置 
		QSort(L, pivotloc+1, high);                 // 对高子表递归排序 
	} 
}  // QSort


void QuickSort(SqList &L){
	// 对顺序表L作快速排序
	QSort(L, 1, L.length); 
} // QuickSort


void print(SqList L){
	for (int i = 1; i<=L.length; i++){
			printf("(%d, %d)", L.r[i].key, L.r[i].otherinfo); 
	}
	
	printf("\n");
}


int main(int argc, char** argv) {
	// 构造待排序数据 
	RedType arrData [] = {{35,85}, {33,90}, {1, 99}, {62, 87}, {61, 88}} ;
	int arrayLength = sizeof(arrData)/sizeof(arrData[0]); 
	
	SqList l;
	for (int i = 0; i< arrayLength; i++){
		l.r[i+1] = arrData[i]; 
	}
	
	l.length = arrayLength;
	
	print(l);
	
	QuickSort(l);
	
	print(l);

	return 0;
}

1.4.2 执行结果

可以看到1,33,35,61,62有序

谢谢阅读完,欢迎您的留言讨论

posted @ 2023-11-02 22:55  岁月信  阅读(309)  评论(0)    收藏  举报