快速排序
在前面,我们了解到了快速排序的思想,详情链接可见:快速排序。其实,快速排序也可以看作是分治策略的一种算法模式,根据快速排序的思想,我们可以实现许多实际中的应用问题。下面,我们来看看这些问题该如何实现。
首先,我们来回顾一个快速排序的实现过程,其中最重要的一步操作就是快速排序中的划分操作。
快速排序的划分操作实现
/**
* 快速排序的划分过程
*/
public static int Partition(int[] arr, int left, int right) {
int temp = arr[left];
while (left < right) {
while (left < right && arr[right] > temp) {
right--;
}
if (left < right) {
arr[left] = arr[right];
}
while (left < right && arr[left] < temp) {
left++;
}
if (left < right) {
arr[right] = arr[left];
}
}
arr[left] = temp;
return left;
}
有了划分操作后,我们就可以进行快速排序了。具体排序思想可以参考链接:快速排序。
快速排序的实现
快速排序的递归实现
/**
* 递归方法实现快速排序
*/
public static void quickSort(int[] arr, int left, int right) {
if (left < right) {
int pos = Partition(arr, left, right);
quickSort(arr, left, pos - 1);
quickSort(arr, pos + 1, right);
}
}
快速排序的非递归实现
- 通过队列实现快速排序。
/**
* 非递归方法实现快速排序
*/
public static void niceQuickSort(int[] arr, int left, int right) {
Queue<Integer> qu = new LinkedList<Integer>();
qu.offer(left);// left , right
qu.offer(right);
while (!qu.isEmpty()) {
left = ((LinkedList<Integer>) qu).pollFirst();
right = ((LinkedList<Integer>) qu).pollFirst();
int pos = Partition(arr, left, right);
if (left < pos - 1) //
{
qu.offer(left);
qu.offer(pos - 1);
}
if (pos + 1 < right) {
qu.offer(pos + 1);
qu.offer(right);
}
}
}
我们都知道,在一般情况下,快速排序经常将最左边的元素作为基准元素进行划分操作,那么是否可以随机指定基准的位置呢。
- 其实,这种情况是可以实现的,我们只需将通过随机对象出来的下标元素与最左边元素进行替换。此时,最左边元素就相当于随机出来的,我们依然可以进行使用上面的划分函数进行快速排序。
/**
* 快速排序
* 随机获取基准值(只需将随机获取到的下标的元素与left元素替换即可)
*/
public static int RandPartition(int[] ar, int left, int right) {
Random rad = new Random();
int index = rad.nextInt(right - left + 1) + left;
int tmp = ar[left];
ar[left] = ar[index];
ar[index] = tmp;
return Partition(ar, left, right);
}
接着,我们可以看到,上面的快速排序算法是使用双向共同进行排序的操作,那么是否可以只使用一边进行快速排序呢。
单向划分实现快速排序
- 具体划分过程如下:

- 代码实现:
/**
* 单向形式实现划分过程
*/
public static int singlePartition(int[] arr, int left, int right) {
int tmp = arr[left];
int i = left, j = i + 1;
while (j <= right) {
if (arr[j] <= tmp) {
i += 1;
if (i != j) {
swap(arr, i, j);
}
}
++j;
}
swap(arr, left, i);
return i;
}
/**
* 递归方法实现单向快速排序
*/
public static void singleQuickSort(int[] arr, int left, int right) {
if (left < right) {
int pos = singlePartition(arr, left, right);
singleQuickSort(arr, left, pos - 1);
singleQuickSort(arr, pos + 1, right);
}
}
通过单向划分数组进行快速排序的思想,我们可以很快联系到单向链表的快速排序方法实现,其原理与上述单向数组排序思想相同。
单向链表的快速排序算法实现
/**
* 单向实现链表划分
*/
public static ListNode ListPartition(ListNode left, ListNode right) {
int tmp = left.Value;
ListNode low = left, h = low.Next;
while (h != right) {
if (h.Value <= tmp) {
low = low.Next;
if (low != h) {
swap_list(low, h);
}
}
h = h.Next;
}
swap_list(left, low);
reutrn low;
}
/**
* 单向实现链表快速排序
*/
public static void ListQuickSort(ListNode left, ListNode right) {
if (left != right) {
ListNode pos = ListPartition(left, right);
ListQuickSort(left, pos);
ListQuickSort(pos.Next, right);
}
}
在此,我们再次看到了关于快速排序算法的实现,那么通过快速排序算法,我们究竟可以解决哪些实际问题呢。
快速排序思想的应用
- 首先,我们来看一个代码。
/**
* 在集合中获取第一小和第二小的元素
*/
public static void Select2Min(int[] ar) {
if (ar.length < 2) return;
int min1 = ar[0] < ar[1] ? ar[0] : ar[1];
int min2 = ar[0] > ar[1] ? ar[0] : ar[1];
for (int i = 2; i < ar.length; ++i) {
if (ar[i] < min1) {
min2 = min1; //注意将 min1 赋值给 min2
min1 = ar[i];
} else if (ar[i] < min2) {
min2 = ar[i];
}
}
System.out.println(min1);
System.out.println(min2);
}
- 这是用于查询数组中第一小和第二小元素的实现过程,那么该如何查询第K小的元素呢?
- 在快速排序的算法中,其中最重要的特征之一就是,基准值在划分完成后其位置将不再进行改变,那么通过此特征,我们是否可以实现查询第K小的元素呢?答案当然是肯定的。代码如下:
查询第K小的元素
/**
* 在集合中获取第K小的元素
*/
public static int Select_K(int[] ar, int left, int right, int k) {
if (left == right && k == 1) return ar[left];
int index = Partition(ar, left, right);
//经过第一次划分
int pos = index - left + 1;// left ... right
//若K值小于pos,说明K值在左边,将左边进行递归划分
if (k <= pos) return Select_K(ar, left, index, k);
//若K值大于pos,说明K值在右边,将右边进行递归划分
//需要注意此时K值需要减去pos值
else return Select_K(ar, index + 1, right, k - pos);
}
/**
* 在集合中获取第K小的元素
*/
public static int Select_KMin(int[] ar, int k) {
return Select_K(ar, 0, ar.length - 1, k);
}
计算相邻元素的最小差值
- 该题目的思路为:我们通过上述的查询第K小的元素,可以简单的查询中处于中间位置的元素大小,接着通过递归算法,我们只需要计算左半部分的最小差值、右半部分的最小差值,以及右半部分最小值与左半部分的最大值的差值,通过比较这三个数值的大小,我们就可以得到整个数组中相邻元素的最小差值。
- 代码如下:
/**
* 计算相邻元素的最小差值
* 整形的最大值
*/
static final int maxint = 0x7fffffff;
/**
* 计算相邻元素的最小差值
* 获取左半部分最大值
*/
public static int MaxS1(int[] ar, int left, int right) {
//通过划分函数,此时传入的index值就是左半部分的最大值
return ar[right];
}
/**
* 计算相邻元素的最小差值
* 获取右半部分最小值
*/
public static int MinS2(int[] ar, int left, int right) {
int tmp = ar[left];
for (int i = left + 1; i <= right; ++i) {
if (tmp > ar[i]) {
tmp = ar[i];
}
}
return tmp;
}
/**
* 计算相邻元素的最小差值
* 获取两个元素中的最小值
*/
public static int Min2d(int a, int b) {
return a < b ? a : b;
}
/**
* 计算相邻元素的最小差值
* 获取三个元素中的最小值
*/
public static int Min3d(int a, int b, int c) {
return Min2d(Min2d(a, b), c);
}
/**
* 计算相邻元素的最小差值
*/
public static int Cpair(int[] ar, int left, int right) {
if (right - left < 1) return maxint;
int k = (right - left + 1) / 2;
Select_K(ar, left, right, k); // left...index; index+1 ... right;
int index = left + k - 1;
int d1 = Cpair(ar, left, index); //s1;
int d2 = Cpair(ar, index + 1, right); //s2;
int maxs1 = MaxS1(ar, left, index);
int mins2 = MinS2(ar, index + 1, right); //s2
return Min3d(d1, d2, mins2 - maxs1);
}
通过快速排序算法的特征,我们可以更快速的解决许多实际中的问题,这对我们具有非常大的帮助,因此,当我们在遇到较难的问题时,要经常去考虑使用合适的算法去帮助我们解决问题。
本文介绍了快速排序算法的应用,包括如何实现快速排序的划分操作、递归与非递归实现,以及单向划分和链表排序。重点讨论了快速排序在解决实际问题中的应用,如查询第K小的元素和计算数组中相邻元素的最小差值,通过算法的特性简化问题解决。

366

被折叠的 条评论
为什么被折叠?



