快速排序算法
快速排序算法的基本思想是通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
我们来看看一趟排序中如何将数据划分为两部分,使得左边部分比给定元素小,而右边部分比给定元素大。
首先,我们选定一个数字作为中轴元素用于划分数据,我们选择数据的第一个元素。
然后,我们定义两个指针,分别指向数据的首(i)和尾(j)。从后面(j)元素开始进行比较,如果j指向的元素大于等于中轴元素,则j–,向前移动一位;否则,交换i和j位置的元素。然后,从前面(i)元素比较,如果i指向的元素小于等于中轴,则i++,向后移动一位;否则,交换i和j位置的元素。这样一直循环,知道i==j为止。这样就完成了一次划分,我们选择的中轴元素刚好位于i(此时,i等于j)位置上。
下面是一个示意图:
下面给出Java的实现:
package cn.tzy;
import java.util.Arrays;
public class SortAlg {
public static void main(String[] args) {
int[] numbers = {5, 1, 6, 7, 0, 4, 2, 3};
quickSort(numbers, 0, numbers.length - 1);
System.out.println(Arrays.toString(numbers));
}
public static void swap(int[] numbers, int i, int j) {
if (numbers[i] == numbers[j]) return;
numbers[i] = numbers[i] ^ numbers[j];
numbers[j] = numbers[i] ^ numbers[j];
numbers[i] = numbers[i] ^ numbers[j];
}
public static void quickSort(int[] numbers, int low, int high) {
if (numbers.length < 2) return;
if (low >= high) return;
int left = low;
int right = high;
int pivot = numbers[low];
while (left < right) {
while (left < right && numbers[right] >= pivot) right--;
swap(numbers, left, right);
while (left < right && numbers[left] <= pivot) left++;
swap(numbers, left, right);
}
quickSort(numbers, low, right - 1);
quickSort(numbers, left + 1, high);
}
}
我们再来看看用Scala的函数式编程思想如何实现:
package cn.tzy
object SortAlg {
def quickSort(numbers: List[Int]): List[Int] = {
if (numbers.length < 2) numbers
else {
quickSort(numbers.filter(_ < numbers.head)) ++
numbers.filter(_ == numbers.head) ++
quickSort(numbers.filter(_ > numbers.head))
}
}
def main(args: Array[String]): Unit = {
val numbers = List(5, 1, 6, 7, 0, 4, 2, 3)
println(numbers)
val sortedNumbers = quickSort(numbers)
println(sortedNumbers)
}
}
有没有感受到函数式编程的简介,我们使用filter函数找出比中轴元素小的,然后是中轴元素,再接着是比中轴元素小。短短几行代码就完成了Java很多行代码的功能!
SovietPower: 此外引用折叠中,没有 左值-左值 T& &、左值-右值 T& &&、右值-右值 T&& && 这三种情况,因为在推导模板实参 T 时,一般推导出的 T 都是不带引用的 int,除非形参是到无 cv 限定 T 的右值引用,即万能引用 T&&,此时如果实参是左值,则 T 才能是 int&。 并且结果是看实参是左值还是右值,而非是左值引用还是右值引用。也没有“右值引用类型变为了左值”这种说法,只是因为 变量名字构成的表达式 是左值表达式,所以它是左值表达式
SovietPower: 博主其实还没理解什么是左值和右值。左右值是表达式的值类别,左值引用和右值引用是一种类型,值类别和类型是表达式的不同且无关的属性。 std::move不是将左值引用转为右值引用,而是将左值转为亡值,从而能调用移动构造。调用移动构造不是因为接收了右值引用,而是因为接收了右值(C++17 起只能是亡值)。 并且左值不是变量,右值也不是临时对象,它们是表达式的特征,只是左值和亡值会指代一个对象,而纯右值指代一个临时对象。并且在C++17后,纯右值不再指代临时对象,只是能发生临时量实质化转为亡值,此时才是一个对象。这也是能进行复制消除的原因
ZVM_hanchan: 感谢,研究了2小时,可算是把代码走通了
电脑小玩家: 分享已经失效了,能再分享一下吗?谢谢了
向你步近 ✨: 案例里面内惩罚函数不应该是ln(8-x1-x2)吗