排序算法——快速排序

前提

最近在学习快排的知识,发现其核心操作哨兵划分还是有些许难度的,这里做个记录。

哨兵划分

这里先贴出快排的流程。

在这里插入图片描述
重点关注哨兵划分的过程。
哨兵划分就是对数组进行处理,将小于哨兵的元素移到哨兵的左侧,将大于哨兵的元素移到哨兵的右侧。在这里,哨兵为数组的首元素

在这里插入图片描述
如上所示,元素10小于哨兵2,所以要移动到哨兵左侧;元素4大于哨兵2,所以要移动到哨兵右侧。
那么如何编码实现这个过程呢?

解决方法1

仔细观察哨兵划分后的数组,以哨兵为分界,左边的元素都小于哨兵,右边的元素都大于哨兵,但原始数组是没有规律的。
一种可行的方法如下:

  1. 从数组左边开始找大于哨兵的元素;从数组右边开始找小于哨兵的元素;
  2. 交换两个元素的位置
  3. 重复上面的步骤,直到两边在中间相遇为止。
    在这里插入图片描述

从左边开始找第一个大于哨兵的元素是4,从右边开始找第一个小于哨兵的元素是0,所以交换40的位置。
然后再往下的话,两边在中间就相遇了。此时并没有结束,因为哨兵还没有移动到正确的位置,所以还需将哨兵和相遇位置处的元素进行交换(这里是1)。
在这里插入图片描述
整体流程如上所以,下面给出对应的代码。

void swap(vector<int> &nums, int i, int j) {
    int tmp = nums[i];
    nums[i] = nums[j];
    nums[j] = tmp;
}
int partition(vector<int>& nums, int left, int right)
{
    // 以nums[left] 为哨兵
    int i = left, j = right;
    while (i < j)
    {
        while (i < j)  // 从右边开始找
        {
            if (nums[j] < nums[left])
            {
                break; // 找到小于哨兵的元素,就跳出while
            }
            else
            {
                j--;   // 没有找到,就继续遍历
            }
        }
        while (i < j)  // 从左边开始找
        {
            if (nums[i] > nums[left])
            {
                break;  // 找到大于哨兵的元素,就跳出while
            }
            else
            {
                i++;  // 没有找到,就继续遍历
            } 
        }
        swap(nums, i, j); // 交换元素
    }
    swap(nums, i, left); // 交换哨兵和中间元素
    return i;  // 返回哨兵的索引
}

还可以优化下上面的代码:

void swap(vector<int> &nums, int i, int j) {
    int tmp = nums[i];
    nums[i] = nums[j];
    nums[j] = tmp;
}
int partition(vector<int>& nums, int left, int right)
{
    // 以nums[left] 为哨兵
    int i = left, j = right;
    while (i < j)
    {
        while (i<j && nums[j] >= nums[left])
        {
            j--;
        }
        while (i < j && nums[i]<= nums[left])
        {
            i++;
        }
        swap(nums, i, j); // 交换元素
    }
    swap(nums, i, left); // 交换哨兵和中间元素
    return i;  // 返回哨兵的索引
}

解决方法2

还有一种方法也可以解决哨兵划分问题。
伪代码如下:

pivot = left; // 哨兵的下标
index = left + 1; // 索引
for(遍历除哨兵外的所有元素)
	if(当前元素 < 哨兵)
	{
		swap(当前元素, nums[index]);
		index++;
	}

swap(哨兵, nums[index-1]);

这种方法就是先确定小于哨兵的元素,小于哨兵的元素确定好位置后,自然剩下的就是大于哨兵的元素。
代码如下:

void swap(vector<int> &nums, int i, int j) {
    int tmp = nums[i];
    nums[i] = nums[j];
    nums[j] = tmp;
}
int partition(vector<int>& nums, int left, int right)
{
    int index = left + 1;
    int pivot = left;  // 哨兵索引
    for (int i = left+1; i < right; i++)
    {
        if (nums[i] < nums[pivot])
        {
            swap(nums[i], nums[index]);
            index++;
        }
    }
    swap(nums[pivot], nums[--index]);
    return index;
}

参考链接

  1. https://www.hello-algo.com/chapter_sorting/quick_sort/#1151
  2. https://visualgo.net/zh/sorting
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值