快速排序改進——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);
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM