当前位置: 首页 > article >正文

大一计算机的自学总结:随机快速排序及随机快速算法

前言

和归并排序一样,随机快速排序的时间复杂度也是O(n*logn)。

一、随机快速排序

随机快速排序的特点就是“随机”,通过随机选择一个数,从而可以满足在任何情况下时间复杂度都为O(n*logn)。

注意,若过程中选择的数不随机,时间复杂度会根据数据的不同而改变。

1.未优化版

#include<bits/stdc++.h>
using namespace std;

//全局变量
#define n 10
int arr[n]; 

//划分
int partition(int l,int r,int x)
{
	int a=l,xi=0;
	for(int i=l;i<=r;i++)
	{
		if(arr[i]<=x)
		{
			int t=arr[a];
			arr[a]=arr[i];
			arr[i]=t;
			
			if(arr[a]==x)
			{
				xi=a;	
			}
			a++;
		}
	}
	int t=arr[xi];
	arr[xi]=arr[a-1];
	arr[a-1]=t;
	
	return a-1;
} 

//随机快速排序 ——未优化版 
void quickSortUnOptimized(int l,int r)
{
	if(l>=r)
		return ;
	
	//设置[a,b]内随机数 -> rand()%(b-a+1)+a
	srand(time(0));
	int x=arr[rand()%(r-l+1)+l];
	
	int mid=partition(l,r,x);//找x下标并划分
	quickSortUnOptimized(l,mid-1);
	quickSortUnOptimized(mid+1,r); 
} 

int main()
{
	for(int i=0;i<n;i++)
	{
		cin>>arr[i];
	}
	
	//随机快速排序 
	quickSortUnOptimized(0,n-1);
	
	cout<<"Sorted:"<<endl;
	for(int i=0;i<n;i++)
	{
		cout<<arr[i]<<" ";
	}
	
	return 0;
}

对于随机快速排序,也可以使用调递归的方法,只需要注意其中随机生成数的代码。

重点是partition函数,通过这个随机出来的数x,将数组划分成小于等于和大于x的部分。通过不断划分,实现整个排序过程。

首先让a=l,用a来表示小于等于x的边界,然后遍历整个从l到r的部分,若小于等于x,就让该位置的数和a位置的数交换,若等于x,则记一下此时的下标,然后让边界a往下移。最后,将xi位置和边界a位置交换,保证等于x的数位于边界处并返回x的位置。

2.优化版——荷兰国旗问题

可以看出,未优化版中,若有多个等于x的数,则一次划分只能选出一个,不够简便。

#include<bits/stdc++.h>
using namespace std;

//全局变量
#define n 10
int arr[n]; 
int a,b;//左右范围

//划分 ——荷兰国旗问题
void partition(int l,int r,int x)
{
	a=l;
	b=r;
	int i=l;
	
	while(i<=b)
	{
		if(arr[i]==x)
		{
			i++;
		}
		else if(arr[i]<x)
		{
			int t=arr[a];
			arr[a]=arr[i];
			arr[i]=t;
			a++;
			i++;
		}
		else if(arr[i]>x)
		{
			int t=arr[b];
			arr[b]=arr[i];
			arr[i]=t;
			b--;
		}
	}
} 

//随机快速排序 ——优化版 
void quickSortOptimized(int l,int r)
{
	if(l>=r)
		return ; 
		
	srand(time(0));
	int x=arr[rand()%(r-l+1)+l];
	
	partition(l,r,x);
	int left=a;
	int right=b;//防止修改a,b值 
	quickSortOptimized(l,left-1);
	quickSortOptimized(right+1,r);
}

int main()
{
	for(int i=0;i<n;i++)
	{
		cin>>arr[i];
	}
	
	//随机快速排序 
	quickSortOptimized(0,n-1);
	
	cout<<"Sorted:"<<endl;
	for(int i=0;i<n;i++)
	{
		cout<<arr[i]<<" ";
	}
	
	return 0;
}

优化之后的划分函数会将数组划分成小于x的部分、等于x的部分和大于x的部分。由于这种划分成三段的方式很像荷兰国旗,所以这个问题就叫“荷兰国旗问题”。

重点依旧是划分函数,为了返回两个边界,所以考虑设置全局变量a,b,每次让a=l,b=r,遍历整个部分,若小于x则交换,并让a++,i++;若等于则只需要让i++;若大于,交换后只让b--,因为此时交换过来的数不清楚大小,需要i维持原位置再比较一次。

注意,为了防止改变两边界的值,需要额外设置left和right两变量。

二、随机快速算法——数组中的第K个最大元素

class Solution {
public:
    int a,b;

    void swap(vector<int>&nums,int x,int y)
    {
        int t=nums[x];
        nums[x]=nums[y];
        nums[y]=t;
    }

    void partition(vector<int>&nums,int l,int r,int x)
    {
        a=l;
        b=r;
        int i=l;
        while(i<=b)
        {
            if(nums[i]==x)
            {
                i++;
            }
            else if(nums[i]>x)
            {
                swap(nums,i,b);
                b--;
            }
            else
            {
                swap(nums,i++,a++);
            }
        }
    }

    int solve(vector<int>&nums,int i)
    {
        int ans=0;
        for(int l=0,r=nums.size()-1;l<=r;)
        {
            srand(time(0));
            int x=nums[rand()%(r-l+1)+l];

            partition(nums,l,r,x);
            if(i<a)
            {
                r=a-1;
            }
            else if(i>b)
            {
                l=b+1;
            }
            else 
            {
                ans=nums[i];
                break;
            }
        }
        return ans;
    }

    int findKthLargest(vector<int>& nums, int k) {
        return solve(nums,nums.size()-k);
    }
};

 因为要求时间复杂度为O(n),所以就不能排序。

考虑第k大的这个条件,即找数组第n-k位置的数,此时可以使用荷兰国旗的划分方法和二分搜索的思想,即划分后可以确定一侧必有一侧必没有

划分函数如上,主函数只要一直循环,每次通过比较n-k位置和边界a,b从而确定去到哪一边继续。

总结

随机快速排序最显著的特点就是随机选数,可以将面对所有数据情况的时间复杂度全变为O(n*logn)。而随机快速算法就是在这一点的基础上,加入二分搜索的思想,在划分后确定一次必有和一侧必没有。

END


http://www.kler.cn/a/514894.html

相关文章:

  • 学习一下强化学习
  • C语言之整数转换英文表示
  • 机器学习(6):K 近邻算法
  • VirtualBox can‘t enable the AMD-V extension
  • 扬帆数据结构算法之雅舟航程,漫步C++幽谷——LeetCode刷题之移除链表元素、反转链表、找中间节点、合并有序链表、链表的回文结构
  • 剑指Offer|LCR 040.最大矩形
  • Solidity06 Solidity变量数据存储和作用域
  • 安装centos7之后问题解决
  • 根除埃博拉病毒(2015MCM美赛A)
  • 嵌入式入门(一)-STM32CubeMX
  • c++中的链表list
  • 【Android】创建基类BaseActivity和BaseFragment
  • Spring注解篇:@RestController详解
  • AI大模型-提示工程学习笔记11-思维树
  • 【线性代数】列主元法求矩阵的逆
  • 云原生架构下的AI智能编排:ScriptEcho赋能前端开发
  • 2025_1_22_进程替换
  • Simula语言的云计算
  • C语言进阶习题【1】指针和数组(4)——指针笔试题3
  • RabbitMQ的消息可靠性保证