• 快速排序改进——3区快速排序(3-way quicksort)


    1.快速排序缺陷

      快速排序面对重复的元素时的处理方法是,把它放在了左部分数组或右部分数组,下次进行分区时,还需检测它。如果需要排序的数组含有大量重复元素,则这个问题会造成性能浪费。

      解决方法:新增一个相同区域,并把重复元素放进去,下次进行分区时,不对相同区域进行分区。

    2. 3区快速排序(3-way quicksort)

      从例子入手:

      

      现有数组A[]如上图。

      令int lt=0; int i =1; int gt=5;

      首先从A[0](53)开始  

      A[lt]与A[i]进行对比,结果:A[0]<A[1], 交换A[i]与A[gt], gt减一:

      

      A[lt]与A[i]进行对比,结果:A[0]>A[1], 交换A[i]与A[lt], lt加一,i加一:

       

      A[lt]与A[i]进行对比,结果:A[1]>A[2],交换A[i]与A[lt], lt加一,i加一:

      

      A[lt]与A[i]进行对比,结果:A[2]=A[3],i加一:

      

      A[lt]与A[i]进行对比,结果:A[1]>A[2],交换A[i]与A[lt], lt加一,i加一:

      

      i>gt,第一次排序结束。

      此时,整个数组分为3个区:

      第一个区里的所有数字都比53小,它含有a[0]~a[2];

      第二个区里的所有数字都等于53,它含有a[3]~a[4];

      第三个的所有数字都比53大,它含有a[5];

      我们还需要对第一个区和第三个区进行3区快速排序(因为这两个区里的数字可能还是乱的,虽然在本例中顺序是对的)

      从第一个区的第一个数字20开始:

      令int lt=0; int i =1; int gt=2;

      

      A[lt]与A[i]进行对比,结果:A[2]=A[3],i加一:

      

      A[lt]与A[i]进行对比,结果:A[0]<A[2], 交换A[i]与A[gt], gt减一:(这里i=gt,所以等于没交换)

       

      i>gt,第二次排序结束。

      此时,整个区分为3个区:

      第一个区里的所有数字都比20小,它没有元素;

      第二个区里的所有数字都等于20,它含有a[0]~a[1];

      第三个的所有数字都比20大,它含有a[2];

      对第一个区和第三个区进行3区快速排序,但第一个区没元素,不用排;第三个区只有一个元素,不用排。

      

      对下一个区进行3区快速排序,但此区只有A[5]一个元素,不用排;

      没有下一个区了,排序结束。

      总结一下:

      对于一个数组A[],令lt=0;i=1,gt为数组的最后一个元素的序号(index)。

      1.从A[lt]开始,如果A[lt]>A[i],交换lt项元素和i项元素,lt++,i++;如果A[lt]=A[i], i++;如果A[lt]<A[i],交换gt项元素和i项元素,gt--。

      2.当i>gt时,数组已经分好3个区域了。

      3.对大于A[lt]的元素区域和小于A[lt]的元素区域分别进行3区快速排序,直到分区数组只有一个元素为止。

    3.实现代码

    .h:
    
    UCLASS()
    class ALGORITHM_API AThreeWayQuicksort : public AActor
    {
        GENERATED_BODY()
        
    public:    
        // Sets default values for this actor's properties
        AThreeWayQuicksort();
        // Called every frame
        virtual void Tick(float DeltaTime) override;
    
        //生成数组
        void InitArray(int N);
        //更换数组里两个数字
        void ExChange(int i, int j);
        //开始排序
        void Sort();
        void Sort(int lo, int hi);
    protected:
        // Called when the game starts or when spawned
        virtual void BeginPlay() override;
    
    public:    
        
    
    private:
        TArray<int> MyIntArray;
    };
    
    .cpp:
    
    // Sets default values
    AThreeWayQuicksort::AThreeWayQuicksort()
    {
         // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
        PrimaryActorTick.bCanEverTick = true;
    }
    
    // Called when the game starts or when spawned
    void AThreeWayQuicksort::BeginPlay()
    {
        Super::BeginPlay();
        //测试
        //生成数组
        InitArray(1000);
        UKismetSystemLibrary::PrintString(this, "Before Sort: ");
        for (int i = 0; i < MyIntArray.Num(); i++)
        {
            UKismetSystemLibrary::PrintString(this, FString::FromInt(i) + " : " + FString::FromInt(MyIntArray[i]));
        }
        //开始排序
        Sort();
        UKismetSystemLibrary::PrintString(this, "After Sort: ");
        for (int i = 0; i < MyIntArray.Num(); i++)
        {
            UKismetSystemLibrary::PrintString(this, FString::FromInt(i) + " : " + FString::FromInt(MyIntArray[i]));
        }
    }
    
    // Called every frame
    void AThreeWayQuicksort::Tick(float DeltaTime)
    {
        Super::Tick(DeltaTime);
    }
    
    void AThreeWayQuicksort::InitArray(int N)
    {
        FRandomStream Stream;
        Stream.GenerateNewSeed();
        for (int i = 0; i < N; i++)
        {
            MyIntArray.Add(Stream.RandRange(0, 100));
        }
    }
    
    void AThreeWayQuicksort::ExChange(int i, int j)
    {
        //序号i,j应该在数组范围内
        if (i > MyIntArray.Num() - 1 || j > MyIntArray.Num() - 1) return;
        //互换
        int Tempint = MyIntArray[i];
        MyIntArray[i] = MyIntArray[j];
        MyIntArray[j] = Tempint;
    }
    
    void AThreeWayQuicksort::Sort()
    {
        Sort(0, MyIntArray.Num() - 1);
    }
    
    void AThreeWayQuicksort::Sort(int lo, int hi)
    {
        if (hi <= lo) return;
        //left是小于V和等于V的分界线
        int Left(lo);
        //Right是大于V和等于V的分界线
        int Right(hi);
        int V(MyIntArray[lo]);
        //i是等于V和未排序元素的分界线
        int i(lo);
        while (i <= Right)
        {
            //如果小于V,放在Left的左边(小于V的元素区间)
            if (MyIntArray[i] < V) ExChange(Left++, i++);
            //如果大于V,放在Right的右边(大于V的元素区间)
            else if (MyIntArray[i] > V) ExChange(i, Right--);
            //如果等于V,i++,相当于放在Left和i之间(等于V的元素区间)
            else i++;
        }
        //然后这两部分数组作为新的部分数组继续分下去,直到hi <= lo
        Sort(lo, Left - 1);
        Sort(Right + 1, hi);
    }
  • 相关阅读:
    php+mysql折线图
    转载] magento 产品数据表结构
    magento 开启模板路径提示
    magento smtp设置
    magento 搬家
    利用DIV+CSS制作网页过程中常用的基本概念及标签使
    使用CSS创建有图标的网站导航菜单
    在网页中使用H1标记的须注意的事项
    兼容firefox的iframe高度自适应代码
    jQuery: 图片不完全按比例自动缩小
  • 原文地址:https://www.cnblogs.com/mcomco/p/10138199.html
Copyright © 2020-2023  润新知