快速排序
毫无疑问,快速排序是最流行的排序算法,因为有充足的理由,在大多数情况下,快速排序都是最快的,执行时间为O(N*logN)级,(这只是对内部排序或者说随机存储器内的排序而言,对于磁盘文件中的数据进行的排序,其他的排序算法可能更好)。
快速排序算法本质上通过把一个数组划分为两个子数组,然后递归的调用自身为每个子数组进行快速排序实现的。但是对这个基本的设计还需要进一步加工,算法还必须要选择枢纽以及对小的划分区域进行排序。
快速排序算法基本有三个步骤:
1、把数组或者子数组划分成左边(较小的关键字)的一组和右边(较大的关键字)的一组。
2、调用自身对左边的一组进行划分排序。
3、再次调用自身对右边的一组进行划分排序。
经过一次划分以后,所有左边子数组的数据项都小雨右边子数组的数据项,只要对左边子数组和右边数组分辨进行排序,整个数组就是有序的了,对子数组进行排序用递归调用排序算法自身即可。
在本例中选择最右边的数作为枢纽基值。
以下是实现代码(方式一):
// quickSort1.java
// demonstrates simple version of quick sort
// to run this program: C>java QuickSort1App
class ArrayIns
{
private long[] theArray; // ref to array theArray
private int nElems; // number of data items
//--------------------------------------------------------------
public ArrayIns(int max) // constructor
{
theArray = new long[max]; // create the array
nElems = 0; // no items yet
}
//--------------------------------------------------------------
public void insert(long value) // put element into array
{
theArray[nElems] = value; // insert it
nElems++; // increment size
}
//--------------------------------------------------------------
public void display() // displays array contents
{
System.out.print("A=");
for(int j=0; j<nElems; j++) // for each element,
System.out.print(theArray[j] + " "); // display it
System.out.println("");
}
//--------------------------------------------------------------
public void quickSort()
{
recQuickSort(0, nElems-1);
}
//--------------------------------------------------------------
public void recQuickSort(int left, int right)
{
if(right-left <= 0) // if size <= 1,
return; // already sorted
else // size is 2 or larger
{
long pivot = theArray[right]; // rightmost item
// partition range
int partition = partitionIt(left, right, pivot);
recQuickSort(left, partition-1); // sort left side
recQuickSort(partition+1, right); // sort right side
}
} // end recQuickSort()
//--------------------------------------------------------------
public int partitionIt(int left, int right, long pivot)
{
int leftPtr = left-1; // left (after ++)
int rightPtr = right; // right-1 (after --)
while(true)
{ // find bigger item
while( theArray[++leftPtr] < pivot )
; // (nop)
// find smaller item
while(rightPtr > 0 && theArray[--rightPtr] > pivot)
; // (nop)
if(leftPtr >= rightPtr) // if pointers cross,
break; // partition done
else // not crossed, so
swap(leftPtr, rightPtr); // swap elements
} // end while(true)
swap(leftPtr, right); // restore pivot
return leftPtr; // return pivot location
} // end partitionIt()
//--------------------------------------------------------------
public void swap(int dex1, int dex2) // swap two elements
{
long temp = theArray[dex1]; // A into temp
theArray[dex1] = theArray[dex2]; // B into A
theArray[dex2] = temp; // temp into B
} // end swap(
//--------------------------------------------------------------
} // end class ArrayIns
class QuickSort1App
{
public static void main(String[] args)
{
int maxSize = 16; // array size
ArrayIns arr;
arr = new ArrayIns(maxSize); // create array
for(int j=0; j<maxSize; j++) // fill array with
{ // random numbers
long n = (int)(java.lang.Math.random()*99);
arr.insert(n);
}
arr.display(); // display items
arr.quickSort(); // quicksort them
arr.display(); // display them again
} // end main()
} // end class QuickSort1App
选择枢纽的第二种方法(左中右三数据项取中):
以下是实现代码:
// quickSort2.java
// demonstrates quick sort with median-of-three partitioning
// to run this program: C>java QuickSort2App
class ArrayIns
{
private long[] theArray; // ref to array theArray
private int nElems; // number of data items
//--------------------------------------------------------------
public ArrayIns(int max) // constructor
{
theArray = new long[max]; // create the array
nElems = 0; // no items yet
}
//--------------------------------------------------------------
public void insert(long value) // put element into array
{
theArray[nElems] = value; // insert it
nElems++; // increment size
}
//--------------------------------------------------------------
public void display() // displays array contents
{
System.out.print("A=");
for(int j=0; j<nElems; j++) // for each element,
System.out.print(theArray[j] + " "); // display it
System.out.println("");
}
//--------------------------------------------------------------
public void quickSort()
{
recQuickSort(0, nElems-1);
}
//--------------------------------------------------------------
public void recQuickSort(int left, int right)
{
int size = right-left+1;
if(size <= 3) // manual sort if small
manualSort(left, right);
else // quicksort if large
{
long median = medianOf3(left, right);
int partition = partitionIt(left, right, median);
recQuickSort(left, partition-1);
recQuickSort(partition+1, right);
}
} // end recQuickSort()
//--------------------------------------------------------------
public long medianOf3(int left, int right)
{
int center = (left+right)/2;
// order left & center
if( theArray[left] > theArray[center] )
swap(left, center);
// order left & right
if( theArray[left] > theArray[right] )
swap(left, right);
// order center & right
if( theArray[center] > theArray[right] )
swap(center, right);
swap(center, right-1); // put pivot on right
return theArray[right-1]; // return median value
} // end medianOf3()
//--------------------------------------------------------------
public void swap(int dex1, int dex2) // swap two elements
{
long temp = theArray[dex1]; // A into temp
theArray[dex1] = theArray[dex2]; // B into A
theArray[dex2] = temp; // temp into B
} // end swap(
//--------------------------------------------------------------
public int partitionIt(int left, int right, long pivot)
{
int leftPtr = left; // right of first elem
int rightPtr = right - 1; // left of pivot
while(true)
{
while( theArray[++leftPtr] < pivot ) // find bigger
; // (nop)
while( theArray[--rightPtr] > pivot ) // find smaller
; // (nop)
if(leftPtr >= rightPtr) // if pointers cross,
break; // partition done
else // not crossed, so
swap(leftPtr, rightPtr); // swap elements
} // end while(true)
swap(leftPtr, right-1); // restore pivot
return leftPtr; // return pivot location
} // end partitionIt()
//--------------------------------------------------------------
public void manualSort(int left, int right)
{
int size = right-left+1;
if(size <= 1)
return; // no sort necessary
if(size == 2)
{ // 2-sort left and right
if( theArray[left] > theArray[right] )
swap(left, right);
return;
}
else // size is 3
{ // 3-sort left, center, & right
if( theArray[left] > theArray[right-1] )
swap(left, right-1); // left, center
if( theArray[left] > theArray[right] )
swap(left, right); // left, right
if( theArray[right-1] > theArray[right] )
swap(right-1, right); // center, right
}
} // end manualSort()
//--------------------------------------------------------------
} // end class ArrayIns
class QuickSort2App
{
public static void main(String[] args)
{
int maxSize = 16; // array size
ArrayIns arr; // reference to array
arr = new ArrayIns(maxSize); // create the array
for(int j=0; j<maxSize; j++) // fill array with
{ // random numbers
long n = (int)(java.lang.Math.random()*99);
arr.insert(n);
}
arr.display(); // display items
arr.quickSort(); // quicksort them
arr.display(); // display them again
} // end main()
} // end class QuickSort2App
以上算法是以3为界限划分的,如果是三个数时就不用快速排序了,否则用快速排序,以下又是一种如果数少的话用插入排序,如果数多的话用快速排序算法,是两种排序算法的组合使用。
下面是java代码:
// quickSort3.java
// demonstrates quick sort; uses insertion sort for cleanup
// to run this program: C>java QuickSort3App
class ArrayIns
{
private long[] theArray; // ref to array theArray
private int nElems; // number of data items
//--------------------------------------------------------------
public ArrayIns(int max) // constructor
{
theArray = new long[max]; // create the array
nElems = 0; // no items yet
}
//--------------------------------------------------------------
public void insert(long value) // put element into array
{
theArray[nElems] = value; // insert it
nElems++; // increment size
}
//--------------------------------------------------------------
public void display() // displays array contents
{
System.out.print("A=");
for(int j=0; j<nElems; j++) // for each element,
System.out.print(theArray[j] + " "); // display it
System.out.println("");
}
//--------------------------------------------------------------
public void quickSort()
{
recQuickSort(0, nElems-1);
// insertionSort(0, nElems-1); // the other option
}
//--------------------------------------------------------------
public void recQuickSort(int left, int right)
{
int size = right-left+1;
if(size < 10) // insertion sort if small
insertionSort(left, right);
else // quicksort if large
{
long median = medianOf3(left, right);
int partition = partitionIt(left, right, median);
recQuickSort(left, partition-1);
recQuickSort(partition+1, right);
}
} // end recQuickSort()
//--------------------------------------------------------------
public long medianOf3(int left, int right)
{
int center = (left+right)/2;
// order left & center
if( theArray[left] > theArray[center] )
swap(left, center);
// order left & right
if( theArray[left] > theArray[right] )
swap(left, right);
// order center & right
if( theArray[center] > theArray[right] )
swap(center, right);
swap(center, right-1); // put pivot on right
return theArray[right-1]; // return median value
} // end medianOf3()
//--------------------------------------------------------------
public void swap(int dex1, int dex2) // swap two elements
{
long temp = theArray[dex1]; // A into temp
theArray[dex1] = theArray[dex2]; // B into A
theArray[dex2] = temp; // temp into B
} // end swap(
//--------------------------------------------------------------
public int partitionIt(int left, int right, long pivot)
{
int leftPtr = left; // right of first elem
int rightPtr = right - 1; // left of pivot
while(true)
{
while( theArray[++leftPtr] < pivot ) // find bigger
; // (nop)
while( theArray[--rightPtr] > pivot ) // find smaller
; // (nop)
if(leftPtr >= rightPtr) // if pointers cross,
break; // partition done
else // not crossed, so
swap(leftPtr, rightPtr); // swap elements
} // end while(true)
swap(leftPtr, right-1); // restore pivot
return leftPtr; // return pivot location
} // end partitionIt()
//--------------------------------------------------------------
// insertion sort
public void insertionSort(int left, int right)
{
int in, out;
// sorted on left of out
for(out=left+1; out<=right; out++)
{
long temp = theArray[out]; // remove marked item
in = out; // start shifts at out
// until one is smaller,
while(in>left && theArray[in-1] >= temp)
{
theArray[in] = theArray[in-1]; // shift item to right
--in; // go left one position
}
theArray[in] = temp; // insert marked item
} // end for
} // end insertionSort()
//--------------------------------------------------------------
} // end class ArrayIns
class QuickSort3App
{
public static void main(String[] args)
{
int maxSize = 16; // array size
ArrayIns arr; // reference to array
arr = new ArrayIns(maxSize); // create the array
for(int j=0; j<maxSize; j++) // fill array with
{ // random numbers
long n = (int)(java.lang.Math.random()*99);
arr.insert(n);
}
arr.display(); // display items
arr.quickSort(); // quicksort them
arr.display(); // display them again
} // end main()
} // end class QuickSort3App
消除递归:对于现在的系统来说,消除递归所带来的改进并不是很明显,因为现在的系统可以更为有效的处理方法调用。
快速排序的效率
快速排序的时间复杂度为O(N*logN),对于分治算法来说都是这样的,在分治算法中用递归的方法把一列数据项分为两组,然后调用自身来分别处理每一组数据项,在这种情况下,算法实际上是以2为底的,运行时间和N*log2N成正比。
以上所述快速排序是用递归实现的,根据排序代码的思路来说,排序方法是:选择数组最右端的数位枢纽基数,然后从最左边开始用第一个数和枢纽相比,若比枢纽数小则继续向右走,用第二个数和枢纽比较,直到有一个数比枢纽数大时停下来,记住此时概数的下标为m,然后用倒数第二个数(倒数第一个数是枢纽)和枢纽比较,若比枢纽大则继续向左,用倒数第三个数和枢纽比较,直到有一个数比枢纽数小则停下来,记住该数的下标为n,假设数组名为array,此时用array[m]和array[n]交换,然后再继续刚才的比较步骤,左边的指针(此处指针并不是C++中的指针,次数只是标记的意思)继续向右,右边的指针继续向左,直至两个指针相碰或者相交,就停止,然后把左边的指针的指向的值(这个值总是比枢纽值大的第一个数)跟枢纽值交换,枢纽值就归位了,此时一趟排序完成,枢纽的左边和右边在按照刚才的步骤进行排序。