1.排序问题
现有一个含有N个数字的数组S,如何通过程序把这个数组变成有序的数组?
例如:
排序前:S:5,3,7,5,9,4,1,100,50
排序后:S:1,3,4,5,5,7,9,50,100
2.快速排序(Quicksort)
简单介绍:
快速排序内含一道重要的工序:分区(Partition)。这里将先介绍分区,然后介绍排序方法。
分区:
对于一个数组,将它的第一个元素设为V,令i=2,j为数组的最后一个元素的序号(index)。
1.先从i开始比较,如果i项元素比V小,则i++;否则停止比较,此时的i项元素大于等于V。
2.然后从j开始比较,如果j项元素比V大,则j--;否则停止比较,此时的i项元素小于等于V。
3.如果i<j,说明i项元素和j项元素之间还有元素,将i项元素和j项元素互换,然后i++,j--,然后重复1,2,3步骤,直到i和j重合(即i=j)。
4.将J项元素和V互换。这样就得到了一个分好区的数组:V的左端的元素都小于等于V;V的右端都大于等于V。
排序:
1.将整个数组进行分区。
2.对分区得到的两个部分数组分别进行分区,不停的分区下去,直到分区数组只有一个元素为止。
3.排序完成。
3.快速排序的优缺点
优点:
不需要额外空间(归并排序需要一个额外数组),速度比归并排序快。
缺点:
1.如果数组是递增的有序数组,对它用快速排序需要N2/2次操作。
2.No Stable:如果数组已经按一种排序方式排成有序了,然后再按另一种排序方式用快速排序对此数组排序,则会打乱第一种排序。(例如手机通讯录先按地区排了一次序,再按名字排一次序。)PS:归并排序是Stable的。
4.快速排序具体代码实现:
.h: UCLASS() class ALGORITHM_API AQuicksort : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties AQuicksort(); // Called every frame virtual void Tick(float DeltaTime) override; //生成数组 void InitArray(int N); //把此部分数组分为两半,中间分界线为v,左半部分的数字都比v小,右半部分的数字都比v大 int Partition(int lo, int hi); //更换数组里两个数字 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 AQuicksort::AQuicksort() { // 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 AQuicksort::BeginPlay() { Super::BeginPlay(); //测试 //生成数组 InitArray(10000); 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 AQuicksort::Tick(float DeltaTime) { Super::Tick(DeltaTime); } void AQuicksort::InitArray(int N) { FRandomStream Stream; Stream.GenerateNewSeed(); for (int i = 0; i < N; i++) { MyIntArray.Add(Stream.RandRange(0, 100)); } } int AQuicksort::Partition(int lo, int hi) { int i(lo); //因为后面会--j,而我们需要从hi项开始比较,所以这里j=hi+1 int j(hi + 1); //只要中途不break,循环一直进行下去 while (true) { //lo项作为比较用的元素,从lo+1开始比较,直到找到大于等于lo项的元素,才停止(除非lo项是此部分数组中最大的,此时i=hi) //停止时,i项元素比lo项大 //注意:MyIntArray[++i] < MyIntArray[lo]中,先进行++i,再进行比较 while (MyIntArray[++i] < MyIntArray[lo]) { if (i == hi) break; } //从hi项开始比较,直到找到小于等于lo项的元素,才停止(除非lo项是此部分数组中最小的,此时i=lo) //注意:MyIntArray[lo]<MyIntArray[--j]中,先进行--j,再进行比较 while (MyIntArray[lo]<MyIntArray[--j]) { if (j == lo) break; } //如果i >= j,说明i,j重合了,不需要继续循环。此时,i项左边的元素都比lo项小;右边的元素都比lo项大 if (i >= j) break; //如果i和j之间还有元素,说明还没比较完,继续比较 ExChange(i, j); } //lo项是分界线元素,把它放到分界线处 ExChange(lo, j); //返回分界线元素的Index return j; } void AQuicksort::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 AQuicksort::Sort() { Sort(0, MyIntArray.Num() - 1); } void AQuicksort::Sort(int lo, int hi) { if (hi <= lo) return; //把此部分数组分为两部分 int j = Partition(lo, hi); //然后这两部分数组作为新的部分数组继续分下去,直到hi <= lo Sort(lo, j - 1); Sort(j + 1, hi); }