关于快排的三种实现的说明

1、取最后一个元素作为中轴的快排

#include <bits/stdc++.h>

using namespace std;

int PartSort(int *arr, int left, int right) {
    
    
    /// 取数组中的最后一个元素作为中轴,&key相当于就是给arr[right]取一个别名。
    /// key和arr[right]指向的是同一个内存地址。
    int &key=arr[right];
    while(left<right) {
    
     /// 当left>=right的时候就退出。
    	/// 下面的左右两次遍历一定是先从左向右,再从右向左,因为是以最后一个元素作为基准。
    	/// 如果是选择第一个元素作为基准,那么遍历的顺序应该反过来。
        while(left<right&&arr[left]<=key) {
    
    
            left++;
        }
        while(left<right&&arr[right]>=key) {
    
    
            right--;
        }
        /// 交换选出来的左边比中轴元素大,右边比中轴元素小这两个元素。
        swap(arr[left], arr[right]);
    }
    /// 将一趟快排中的中轴元素放到他应该放入的位置。
    swap(arr[left], key);
    return left; /// 返回一趟快排之后的中轴元素最终的位置。
}

void QuickSort(int *arr, int left, int right) {
    
    
    /**
     assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行,原型定义:
     #include <assert.h>
     void assert( int expression );
     assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,
     然后通过调用 abort 来终止程序运行。
     */
    assert(arr); /// 如果arr为空的数组的话会调用abort来终止程序。
    if(left>=right) {
    
     /// 递归终止条件。
        return;
    }
    int index=PartSort(arr, left, right);
    /// 每一次快排都会确定一个元素在数组中最终的位置。
    QuickSort(arr, left, index-1);
    QuickSort(arr, index+1, right);
}

int main() {
    
    
    int arr[100005];
    int n;
    scanf("%d", &n);
    for(int i=0;i<n;i++) {
    
    
        scanf("%d", &arr[i]);
    }
    QuickSort(arr, 0, n-1);
    for(int i=0;i<n;i++) {
    
    
        printf("%d:%d\n", i, arr[i]);
    }
    return 0;
}

上面代码中的一趟快排还有另一种写法。

int PartSort(int* arr,int left,int right) {
    
    
	int key = arr[right]; /// 选取最后一个元素作为中轴,并将它先保存在变量key中。
	while(left < right) {
    
    
		while(left < right && arr[left] <= key)	{
    
    
			++left;
		}
		arr[right] = arr[left];
		while(left < right && arr[right] >= key) {
    
    
			--right;
		}
		arr[left] = arr[right];
	}
	/// 下面的代码left和right应该都是一样的。
	arr[right] = key;
	return right;
}

2、采用三轴取中的方法对快排进行优化。

#include <bits/stdc++.h>

using namespace std;

/// 三数取中
int GetMid(int* arr,int left,int right)
{
    
    
    assert(arr);
    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 right;
        else
            return mid;
    }
    else
    {
    
    
        if(arr[mid] < arr[right])
            return right;
        else if(arr[mid] > arr[left])
            return left;
        else
            return mid;
    }

}

int PartSort(int* arr,int left,int right) {
    
    
    int mid = GetMid(arr,left,right); /// 找出三个数中大小在中等的那个数的下标。
    swap(arr[mid],arr[right]); /// 将选出来的中等的数与最后一个数继续交换,就可以直接套用之前的以最后一个元素为中轴的快排了。

    int& key = arr[right]; /// 给arr[right]取一个别名为key,两者指向同一个内存空间。
    while(left < right)
    {
    
    
        while(left < right && arr[left] <= key)/// 因为有可能有相同的值,防止越界,所以加上left < right
            ++left;
        while(left < right && arr[right] >= key)
            --right;

        swap(arr[left],arr[right]);
    }

    swap(arr[left],key);
    return left;
}

void QuickSort(int *arr, int left, int right) {
    
    
    /**
     assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行,原型定义:
     #include <assert.h>
     void assert( int expression );
     assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,
     然后通过调用 abort 来终止程序运行。
     */
    assert(arr); /// 如果arr为空的数组的话会调用abort来终止程序。
    if(left>=right) {
    
     /// 递归终止条件。
        return;
    }
    int index=PartSort(arr, left, right);
    /// 每一次快排都会确定一个元素在数组中最终的位置。
    QuickSort(arr, left, idx-1);
    QuickSort(arr, idx+1, right);
}

int main() {
    
    
    int arr[100005];
    int n;
    scanf("%d", &n);
    for(int i=0;i<n;i++) {
    
    
        scanf("%d", &arr[i]);
    }
    QuickSort(arr, 0, n-1);
    for(int i=0;i<n;i++) {
    
    
        printf("%d:%d\n", i, arr[i]);
    }
    return 0;
}

3、快排的非递归实现

#include <bits/stdc++.h>

using namespace std;

int PartSort(int *arr, int left, int right) {
    
    
    /// 取数组中的最后一个元素作为中轴,&key相当于就是给arr[right]取一个别名。
    /// key和arr[right]指向的是同一个内存地址。
    int &key=arr[right];
    while(left<right) {
    
     /// 当left>=right的时候就退出。
        while(left<right&&arr[left]<=key) {
    
    
            left++;
        }
        while(left<right&&arr[right]>=key) {
    
    
            right--;
        }
        /// 交换选出来的左边比中轴元素大,右边比中轴元素小这两个元素。
        swap(arr[left], arr[right]);
    }
    /// 将一趟快排中的中轴元素放到他应该放入的位置。
    swap(arr[left], key);
    return left; /// 返回一趟快排之后的中轴元素最终的位置。
}

void QuickSortNotR(int* arr,int left,int right) {
    
    
	assert(arr);
	stack<int> s; /// 定义一个栈,用来模拟递归。
	/// 初始的时候将数组边界【0和n-1】压入栈中。先压入左区间还是右区间都可以,
	/// 只是取出栈的顺序不同,下面的代码要做相应的变化。
	s.push(left);
	s.push(right); /// 后入的right,所以要先拿right
	while(!s.empty()) {
    
     ///栈不为空
	    /// 取出要进行一趟快排的左右区间。
		int right = s.top();
		s.pop();
		int left = s.top();
		s.pop();
        /// 进行一趟快排。
		int index = PartSort(arr,left,right);
		/// 如果进行一趟快排之后中轴左边的序列有超过一个元素,那么就要进行排序。
		if((index - 1) > left) {
    
     ///左子序列
		    /// 将左右区间压入栈。
			s.push(left);
			s.push(index - 1);
		}
		/// 如果进行一趟快排之后中轴右边的序列有超过一个元素,那么就要进行排序。
		if((index + 1) < right) {
    
     ///右子序列
		    /// 将左右区间压入栈。
			s.push(index + 1);
			s.push(right);
		}
	}
}


int main() {
    
    
    int arr[100005];
    int n;
    scanf("%d", &n);
    for(int i=0;i<n;i++) {
    
    
        scanf("%d", &arr[i]);
    }
    QuickSortNotR(arr, 0, n-1);
    for(int i=0;i<n;i++) {
    
    
        printf("%d:%d\n", i, arr[i]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ISs_Cream/article/details/109903618