数据结构——快速排序

快速排序

一.思路:

1.选择一个基准值(以最右边为例)

2.分割区间:分割区间的结果是

      小于等于基准值的      基准值      大于等于基准值的

3.分治:递归处理左右两个区间。

        递归边界:

              (1)区间内没有数字了

              (2)区间内只有一个数字——即有序了。

 

二.分割区间的三种方法

1.Hover法:

代码:

//1.Hover法
int partition1(int arr[], int left, int right) {
	int begin = left;
	int end = right;
	while (begin < end) {
		while (begin < end && arr[begin] <= arr[right]) {
			begin++; 
		}
		//此时,该循环走完,begin下标所对应的数字就是大于基准值的
		//开始走end
		while (begin < end && arr[end] >= arr[right]) {
			end--;
		}
		Swap(arr, begin, end);
	}
	//大循环走完,begin与end相遇,将begin/end下标所对应的数字与基准值交换
	Swap(arr, begin, right);
	return begin;
}

2.挖坑法:思路与Hover法一模不一样。

 代码:

//2.挖坑法
int partition2(int arr[], int left, int right) {
	int begin = left;
	int end = right;
	int pivot = arr[right];
	while (begin < end) {
		while (begin < end&&arr[begin] <= pivot) {
			begin++;
		}
		//此时begin下标锁定对应的数字一定大于基准值,直接填到end位置
		arr[end] = arr[begin];
		//开始走end
		while (begin < end&&arr[end] >= pivot) {
			end--;
		}
		//此时,end下标所对应的值一定小于基准值,直接填到begin位置
		arr[begin] = arr[end];
	}
	//此时,begin与end相遇,将基准值填到此处即可
	arr[begin] = pivot;
	return begin;
}

3.前后下标法 

代码:

//3.前后下标法
int partition3(int arr[], int left, int right) {
	int d = left;
	for (int i = left; i < right; i++) {
		if (arr[i] < arr[right]) {
			Swap(arr, i, d);
			d++;
		}
	}
	Swap(arr, d, right);
	return d;
}

三.快排代码

void Swap(int arr[], int left, int right) {
	int temp = arr[left];
	arr[left] = arr[right];
	arr[right] = temp;
}

//2.挖坑法
int partition2(int arr[], int left, int right) {
	int begin = left;
	int end = right;
	int pivot = arr[right];
	while (begin < end) {
		while (begin < end&&arr[begin] <= pivot) {
			begin++;
		}
		//此时begin下标锁定对应的数字一定大于基准值,直接填到end位置
		arr[end] = arr[begin];
		//开始走end
		while (begin < end&&arr[end] >= pivot) {
			end--;
		}
		//此时,end下标所对应的值一定小于基准值,直接填到begin位置
		arr[begin] = arr[end];
	}
	//此时,begin与end相遇,将基准值填到此处即可
	arr[begin] = pivot;
	return begin;
}

void QuickSort(int arr[], int left, int right) {
	//递归边界:区间内部没有数字或者区间内只有一个数字
	if (left > right || left == right) {
		return;
	}
	//1.分割区间
	int d = partition2(arr, left, right);
	//2.分治左右两个区间
	QuickSort(arr, left, d - 1);
	QuickSort(arr, d + 1, right);
}

四.存在问题

问题一:

1.问题:当选取的基准值是数组的最值时,就会出现最坏情况,即分割的一半区间没有数字,另一半区间是数组的所有或绝大部分元素。

2.解决:三数取中法:

         (1)取区间中间下标的数字,将左右边界与其比较,返回中间大小数字的下标

         (2)将选取的基准值与其交换即可

3.代码:

int getmidindex(int* arr,int left,int right){
    int mid = left + (right-left) >> 1;
    if(arr[left] < arr[right]){
        if(arr[mid] < arr[left]){
            return left;
        }
        else if(arr[mid] < arr[right]){
            return mid;
        }
        else{
            return right;
        }
    }
    else{
        if(arr[mid] < arr[right]){
            return right;
        }
        else if(arr[mid] < arr[left]){
            return mid;
        }
        else{
            return left;
        }
    }
}

最后只要在partition里面先将得到的中间数字与要选择的基准值进行交换即可。

问题二:当区间内部数字过多时,递归的层次较深,性能大打折扣。可以判断如果区间内部数字较少的情况下,可以选择插入排序。

if(right - left <= 16){
    insert(arr + left, right - left);
}

五.非递归

1.实现:

(1)将左右边界入栈

(2)取栈顶,出栈,拿到左右边界

(3)判断是否需要对区间进行分割--判断区间内部元素的数目

(4)栈非空,就循环判断分割区间,直到栈空,就代表排序完毕

2.代码:

void QuickSortNor(int* arr, int size) {
	int left = 0;
	int right = size - 1;
	stack<int> s;
	s.push(right);
	s.push(left);
	//栈如果不空,就让其一直循环
	while (!s.empty()) {
		//先取到栈顶元素
		left = s.top();
		s.pop();
		right = s.top();
		s.pop();
		//判断是否需要分割区间
		if (right - left > 1) {
			int mid = partition(arr, left, right);
			//处理左侧区间
			s.push(mid);
			s.push(left);
			//右侧区间
			s.push(right);
			s.push(mid + 1);
		}
	}
}

六.总结:

1.选择基准值

2.分割区间

3.分治处理左右两个小区间直到区间不必在分割--区间内部没有数字,或只有一个数字

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值