冒泡排序 O n 2 O_{n^2} On2
1.循环比较相邻值,符合条件就交换位置,一次循环至少让一个元素移动到它应该在的位置
2.若在一次循环中没有位置发生交换,则认为该排序已完成
PHP
function bubble_sort(&$nums)
{
$len = count($nums);
for ($i = 0; $i < $len; $i++) {
$change = false;
for ($j = 0; $j < $len - $i - 1; $j++) {
if ($nums[$j] > $nums[$j + 1]) {
$tmp = $nums[$j];
$nums[$j] = $nums[$j + 1];
$nums[$j + 1] = $tmp;
$change = true;
}
}
if ($change === false) {
break;
}
}
}
$nums = [4, 5, 6, 3, 2, 1];
bubble_sort($nums);
var_dump($nums);
GO
package main
import "fmt"
func main(){
nums := []int{4, 5, 6, 3, 2, 1}
bubble_sort(&nums)
fmt.Println(nums)
}
func bubble_sort(nums *[]int) {
len := len(*nums)
for i := 0; i < len; i++ {
change := 0
for j := 0; j < len - i - 1; j++ {
if (*nums)[j] > (*nums)[j+1] {
tmp := (*nums)[j]
(*nums)[j] = (*nums)[j+1]
(*nums)[j+1] = tmp
change = 1
}
}
if change == 0 {
break
}
}
}
插入排序 O n 2 O_{n^2} On2
1.将数组中的数据分为两个区间,已排序区间和未排序区间
2.初始已排序区间只有一个元素,就是数组的第一个元素
3.取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序
4.重复这个过程,直到未排序区间中元素为空,算法结束
PHP
function insertion_sort(&$nums)
{
$len = count($nums);
for ($i = 0; $i < $len; $i++) {
$value = $nums[$i];
for ($j = $i - 1; $j >= 0; $j--) {
if ($nums[$j] > $value) {
$nums[$j + 1] = $nums[$j];
} else {
break;
}
}
$nums[$j + 1] = $value;
}
}
$nums = [4, 5, 6, 3, 2, 1];
insertion_sort($nums);
var_dump($nums);
GO
package main
import "fmt"
func main(){
nums := []int{4, 5, 6, 3, 2, 1}
insertion_sort(&nums)
fmt.Println(nums)
}
func insertion_sort(nums *[]int) {
var j int
len := len(*nums)
for i := 0; i < len; i++ {
value := (*nums)[i]
for j = i - 1; j >= 0; j-- {
if ((*nums)[j] > value) {
(*nums)[j + 1] = (*nums)[j]
} else {
break
}
}
(*nums)[j + 1] = value
}
}
选择排序 O n 2 O_{n^2} On2
选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾
PHP
function selection_sort(&$nums)
{
$len = count($nums);
for ($i = 0; $i < $len; $i++) {
$min = $i;
for ($j = $i + 1; $j < $len; $j++) {
if ($nums[$j] < $nums[$min]) {
$min = $j;
}
}
if ($min != $i) {
$tmp = $nums[$min];
$nums[$min] = $nums[$i];
$nums[$i] = $tmp;
}
}
}
$nums = [4, 5, 6, 3, 2, 1];
selection_sort($nums);
var_dump($nums);
快速排序 O n l o g n O_{nlogn} Onlogn
1.通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小
2.然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列
双路快排
从当前数组中,找到一个元素作为基准比较值(key),分别从两个方向进行比较。从后往前找,比key小元素放在数组前面。然后从前往后找,比key大的元素放在数组后面。最终两个方向交汇到中间,让key交换到数组的中间位置。
常见的取key方式是,取当前排序数组范围的最左边第一位,key是从前边取的,所以先要从后边(右边)开始往前找数字。
1.令left为第一位0(first),令right为最末位数组长度n。
2.key是从前边取,我们就需要先从right开始,从后往前走。
3.如果 a[right]>=key(即34),说明是正常的,因为大数字就应该放后面。因此,right继续往前走,也就是right–。
4.直到出现 a[right]<key,这就说明有一个小数字出现在了后面,至于它是不是真的属于后面?这个还暂时不知道,所以先停下来,这时候的right就标记了这个位置。
5.从后往前找小数,已经找到了一个位置了,那么就换一个方向,从前往后找大数。因此从left开始,从前往后走。
6.如果 a[left]<=key(即34),说明是正常的,因为小数字就应该放前面。因此,left 继续往后走,也就是left++。(这里可以看出,left 和 right 的操作是对称的)
7.直到出现 a[left]>key, 这就说明有一个大数字出现在了前面,至于它是不是真的属于前面?这个就需要判断 left 和 right 的关系。
8.如果 left < right 此时,一个大数字出现在了前面,一个小数字出现在了后面,因此我们直接将其互换,将 a[left] 和 a[right] 互换。
9.互换完之后,当前的标记位left和right 继续两边往中间走。同理,先从right继续从后往前走,直到遇见一个小数字(比key小)。然后换方向从left继续从前往后走,直到遇见一个大数字(比key大)
10.如果left = right,说明以及抵达了数值的中心(now)
11.如果 first != now, 则交换位置
12.完成了这一轮的快速排序之后,就会发现,这个数组可以拆分成左半部分和右半部分。
13.左半部分是 从 第一位 到 now-1,里面的数字都比中心的a[now]小。
14.右半部分是 从 now+1 到 最末位 ,里面的数字都比中心的a[now]大。
15.因此,我们就可以继续递归地对左半部分和右半部分使用快速排序即可。
16.直到最终, 开始的left >= 开始right ,说明已经是拆成1个数字了,就没必要再比下去了。
PHP
sort() 当数据量较小时(小于等于16),会使用插入排序,因为此时插入排序性能更好;否则会使用快速排序
//非原地
function quick_sort($nums)
{
if (count($nums) <= 1) {
return $nums;
}
$left = [];
$right = [];
$len = count($nums);
$pivot = $nums[0];
for ($i = 1; $i < $len; $i++) {
if ($nums[$i] > $pivot) {
array_push($right, $nums[$i]);
} else {
array_push($left, $nums[$i]);
}
}
return array_merge(quick_sort($left), array($pivot), quick_sort($right));
}
$nums = [4, 5, 6, 3, 2, 1];
$nums = quick_sort($nums);
var_dump($nums);
//双路快排(原地)
function quickSort(&$nums, $left = 0, $right = null) {
if ($right === null) {
$right = count($nums) - 1;
}
if ($left >= $right) {
return;
}
$firstIndex = $left;
$lastIndex = $right;
$pivot = $nums[$firstIndex];
while ($left != $right) {
while ($pivot <= $nums[$right] && $left != $right) {
$right--;
}
while ($pivot >= $nums[$left] && $left != $right) {
$left++;
}
if ($left < $right) {
$tmp = $nums[$right];
$nums[$right] = $nums[$left];
$nums[$left] = $tmp;
}
}
if ($firstIndex != $left) {
$tmp = $nums[$firstIndex];
$nums[$firstIndex] = $nums[$left];
$nums[$left] = $tmp;
}
quickSort($nums, $firstIndex, $left - 1);
quickSort($nums, $firstIndex + 1, $lastIndex);
}
$nums = [34, 5, 5, 555, 5, 4, 14, 5, 88, 89, 54];
quickSort($nums);
print_r($nums);
GO
package main
import "fmt"
func main() {
nums := []int{4, 5, 6, 3, 2, 1}
nums2 := []int{4, 5, 6, 3, 2, 1}
nums = quick_sort(nums)
fmt.Println(nums)
quickSort(&nums2, 0, len(nums2)-1)
fmt.Println(nums2)
}
//非原地
func quick_sort(nums []int) []int {
var left, right []int
len := len(nums)
if len <= 1 {
return nums
}
pivot := nums[0]
for i := 1; i < len; i++ {
if nums[i] <= pivot {
left = append(left, nums[i])
} else {
right = append(right, nums[i])
}
}
left = quick_sort(left)
right = quick_sort(right)
return append(append(left, pivot), right...)
}
//双路快排(原地)
func quickSort(nums *[]int, left int, right int) {
if left >= right {
return
}
firstIndex := left
lastIndex := right
pivot := (*nums)[firstIndex]
for {
if left == right {
break
}
for {
if (*nums)[right] < pivot || left == right {
break
}
right--
}
for {
if (*nums)[left] > pivot || left == right {
break
}
left++
}
if left < right {
tmp := (*nums)[left]
(*nums)[left] = (*nums)[right]
(*nums)[right] = tmp
}
}
if firstIndex != left {
tmp := (*nums)[firstIndex]
(*nums)[firstIndex] = (*nums)[left]
(*nums)[left] = tmp
}
quickSort(nums, firstIndex, left-1)
quickSort(nums, left+1, lastIndex)
}