修改此快速排序以始终使用最后一个元素作为基准

发布于 2024-08-22 12:44:08 字数 2056 浏览 5 评论 0原文

我有以下 Quicksort ,它始终选择子序列的第一个元素作为其枢轴:

void qqsort(int array[], int start, int end) {
    int i = start; // index of left-to-right scan
    int k = end; // index of right-to-left scan

    if (end - start >= 1) { // check that there are at least two elements to sort 
        int pivot = array[start]; // set the pivot as the first element in the partition
        while (k > i) { // while the scan indices from left and right have not met, 
            while (array[i] <= pivot && i <= end && k > i)  // from the left, look for the first element greater than the pivot
                i++;
            while (array[k] > pivot && k >= start && k >= i) // from the right, look for the first element not greater than the pivot
                k--;
            if (k > i) // if the left seekindex is still smaller than the right index, swap the corresponding elements
                swap(array, i, k);              
        }
        swap(array, start, k); // after the indices have crossed, swap the last element in the left partition with the pivot 
        qqsort(array, start, k - 1); // quicksort the left partition
        qqsort(array, k + 1, end); // quicksort the right partition
    } else { // if there is only one element in the partition, do not do any sorting 
        return;
    }
}

现在,如您所见,该算法始终将第一个元素作为主元: int hub = array[start];

我想修改此算法,使其始终使用最后一个元素而不是子序列的第一个元素,因为我想分析两种实现的物理运行时间。

我尝试将行 intivot = array[start]; 更改为 intivot=array[end]; 但算法随后输出了未排序的序列:

//Changes: int pivot = array[end]; 
unsorted: {5 4 3 2 1}
*sorted*: {1 2 5 4 3}

要测试另一个数据透视表,我也尝试使用子序列的中心元素,但算法仍然失败:

//Changes: int pivot = array[(start + end) / 2]; 
unsorted: {5 3 4 2 1}
*sorted*: {3 2 4 1 5}

有人可以帮助我正确理解这个算法,并告诉我需要做哪些更改才能成功地让这个实现始终选择子序列的最后一个元素作为枢?

I have the following Quicksort that always chooses the first element of the subsequence as its pivot:

void qqsort(int array[], int start, int end) {
    int i = start; // index of left-to-right scan
    int k = end; // index of right-to-left scan

    if (end - start >= 1) { // check that there are at least two elements to sort 
        int pivot = array[start]; // set the pivot as the first element in the partition
        while (k > i) { // while the scan indices from left and right have not met, 
            while (array[i] <= pivot && i <= end && k > i)  // from the left, look for the first element greater than the pivot
                i++;
            while (array[k] > pivot && k >= start && k >= i) // from the right, look for the first element not greater than the pivot
                k--;
            if (k > i) // if the left seekindex is still smaller than the right index, swap the corresponding elements
                swap(array, i, k);              
        }
        swap(array, start, k); // after the indices have crossed, swap the last element in the left partition with the pivot 
        qqsort(array, start, k - 1); // quicksort the left partition
        qqsort(array, k + 1, end); // quicksort the right partition
    } else { // if there is only one element in the partition, do not do any sorting 
        return;
    }
}

Now as you can see, this algorithm always takes the first element to be the pivot: int pivot = array[start];

I want to modify this algorithm to make it always use the last element instead of the first element of the subsequence, because I want to analyze the physical running times of both implementations.

I tried changing the line int pivot = array[start]; to int pivot = array[end]; but the algorithm then outputted an unsorted sequence:

//Changes: int pivot = array[end]; 
unsorted: {5 4 3 2 1}
*sorted*: {1 2 5 4 3}

To test another pivot, I also tried using the center element of the subsequence but the algorithm still failed:

//Changes: int pivot = array[(start + end) / 2]; 
unsorted: {5 3 4 2 1}
*sorted*: {3 2 4 1 5}

Can someone please help me understand this algorithm correctly and tell me what changes do I need to make to successfully have this implementation always choose the last element of the subsequence as the pivot?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

羞稚 2024-08-29 12:44:08

问题的原因

问题是您使用了int k = end;。当您将枢轴元素作为数组中的第一个元素时,使用 int i = start; 就可以了,因为循环中的检查将略过它(array[i] array[i] array[i] = 枢轴)。但是,当您使用最后一个元素作为枢轴时,k 会在结束索引处停止并将枢轴切换到分区左半部分的位置。您已经遇到麻烦了,因为您的枢轴很可能位于左侧分区内部的某个位置而不是边界。

解决方案

要解决此问题,当您使用最右边的元素作为基准时,需要设置int k = end - 1;。您还需要更改用于将枢轴交换到左侧和右侧分区之间的边界的行:

swap(array, i, end);
qqsort(array, start, i - 1);
qqsort(array, i + 1, end);

为此,您必须使用 i ,因为 i 将最终位于右侧分区的最左侧元素(然后可以将其与枢轴位于最右边的元素中,它将保留顺序)。最后,您需要将 k >= i 更改为 k > i 在同时递减 k 的同时,否则数组 [-1] 索引错误会发生微小变化。这在以前是不可能发生的,因为此时 i 始终至少等于 i+1。

应该可以做到这一点。

旁注:

这是一个写得不好的快速排序,我不建议学习。它有一些无关的、不必要的比较以及其他一些我不会浪费时间列出的错误。我建议使用 Sedgewick 和 Bentley 的本演示文稿中的快速排序。

The Cause of the Problem

The problem is that you use int k = end;. It was fine to use int i = start; when you had the pivot element as the first element in the array because your checks in the loop will skim past it (array[i] <= pivot). However, when you use the last element as the pivot, k stops on the end index and switches the pivot to a position in the left half of the partition. Already you're in trouble because your pivot will most likely be somewhere inside of the left partition rather than at the border .

The Solution

To fix this, you need to set int k = end - 1; when you use the rightmost element as the pivot. You'll also need to change the lines for swapping the pivot to the border between the left and right partitions:

swap(array, i, end);
qqsort(array, start, i - 1);
qqsort(array, i + 1, end);

You have to use i for this because i will end up at the leftmost element of the right partition (which can then be swapped with the pivot being in the rightmost element and it will preserver the order). Lastly, you'll want to change k >= i to k > i in the while which decrements k or else there is small change of an array[-1] indexing error. This wasn't possible to happen before because i always at least was equal to i+1 by this point.

That should do it.

Sidenote:

This is a poorly written quicksort which I wouldn't recommend learning from. It has a some extraneous, unnecessary comparisons along with some other faults that I won't waste time listing. I would recommend using the quicksorts in this presentation by Sedgewick and Bentley.

眼泪淡了忧伤 2024-08-29 12:44:08

我没有测试它,但无论如何检查一下:

// after the indices have crossed,
// swap the last element in the left partition with the pivot
swap(array, start, k);

可能应该是

swap(array, end, i);

如果我们选择 end 作为枢纽,

或类似的东西。 编辑:这是一种有趣的分区算法,但它不是标准算法。

嗯,枢轴在分区逻辑中是固定的。
该算法将第一个元素视为 Head,其余元素视为要分区的 Body。
分区完成后,作为最后一步,头部(枢轴)与左侧分区部分的最后一个元素交换,以保持顺序。

我想在不改变算法的情况下使用不同的枢轴的唯一方法是:

...
if (end - start >= 1) {
    // Swap the 1st element (Head) with the pivot
    swap(array, start, pivot_index);
    int pivot = array[start];
...

I didn't test it, but check it anyway:

this

// after the indices have crossed,
// swap the last element in the left partition with the pivot
swap(array, start, k);

probably should be

swap(array, end, i);

or something similar, if we choose end as pivot.

Edit: That's an interesting partitioning algorithm, but it's not the standard one.

Well, the pivot is fixed in the logic of the partitioning.
The algorithm treats the first element as the Head and the rest elements as the Body to be partitioned.
After the partitioning is done, as a final step, the head (pivot) is swapped with the last element of the left partitioned part, to keep the ordering.

The only way I figured to use a different pivot, without changing the algorithm, is this:

...
if (end - start >= 1) {
    // Swap the 1st element (Head) with the pivot
    swap(array, start, pivot_index);
    int pivot = array[start];
...
天冷不及心凉 2024-08-29 12:44:08

第一个提示:如果数据是随机的,那么平均而言,选择哪个值作为主元并不重要。真正提高枢轴“质量”的唯一方法是采用更多(例如 3)个指数并使用其中值的中值。

第二个提示:如果更改主元值,还需要更改主元索引。这没有明确命名,但 array[start] 在某一点被交换到已排序子序列的“中间”。您需要相应地修改这一行。如果您采用的索引不在子序列的边缘,则需要在迭代之前先将其交换到边缘。

第三个提示:您提供的代码注释过多。您应该能够真正理解这个实现。

First hint: If the data are random, it does not matter, on the average, which value you choose as pivot. The only way to actually improve the "quality" of the pivot is to take more (e.g. 3) indices and use the one with median value of these.

Second hint: If you change the pivot value, you also need to change the pivot index. This is not named explicitly, but array[start] is swapped into the "middle" of the sorted subsequence at one point. You need to modify this line accordingly. If you take an index which is not at the edge of the subsequence, you need to swap it to the edge first, before the iteration.

Third hint: The code you provided is excessively commented. You should be able to actually understand this implementation.

执笏见 2024-08-29 12:44:08

放置一个

swap(array, start, end)

在初始化枢轴之前

int pivot = array[start]

Put a single

swap(array, start, end)

before initializing pivot

int pivot = array[start]
夏の忆 2024-08-29 12:44:08
#include <time.h>
#include <stdlib.h>
#include<iostream>
#include<fstream>
using namespace std;

int counter=0;
void disp(int *a,int n)
{
for(int i=0;i<n;i++)
cout<<a[i]<<" ";
cout<<endl;
}

void swap(int a[],int p,int q)
{

int temp;
temp=a[p];
a[p]=a[q];
a[q]=temp;

}

int partition(int a[], int p, int start, int end)
{
swap(a,p,start);// to swap the pivot with the first element of the partition
counter+=end-start;  // instead of (end-start+1)
int i=start+1;
for(int j=start+1 ; j<=end ; j++)
{
    if(a[j]<a[start])
    {
        swap(a,j,i);
        i++;

    }


}
swap(a,start,i-1);  // not swap(a,p,i-1) because p and start were already swaped.....    this was the earlier mistake comitted
return i-1; // returning the adress of pivot
}

void quicksort(int a[],int start,int end)
{
if(start>=end)
    return;


int p=end; // here we are choosing last element of the sub array as pivot
// here p is the index of the array where pivot is chosen randomly

int index=partition(a,p,start,end);

quicksort(a,start,index-1);
quicksort(a,index+1,end);

}

int main()
{

ifstream fin("data.txt");
int count=0;

int array[100000];

while(fin>>array[count])
{
    count++;
}
quicksort(array,0,count-1);
/*
int a[]={32,56,34,45,23,54,78};
int n=sizeof(a)/sizeof(int);
disp(a,n);

quicksort(a,0,n-1);
disp(a,n);*/
cout<<endl<<counter;
return 0;

}
#include <time.h>
#include <stdlib.h>
#include<iostream>
#include<fstream>
using namespace std;

int counter=0;
void disp(int *a,int n)
{
for(int i=0;i<n;i++)
cout<<a[i]<<" ";
cout<<endl;
}

void swap(int a[],int p,int q)
{

int temp;
temp=a[p];
a[p]=a[q];
a[q]=temp;

}

int partition(int a[], int p, int start, int end)
{
swap(a,p,start);// to swap the pivot with the first element of the partition
counter+=end-start;  // instead of (end-start+1)
int i=start+1;
for(int j=start+1 ; j<=end ; j++)
{
    if(a[j]<a[start])
    {
        swap(a,j,i);
        i++;

    }


}
swap(a,start,i-1);  // not swap(a,p,i-1) because p and start were already swaped.....    this was the earlier mistake comitted
return i-1; // returning the adress of pivot
}

void quicksort(int a[],int start,int end)
{
if(start>=end)
    return;


int p=end; // here we are choosing last element of the sub array as pivot
// here p is the index of the array where pivot is chosen randomly

int index=partition(a,p,start,end);

quicksort(a,start,index-1);
quicksort(a,index+1,end);

}

int main()
{

ifstream fin("data.txt");
int count=0;

int array[100000];

while(fin>>array[count])
{
    count++;
}
quicksort(array,0,count-1);
/*
int a[]={32,56,34,45,23,54,78};
int n=sizeof(a)/sizeof(int);
disp(a,n);

quicksort(a,0,n-1);
disp(a,n);*/
cout<<endl<<counter;
return 0;

}
小姐丶请自重 2024-08-29 12:44:08

如果您开始监视从数组的第一个元素到最后一个 - 1 的每个元素,并在每次递归时将最后一个元素保留为枢轴,那么您将在精确的 O(nlogn) 时间内得到答案。

#include<stdio.h>
void quicksort(int [], int, int);
int main()
{
int n, i = 0, a[20];
scanf("%d", &n);
while(i < n)
scanf("%d", &a[i++]);

quicksort(a, 0, n - 1);
i = 0;
while(i < n)
printf("%d", a[i++]);

}
void quicksort(int a[], int p, int r)
{
int i, j, x, temp;
if(p < r)
{
    i = p;
    x = a[r];
    for(j = p; j < r; j++)
    {
        if(a[j] <= x)
        {
            if(a[j] <a[i])
            {
                temp = a[j];
                a[j] = a[i];
                a[i] = temp;
            }
            i++;
        }

        else
        {
            temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
    }
    if(x != i)
    {
        temp = a[r];
        a[r] = a[i];
        a[i] = temp;
    }

    quicksort(a, p, i - 1);
    quicksort(a, i + 1, r);
}
}

If you start monitoring each element from the 1st element of the array to the last - 1, keeping the last element as the pivot at every recursion, then you will get the answer in exact O(nlogn) time.

#include<stdio.h>
void quicksort(int [], int, int);
int main()
{
int n, i = 0, a[20];
scanf("%d", &n);
while(i < n)
scanf("%d", &a[i++]);

quicksort(a, 0, n - 1);
i = 0;
while(i < n)
printf("%d", a[i++]);

}
void quicksort(int a[], int p, int r)
{
int i, j, x, temp;
if(p < r)
{
    i = p;
    x = a[r];
    for(j = p; j < r; j++)
    {
        if(a[j] <= x)
        {
            if(a[j] <a[i])
            {
                temp = a[j];
                a[j] = a[i];
                a[i] = temp;
            }
            i++;
        }

        else
        {
            temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
    }
    if(x != i)
    {
        temp = a[r];
        a[r] = a[i];
        a[i] = temp;
    }

    quicksort(a, p, i - 1);
    quicksort(a, i + 1, r);
}
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文