1.排序问题

  现有一个含有N个数字的数组S,如何通过程序把这个数组变成有序的数组?

  例如:

  排序前:S:5,3,7,5,9,4,1,100,50

  排序后:S:1,3,4,5,5,7,9,50,100

2.解决方法A

  从第一项逐步读到最后一项。每读一项,便把它与之前读过的数字全部进行比较,并把它放在大小合适的位置(大于等于前一个,小于等于后一个)。

  这种解法固然可以解决问题,但是效率不够高。

  实现代码大概是这样子的:

 

3.解决方法B:希尔排序(ShellSort)

  为了高效地解决这个问题,这里引入希尔排序。

  与方法A思路类似,但此算法不是逐个读下去,而是跳着读。

  例如4个4个跳着读:

  即第一次排序第1,5,9,13项;第二次排序第2,6,10,14项;第三次排序第3,7,11,15项;第四次排序第4,8,12,16项;然后一个一个地排序。由于数组经过4个4个地排,变成了部分有序数组,此时再逐一排序,则会排得比方法A快(因为方法A是完全无序的,每次排都有可能要从头比较到尾)。

  那么,应该跳多少个来读下去呢?目前还是个数学难题,大家发现3X+1相对较快。以下的实现也是用3X+1。

  解决一开始提出的排序问题的代码实现:

.h: 

UCLASS()
class ALGORITHM_API AShellSort : public AActor
{
    GENERATED_BODY()
    
public:    
    // Sets default values for this actor's properties
    AShellSort();

    // Called every frame
    virtual void Tick(float DeltaTime) override;
    //生成数组
    void InitArray(int N);
    //更换数组里两个数字
    void ExChange(int i, int j);
    //比较数组里两个数字的大小
    bool Less(int i, int j);
    //希尔排序(ShellSort)
    void ShellSort();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:    
    
    TArray<int> MyIntArray;
};

.cpp:

AShellSort::AShellSort()
{
     // 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 AShellSort::BeginPlay()
{
    Super::BeginPlay();
    //测试
    //生成数组
    InitArray(100000);
    //排序前
    for (int i = 0; i < MyIntArray.Num(); i++)
    {
        UKismetSystemLibrary::PrintString(this, "Before: " + FString::FromInt(i)+" : "+FString::FromInt(MyIntArray[i]));
    }
    ShellSort();
    //排序后
    for (int i = 0; i < MyIntArray.Num(); i++)
    {
        UKismetSystemLibrary::PrintString(this, "After: " + FString::FromInt(i) + " : " + FString::FromInt(MyIntArray[i]));
    }
}

// Called every frame
void AShellSort::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

//生成随机数组
void AShellSort::InitArray(int N)
{
    FRandomStream Stream;
    Stream.GenerateNewSeed();
    for (int i = 0; i < N; i++)
    {
        MyIntArray.Add(Stream.RandRange(0, 100));
    }
}

void AShellSort::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;
}

bool AShellSort::Less(int i, int j) 
{
    //序号i,j应该在数组范围内
    if (i > MyIntArray.Num() - 1 || j > MyIntArray.Num() - 1) return false;
    //比较大小
    return MyIntArray[i] < MyIntArray[j];
}

void AShellSort::ShellSort()
{
    int N(MyIntArray.Num());
    int h(1);
    //用3x+1的间距分割数组,先找出最大的h
    while (h < N / 3) h = 3 * h + 1;
    //以h为间隔排序数组(h-sort)
    while (h >= 1)
    {
        for (int i = h; i < N; i++)
        {
            for (int j = i; j >= h && Less(j, j - h); j -= h)
            {
                ExChange(j, j - h);
            }
        }
        //寻找下一个h,直到h=1为止
        h = h / 3;
    }
}