1.排序问题
现有一个含有N个数字的数组S,如何通过程序把这个数组变成有序的数组?
例如:
排序前:S:5,3,7,5,9,4,1,100,50
排序后:S:1,3,4,5,5,7,9,50,100
2.归并排序(merge sort)
不同于希尔排序,这里将介绍归并排序。
百度百科的定义:归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
简单介绍:
我们将使用一个与目标数组同等大小的辅助数组。
如果一个数组前半部分和后半部分都是有序的,那么,我们可以将这个数组复制给辅助数组,然后逐一对比前半部分和后半部分的元素并把较小的元素放到原数组里,那么我们就可以得到一个有序的数组。
如下图所示:
a[]是一个前半部分和后半部分都是有序的数组。
这也是一个前半部分和后半部分都是有序的数组,将数组a[]复制给辅助数组aux[];
令i=lo;j=mid+1;
然后对比i项元素和j项元素。结果i项元素更小,放回原数组,即a[0]=aux[i], 并且i++。
此时i=lo+1, 对比aux[i]和aux[j],结果i项元素更小,放回原数组,即a[1]=aux[i], 并且i++。
此时i=lo+2, 对比aux[i]和aux[j],结果j项元素更小,放回原数组,即a[2]=aux[j], 并且j++。
如此类推,直到前半部分或者后半部分对比完,再把另外一部分按顺序赋值到a[]。结果a[]变成了有序数组。
此例子中,前半部分先排完,后半部分留下了S和T。故a[8]=S;a[9]=T。
然后,如何把一个无序的数组变成前半部分和后半部分都是有序的数组呢?
如图所示,我们只要不停地对半划分数组,最后的子数组便只有两个元素。这个子数组就是前半部分和后半部分都是有序的数组!
然后用归并排序,从下而上地逐一排序(即排完两个相邻的D(2),把这两个D(2)看成D(4)的前半部分和后半部分;然后两个D(4)组成D(8),由此类推得到D(N)),最终得到一个有序的数组。
代码实现:
.h: UCLASS() class ALGORITHM_API AMergesort : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties AMergesort(); // Called every frame virtual void Tick(float DeltaTime) override; //生成数组 void InitArray(int N); //检测这部分的数组是否有序 void IsSorted(int MinIndex, int MaxIndex); //归并排序(MergeSort),输入辅助数组及此部分数组的最小id、中间Id、最大id void Merge(TArray<int> AuArray, int lo, int mid, int hi); //开始排序 void Sort(); void Sort(TArray<int> AuArray, int lo, int hi); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; public: //目标数组 TArray<int> MyIntArray; }; .cpp: // Sets default values AMergesort::AMergesort() { // 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 AMergesort::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 AMergesort::Tick(float DeltaTime) { Super::Tick(DeltaTime); } //生成随机数组 void AMergesort::InitArray(int N) { FRandomStream Stream; Stream.GenerateNewSeed(); for (int i = 0; i < N; i++) { MyIntArray.Add(Stream.RandRange(0, 100)); } } void AMergesort::IsSorted(int MinIndex, int MaxIndex) { //这部分数组应该在整个数组之内 if (MaxIndex > MyIntArray.Num() || MinIndex > MyIntArray.Num()) return; //检测数组 for (int i = MinIndex; i < MaxIndex - 2; i++) { //如果有序,前一项应该小于等于后一项 if (MyIntArray[i] > MyIntArray[i + 1]) { UKismetSystemLibrary::PrintString(this, " Error: UnOrder"); return; } } } //合并部分数组 void AMergesort::Merge(TArray<int> AuArray, int lo, int mid, int hi) { //先检测前半段和后半段是否都是有序的 IsSorted(lo, mid); IsSorted(mid + 1, hi); //把这一部分数组拷贝给辅助数组 for (int k = lo; k <= hi; k++) { AuArray[k] = MyIntArray[k]; } //前半段和后半段的起始序号 int i = lo; int j = mid + 1; for (int k = lo; k <= hi; k++) { //如果前半段已经没有了,则把后半段直接加上去 //PS:MyIntArray[k] = AuArray[j++]等效于MyIntArray[k] = AuArray[j]; j++; if (i > mid) MyIntArray[k] = AuArray[j++]; //如果后半段已经没有了,则把前半段直接加上去 else if (j > hi) MyIntArray[k] = AuArray[i++]; //如果i项元素比j项元素大,则取j项元素 else if (AuArray[i] > AuArray[j]) MyIntArray[k] = AuArray[j++]; //否则取i项元素 else MyIntArray[k] = AuArray[i++]; } //检查数组排序有没问题 IsSorted(lo, hi); } //开始排序 void AMergesort::Sort() { //创造一个与目标数组同等大小的辅助数组 TArray<int> AuArray; AuArray.Init(0, MyIntArray.Num()); Sort(AuArray, 0, MyIntArray.Num() - 1); } void AMergesort::Sort(TArray<int> AuArray, int lo, int hi) { if (hi <= lo) return; //处于中间的元素的ID int mid = lo + (hi - lo) / 2; //把前半段不停地分割成两部分,然后从最小的部分开始合并,最后得到有序的前半部分 Sort(AuArray, lo, mid); //把后半段不停地分割成两部分,然后从最小的部分开始合并,最后得到有序的后半部分 Sort(AuArray, mid + 1, hi); //最后把前半部分和后半部分合并,得到有序的数组,排序完成 Merge(AuArray, lo, mid, hi); }
.