几种排序算法(PHP版)

// 总结一下常见的排序算法。
// 排序分内排序和外排序。
// 内排序:指在排序期间数据对象全部存放在内存的排序。
// 外排序:指在排序期间全部对象个数太多,不能同时存放在内存,必须根据排序过程的要求,不断在内、外存之间移动的排序。
// 内排序的方法有许多种,按所用策略不同,可归纳为五类:插入排序、选择排序、交换排序、归并排序、分配排序和计数排序。
// 插入排序主要包括直接插入排序,折半插入排序和希尔排序两种;
// 选择排序主要包括直接选择排序和堆排序;
// 交换排序主要包括冒泡排序和快速排序;
// 归并排序主要包括二路归并(常用的归并排序)和自然归并。
// 分配排序主要包括箱排序和基数排序。
// 计数排序就一种。
// 稳定排序:假设在待排序的文件中,存在两个或两个以上的记录具有相同的关键字,在用某种排序法排序后,若这些相同关键字的元素的相对次序仍然不变,则这种排序方法是稳定的。
// 其中冒泡,插入,基数,归并属于稳定排序;选择,快速,希尔,堆属于不稳定排序。

//直接插入法排序
//每次将一个待排序的数据元素,插入到前面已经排好序的数列中的适当的位置,使数列依然有序;直到待排序的数据元素全部插入完成为止。
:六个数12 15 9 20  6 31 24 用直接插入排序,如下图:

思路:

第一步:从给出的六个数中,随便拿出一个数,比如12,形成一个有序的数据序列(一个数当然是有序的数据序列了,不看12之外的数,就当其他的数不存在);

第二步:从剩下的五个数中挑一个数来,比如15,和刚才的12作比较,12<15,因此,放在12后面,形成数据序列12 15;

第三步:从剩下的四个数中挑出一个数来,比如9,和刚才的有序数据序列12 15作比较,9 < 12 < 15,因此,放在最前面,形成数据序列9 12 15;

第N步,经过这样一个一个的插入并对比,就形成了上图所示的排序结果。在一个元素插入时,首先要和数据序列中最大的元素作比较,如果遇到相同的,则放在相同元素的后面。

特性:

因为要不断的插入,因此直接插入排序一般采用链表结构,属于稳定排序。

    function insert_sort($arr)  
    {  
        $len = count($arr);  
        if($len <= 0) return false;  
        for($i=1; $i<$len; $i++)  
        {  
            $flag = $arr[$i]; //待插入的数  
            for($j=$i-1; $arr[$j]>$flag && $j>=0; $j--) //向前插入合适位置  
            {  
                $arr[$j+1] = $arr[$j]; //比它大的逐个后移  
            }  
            $arr[$j+1] = $flag;  
        }  
        return $arr;  
    }  

 

// 折半插入排序
// 折半插入排序(binary insertion sort)是对插入排序算法的一种改进,由于排序算法过程中,就是不断的依次将元素插入前面已排好序的序列中。由于前半部分为已排好序的数列,这样我们不用按顺序依次寻找插入点,可以采用折半查找的方法来加快寻找插入点的速度。
// 折半插入排序算法的具体操作为:在将一个新元素插入已排好序的数组的过程中,寻找插入点时,将待插入区域的首元素设置为a[low],末元素设置为 a[high],则轮比较时将待插入元素与a[m],其中m=(low+high)/2相比较,如果比参考元素小,则选择a[low]到a[m-1]为新 的插入区域(即high=m-1),否则选择a[m+1]到a[high]为新的插入区域(即low=m+1),如此直至low<=high不成 立,即将此位置之后所有元素后移一位,并将新元素插入a[high+1]。
// 折半插入排序算法是一种稳定的排序算法,比直接插入算法明显减少了关键字之间比较的次数,因此速度比直接插入排序算法快,但记录移动的次数没有变,所以折半插入排序算法的时间复杂度仍然为O(n^2),与直接插入排序算法相同。

$binary_insert_sort = function($arr){
	$num = count($arr);
	//遍历数组
	for($i=1;$i<$num;$i++){
		//有序数列的起点
		$low=0;
		//有序数列的终点
		$high=$i-1;
		//待插入元素
		$t=$arr[$i];
		$mid;
		//循环二分找出插入点
		while($low<=$high){
			$mid=floor(($low+$high)/2);
			if($t<$arr[$mid]){
				$high=$mid-1;
			}else{
				$low=$mid+1;
			}
		}
		//将有序数组中的大于插入点的数据后移
		$j = $i;
		for($j;$j>$low;$j--){
			$arr[$j]=$arr[$j-1];
		}
		//插入数据
		$arr[$low]=$t;
	}
	return $arr;
};

 

// 希尔排序
// 希尔排序(Shell Sort)又叫做缩小增量排序(diminishing increment sort),是一种很优秀的排序法,算法本身不难理解,也很容易实现,而且它的速度很快。
// 基本思想:
//   先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插入 排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1), 即所有记录放在同一组中进行直接插入排序为止。
//   该方法实质上是一种分组插入方法。插入排序(Insertion Sort)的一个重要的特点是,如果原始数据的大部分元素已经排序,那么插入排序的速度很快(因为需要移动的元素很少)。从这个事实我们可以想到,如果原 始数据只有很少元素,那么排序的速度也很快。--希尔排序就是基于这两点对插入排序作出了改进。

是直接插入排序的改进,例:十个数57 68 59 52 72 28 96 33 24 19用希尔排序,如下图:


思路:

第一步:用排序数字的总数除以2,取奇数得到步长(增量)d1 = 5;

第二步:根据步长d1,将十个数分成五组,如图所示,对这五组各自进行直接插入排序;

第三步:用步长d2继续除以2,取最近的奇数得到步长d2=3;

第四步:根据步长d2,将十个数分成三组,如图所示,对着五组各自进行直接插入排序;

第N步:重复上述分组和排序操作,直到步长变成1,即所有记录放进一个组中排序为止。

特性

由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。

// 希尔排序
// 希尔排序(Shell Sort)又叫做缩小增量排序(diminishing increment sort),是一种很优秀的排序法,算法本身不难理解,也很容易实现,而且它的速度很快。 
// 基本思想:
//   先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插入 排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1), 即所有记录放在同一组中进行直接插入排序为止。
//   该方法实质上是一种分组插入方法。插入排序(Insertion Sort)的一个重要的特点是,如果原始数据的大部分元素已经排序,那么插入排序的速度很快(因为需要移动的元素很少)。从这个事实我们可以想到,如果原 始数据只有很少元素,那么排序的速度也很快。--希尔排序就是基于这两点对插入排序作出了改进。
$shell_sort = function($arr){
	$num = count($arr);
	$gap = floor($num/2);
	$flag=true;
	//按一半的步长分组比对
	while($gap>1||$flag)
	{
		$flag=false;
		for($i=0;$i+$gap<$num;$i++){
			//比对交换
			if($arr[$i]>$arr[$i+$gap])
			{
				$temp = $arr[$i];
				$arr[$i] = $arr[$i+$gap];
				$arr[$i+$gap] = $temp;
				$flag=true;
			}
		}
		//步长在减半
		if($gap>1){
			$gap = floor($gap/2);
		}
	}
	return $arr;
};

 // 直接选择排序
// 排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个 元素不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择,如果当前元素比一个元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么 交换后稳定性就被破坏了。比较拗口,举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。时间复杂度是O(n^2)

 

例:四个数57 68 59 52选择排序:

思路:

第一步:从四个数找到最小的,和初始状态排在第一位的移动互换;

第二步:从剩下三个数中找到最小的,和初始状态排在第二位的移动互换;

第N步:重复上述查找最小和互换的步骤,直到最后一个为止。

特性

举个例子,序列5 8 5 2 9, 我们知道第一遍选择第1个元素5会和2交换,那么原序列中两个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。

    function select_sort($arr)  
    {  
        $len = count($arr);  
        if($len <= 0) return false;  
        for($i=0; $i<$len; $i++)  
        {  
            $min = $i;  
            for($j=$i+1; $j<$len; $j++) //记录剩下中最小那的  
            {  
                if($arr[$j]<$arr[$min]) $min = $j;  
            }  
            if($min != $i) //把当前数与该最小数交换  
            {  
                $temp = $arr[$min];  
                $arr[$min] = $arr[$i];  
                $arr[$i] = $temp;  
            }  
        }  
        return $arr;  
    }  

 

// 堆排序
// 我们知道堆的结构是节点i的孩子为2*i和2*i+1节点,大顶堆要求父节点大于等于其2个子节点,小顶堆要求父节点小于等于其2个子节点。在一个长为n 的序列,堆排序的过程是从第n/2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n /2-1, n/2-2, ...1这些个父节点选择元素时,就会破坏稳定性。有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节点把后面一个相同的元素没 有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法。
//堆排序

关于堆排序,有些复杂,下面举例说明:例:对数列{12,56,23,26,15,86,92,75,65}建立大顶堆(大根堆),则初始堆是?

思路:

分为两个大的过程,第一是建堆过程


思路:

第一步:根据完全二叉树排列给出的数字,如上图中第一个图;

第二步:因为树是一个单向的过程,叶子结点是无法知道父结点的,因此不能拿叶子结点去和父结点比较;根据结点i,i>=n/2为叶子结点的特性,找出最后一个非叶子结点,然后拿它和它的叶子结点作比较,如果比叶子结点小,则互换(建立的是大顶堆),反之不动。

第三步:紧接着调整n/2 - 1号结点(求出n/2 -1 = 3,也就是23号结点),从图中看出23号结点的两个结点都比它大,那么择优选取一个最大的进行互换。

第N步:按照上述方法,依次互换,最后建立了一个大顶堆。

第二是堆排序过程:


思路:

第一步:首先根据上面建立好的初始堆将根结点92输出,然后用编号最大的结点65替代根结点,断开最大编号结点的指针。

第二步:上一步完成后,检查65结点是否符合大顶堆要求,如果不符合又进行一次建堆的过程(参照第一个过程)。

第N步:按照上述两个步骤,反复操作,就会得到需要的结果。

有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节点把后面一个相同的元素没有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法

function heapSort(&$arr) {
 #初始化大顶堆
 initHeap($arr, 0, count($arr) - 1);
 
 //开始交换首尾节点,并每次减少一个末尾节点再调整堆,直到剩下一个元素
 for($end = count($arr) - 1; $end > 0; $end--) {
	 $temp = $arr[0];
	 $arr[0] = $arr[$end];
	 $arr[$end] = $temp;
	 ajustNodes($arr, 0, $end - 1);
 }
}

//初始化最大堆,从最后一个非叶子节点开始,最后一个非叶子节点编号为 数组长度/2 向下取整
function initHeap(&$arr) {
 $len = count($arr);
 for($start = floor($len / 2) - 1; $start >= 0; $start--) {
	 ajustNodes($arr, $start, $len - 1);
 }
}

//调整节点
//@param $arr    待调整数组
//@param $start    调整的父节点坐标
//@param $end    待调整数组结束节点坐标
function ajustNodes(&$arr, $start, $end) {
 $maxInx = $start;
 $len = $end + 1;    //待调整部分长度
 $leftChildInx = ($start + 1) * 2 - 1;    //左孩子坐标
 $rightChildInx = ($start + 1) * 2;    //右孩子坐标
 
 //如果待调整部分有左孩子
 if($leftChildInx + 1 <= $len) {
	 //获取最小节点坐标
	 if($arr[$maxInx] < $arr[$leftChildInx]) {
		 $maxInx = $leftChildInx;
	 }
	 
	 //如果待调整部分有右子节点
	 if($rightChildInx + 1 <= $len) {
		 if($arr[$maxInx] < $arr[$rightChildInx]) {
			 $maxInx = $rightChildInx;
		 }
	 }
 }
 
 //交换父节点和最大节点
 if($start != $maxInx) {
	 $temp = $arr[$start];
	 $arr[$start] = $arr[$maxInx];
	 $arr[$maxInx] = $temp;
	 
	 //如果交换后的子节点还有子节点,继续调整
	 if(($maxInx + 1) * 2 <= $len) {
		 ajustNodes($arr, $maxInx, $end);
	 }
 }
}

 

//冒泡排序
//冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,是不用交换的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改 变,所以冒泡排序是一种稳定排序算法。

比较常见的排序算法。举例说明:


思路:

第一步:将倒数第一个和倒数第二个数进行比较,如果小,则互换;

第二步:将倒数第二个和倒数第三个数进行比较,如果小,则互换;

第N步:筛选出最小的一个数,然后从剩下的数中按照上面的方法反复操作,得到需要的序列。

特性

如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并

    function bubble_sort($arr)  
    {  
        $len = count($arr);  
        if($len <= 0) return false;  
        for($i=0; $i<$len-1; $i++)  
        {  
            for($j=$i+1; $j<$len-1; $j++)  
            {  
                if($arr[$i] > $arr[$j])  
                {  
                    $t = $arr[$i];  
                    $arr[$i] = $arr[$j];  
                    $arr[$j] = $t;  
                }  
            }  
        }  
        return $arr;  
    }  

 

//快速排序
//快速排序(Quicksort)是对冒泡排序的一种改进。由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然 后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

快速排序(分治思想)是对冒泡排序的一种改进,思想:从数列中挑出一个元素,称为 "基准"(pivot);重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分割 结束之后,该基准就处于数列的中间位置。这个称为分割(partition)操作;递归地(recursive)把小于基准值元素的子数列和大于基准值元 素的子数列排序。

思路:

第一步:从给出的数列中找到一个基准,如图中的57,左指针指向57,右指针指向最后一个元素;

第二步:对左指针与右指针指向的元素作对比,右指针指向的元素19比左指针指向的元素57小(基准),互换位置;

第三步:左指针右移一个后跟右指针对比,68>57,因此互换;

第N步:按照上述的步骤经过指针的不断移动和元素的对比互换,最后得出第一个以57为中心的序列(左侧小于57,右侧大于57);接下来利用递归分别对57前后的进行排序。

特性

上面右侧的动态图很好的说明了快速排序的思路,快速排序是一个不稳定的排序算法。

    function quick_sort($arr)  
    {  
        $len = count($arr);  
        if($len <= 1) return $arr;  
        $mid = $arr[0]; //以此数为基准拆分为两个数组  
        $l = $r = array();  
        for($i=1; $i<$len; $i++)  
        {  
            if($arr[$i]<$mid) $l[] = $arr[$i];  
            else $r[] = $arr[$i];  
        }  
        $l = quick_sort($l);  
        $r = quick_sort($r);  
        return array_merge($l, array($mid), $r);  
    } 

 

//二路归并排序    
//归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有 序的长序列,不断合并直到原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小相等也没有人故意交换,这不会破坏稳定 性。那么,在短的有序序列合并的过程中,稳定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我们把处在前面的序列的元素保存在结 果序列的前面,这样就保证了稳定性。所以,归并排序也是稳定的排序算法
// 第一步:把待排序的每一个元素看做一个有序表(则由n个有序表),通过两两合并,生成⌊n/2⌋个长度为2(最后一个表的长度可能小于2)的有序表。

// 第二步:每组内部进行排序;

// 第三步:两组两组进行归并,将两个指针分别定为两组最小的两个数,然后进行比较,小的挑出来,指针后移,继续比较。

// 第N步:进过上述不断的归并和比较,最终得出一个正确的序列。

该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。


思路:

第一步:把待排序的每一个元素看做一个有序表(则由n个有序表),通过两两合并,生成⌊n/2⌋个长度为2(最后一个表的长度可能小于2)的有序表。

第二步:每组内部进行排序;

第三步:两组两组进行归并,将两个指针分别定为两组最小的两个数,然后进行比较,小的挑出来,指针后移,继续比较。

第N步:进过上述不断的归并和比较,最终得出一个正确的序列。

归并排序是稳定的排序算法

function  merge(&$arr,$start,$mid,$end) {
	$i = $start;
	$j = $mid+1;
	$k = 0;
	$ary = array();
	while($i <= $mid && $j <= $end) {
	if($arr[$i] <= $arr[$j]) {
			$ary[$k++] = $arr[$i++];
	} else {
			$ary[$k++] = $arr[$j++];
	}
	}
	while($i <= $mid) {
		   $ary[$k++] = $arr[$i++];
	}
	while($j <= $end) {
			$ary[$k++] = $arr[$j++];
	}
	for($i = 0 ;$i < $k;$i++) {
			$arr[$i + $start] = $ary[$i];
	}
}
function merge_sort(&$array,$low,$high) {
	if($low < $high) { // 任何递归都要有边界,否则就是死循环
			$mid = floor(($low+$high) / 2);
			merge_sort($array,$low,$mid);
			merge_sort($array,$mid+1,$high);
			merge($array,$low,$mid,$high);
	} else {
			return ;// 这里其实可以不写,php内部默认都会自动加return的
	}
}

 

排序算法复杂度:

按平均时间将排序分为四类:
(1)平方阶(O(n2))排序
     一般称为简单排序,例如直接插入、直接选择和冒泡排序;
(2)线性对数阶(O(nlgn))排序
     如快速、堆和归并排序;
(3)O(n1+£)阶排序
     £是介于0和1之间的常数,即0<£<1,如希尔排序;
(4)线性阶(O(n))排序
     如桶、箱和基数排序。

不同条件下,排序方法的选择
(1)若n较小(如n≤50),可采用直接插入或直接选择排序。
     当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插人,应选直接选择排序为宜。
(2)若文件初始状态基本有序(指正序),则应选用直接插人、冒泡或随机的快速排序为宜;
(3)若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或归并排序。
     快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
     堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。
     若要求排序稳定,则可选用归并排序。但本章介绍的从单个记录起进行两两归并的  排序算法并不值得提倡,通常可以将它和直接插入排序结合在一起使用。先利用直接插入排序求得较长的有序子文件,然后再两两归并之。因为直接插入排序是稳定 的,所以改进后的归并排序仍是稳定的。 4)在基于比较的排序方法中,每次比较两个关键字的大小之后,仅仅出现两种可能的转移,因此可以用一棵二叉树来描述比较判定过程。
     当文件的n个关键字随机分布时,任何借助于"比较"的排序算法,至少需要O(nlgn)的时间。

 

   各类算法的比较

1000个随机数测试结果:
直接插入排序:
时间:0.22043514251709
二分插入排序:
时间:0.17451214790344
哈希插入排序:
时间:0.32956099510193
选择排序:
时间:0.27549505233765
堆排序:
时间:0.055191993713379
冒泡排序:
时间:0.61164093017578
快速排序:
时间:0.02063512802124
归并排序:
时间:0.031260013580322

 

参考

http://blog.csdn.net/bjyfb/article/details/7513509

http://www.cnblogs.com/xkfz007/archive/2012/07/01/2572017.html

<?php
//直接插入法排序
$InsertSort = function($arr){
    $num = count($arr);
    // 遍历数组
    for ($i = 1;$i < $num; $i++) {
        // 获得当前值
        $iTemp = $arr[$i];
        // 获得当前值的前一个位置
        $iPos = $i - 1;
        // 如果当前值小于前一个值切未到数组开始位置
        while (($iPos >= 0) && ($iTemp < $arr[$iPos])) {
            // 把前一个的值往后放一位
            $arr[$iPos + 1] = $arr[$iPos];
            // 位置递减
            $iPos--;
        }
        $arr[$iPos+1] = $iTemp;
    }
    return $arr;
};

// 折半插入排序
$binary_insert_sort = function($arr){
	$num = count($arr);
	//遍历数组
	for($i=1;$i<$num;$i++){
		//有序数列的起点
		$low=0;
		//有序数列的终点
		$high=$i-1;
		//待插入元素
		$t=$arr[$i];
		$mid;
		//循环二分找出插入点
		while($low<=$high){
			$mid=floor(($low+$high)/2);
			if($t<$arr[$mid]){
				$high=$mid-1;
			}else{
				$low=$mid+1;
			}
		}
		//将有序数组中的大于插入点的数据后移
		$j = $i;
		for($j;$j>$low;$j--){
			$arr[$j]=$arr[$j-1];
		}
		//插入数据
		$arr[$low]=$t;
	}
	return $arr;
};

// 希尔排序
$shell_sort = function($arr){
	$num = count($arr);
	$gap = floor($num/2);
	$flag=true;
	//按一半的步长分组比对
	while($gap>1||$flag)
	{
		$flag=false;
		for($i=0;$i+$gap<$num;$i++){
			//比对交换
			if($arr[$i]>$arr[$i+$gap])
			{
				$temp = $arr[$i];
				$arr[$i] = $arr[$i+$gap];
				$arr[$i+$gap] = $temp;
				$flag=true;
			}
		}
		//步长在减半
		if($gap>1){
			$gap = floor($gap/2);
		}
	}
	return $arr;
};

// 直接选择排序
$select_sort = function($arr)
{
	$num = count($arr);
	for($i=0;$i<$num-1;$i++)
	{
		//取得最小值
		$min=$arr[$i];
		$index=$i;
		for($j=$i+1;$j<$num;$j++){
			if($arr[$j]<$min)
			{
				$min=$arr[$j];
				$index=$j;
			}
		}
		//交换
		$temp = $arr[$i];
		$arr[$i] = $arr[$index];
		$arr[$index] = $temp;
	}
	return $arr;
};
  
// 堆排序
function heapSort(&$arr) {
 #初始化大顶堆
 initHeap($arr, 0, count($arr) - 1);
 
 //开始交换首尾节点,并每次减少一个末尾节点再调整堆,直到剩下一个元素
 for($end = count($arr) - 1; $end > 0; $end--) {
	 $temp = $arr[0];
	 $arr[0] = $arr[$end];
	 $arr[$end] = $temp;
	 ajustNodes($arr, 0, $end - 1);
 }
}

//初始化最大堆,从最后一个非叶子节点开始,最后一个非叶子节点编号为 数组长度/2 向下取整
function initHeap(&$arr) {
 $len = count($arr);
 for($start = floor($len / 2) - 1; $start >= 0; $start--) {
	 ajustNodes($arr, $start, $len - 1);
 }
}

//调整节点
//@param $arr    待调整数组
//@param $start    调整的父节点坐标
//@param $end    待调整数组结束节点坐标
function ajustNodes(&$arr, $start, $end) {
 $maxInx = $start;
 $len = $end + 1;    //待调整部分长度
 $leftChildInx = ($start + 1) * 2 - 1;    //左孩子坐标
 $rightChildInx = ($start + 1) * 2;    //右孩子坐标
 
 //如果待调整部分有左孩子
 if($leftChildInx + 1 <= $len) {
	 //获取最小节点坐标
	 if($arr[$maxInx] < $arr[$leftChildInx]) {
		 $maxInx = $leftChildInx;
	 }
	 
	 //如果待调整部分有右子节点
	 if($rightChildInx + 1 <= $len) {
		 if($arr[$maxInx] < $arr[$rightChildInx]) {
			 $maxInx = $rightChildInx;
		 }
	 }
 }
 
 //交换父节点和最大节点
 if($start != $maxInx) {
	 $temp = $arr[$start];
	 $arr[$start] = $arr[$maxInx];
	 $arr[$maxInx] = $temp;
	 
	 //如果交换后的子节点还有子节点,继续调整
	 if(($maxInx + 1) * 2 <= $len) {
		 ajustNodes($arr, $maxInx, $end);
	 }
 }
}


//冒泡排序
$BubbleSort = function($arr) {
    // 获得数组总长度
    $num = count($arr);
    // 正向遍历数组
    for ($i = 1; $i < $num; $i++) {
        // 反向遍历
        for ($j = $num - 1; $j >= $i ; $j--) {
            // 相邻两个数比较
            if ($arr[$j] < $arr[$j-1]) {
                // 暂存较小的数
                $iTemp = $arr[$j-1];
                // 把较大的放前面
                $arr[$j-1] = $arr[$j];
                // 较小的放后面
                $arr[$j] = $iTemp;
            }
        }
    }
    return $arr;
};

$bubble_sort2 = function($a)
{
	$n = count($a);
	$flag=true;
	for($i=$n-1;$i>0 && $flag;$i--){
		$flag=false;
		for($j=0;$j<$i;$j++){
			//把最大的换到后面
			if($a[$j]>$a[$j+1]){
				$iTemp = $a[$j];
				$a[$j] = $a[$j+1];
				$a[$j+1] = $iTemp;
				$flag=true;
			}
		}
	}
	return $a;
};


//快速排序
function quick_sort($arr)  
{  
	$len = count($arr);  
	if($len <= 1) return $arr;  
	$mid = $arr[0]; //以此数为基准拆分为两个数组  
	$l = $r = array();  
	for($i=1; $i<$len; $i++)  
	{  
		if($arr[$i]<$mid) $l[] = $arr[$i];  
		else $r[] = $arr[$i];  
	}  
	$l = quick_sort($l);  
	$r = quick_sort($r);  
	return array_merge($l, array($mid), $r);  
}  
	
//二路归并排序	
function  merge(&$arr,$start,$mid,$end) {
	$i = $start;
	$j = $mid+1;
	$k = 0;
	$ary = array();
	while($i <= $mid && $j <= $end) {
	if($arr[$i] <= $arr[$j]) {
			$ary[$k++] = $arr[$i++];
	} else {
			$ary[$k++] = $arr[$j++];
	}
	}
	while($i <= $mid) {
		   $ary[$k++] = $arr[$i++];
	}
	while($j <= $end) {
			$ary[$k++] = $arr[$j++];
	}
	for($i = 0 ;$i < $k;$i++) {
			$arr[$i + $start] = $ary[$i];
	}
}
function merge_sort(&$array,$low,$high) {
	if($low < $high) { // 任何递归都要有边界,否则就是死循环
			$mid = floor(($low+$high) / 2);
			merge_sort($array,$low,$mid);
			merge_sort($array,$mid+1,$high);
			merge($array,$low,$mid,$high);
	} else {
			return ;// 这里其实可以不写,php内部默认都会自动加return的
	}
}



echo '1000个随机数:','<br/>';
for($i=0;$i<1000;$i++){
	$array[] = rand();
}
foreach($array as $val){
	echo $val,',';
}
echo '<br/>';


echo '直接插入排序:','<br/>';
$b = microtime(true);
foreach($InsertSort($array) as $val){
	echo $val,',';
}
$e = microtime(true);
echo '<br/>';
echo '时间:',$e-$b,'<br/>';

echo '二分插入排序:','<br/>';
$b = microtime(true);
foreach($binary_insert_sort($array) as $val){
	echo $val,',';
}
$e = microtime(true);
echo '<br/>';
echo '时间:',$e-$b,'<br/>';

echo '哈希插入排序:','<br/>';
$b = microtime(true);
foreach($shell_sort($array) as $val){
	echo $val,',';
}
$e = microtime(true);
echo '<br/>';
echo '时间:',$e-$b,'<br/>';

echo '选择排序:','<br/>';
$b = microtime(true);
foreach($select_sort($array) as $val){
	echo $val,',';
}
$e = microtime(true);
echo '<br/>';
echo '时间:',$e-$b,'<br/>';

echo '堆排序:','<br/>';
$arrayT = $array;
$b = microtime(true);
heapSort($arrayT);
foreach($arrayT as $val){
	echo $val,',';
}
$e = microtime(true);
echo '<br/>';
echo '时间:',$e-$b,'<br/>';


echo '冒泡排序:','<br/>';
$b = microtime(true);
foreach($bubble_sort2($array) as $val){
	echo $val,',';
}
$e = microtime(true);
echo '<br/>';
echo '时间:',$e-$b,'<br/>';

echo '快速排序:','<br/>';
$b = microtime(true);
foreach(quick_sort($array) as $val){
	echo $val,',';
}
$e = microtime(true);
echo '<br/>';
echo '时间:',$e-$b,'<br/>';

echo '归并排序:','<br/>';
$arrayG = $array;
$b = microtime(true);
merge_sort($arrayG,0,count($arrayG)-1);
foreach($arrayG as $val){
	echo $val,',';
}
$e = microtime(true);
echo '<br/>';
echo '时间:',$e-$b,'<br/>';

 

posted on 2012-05-28 17:15  kudosharry  阅读(590)  评论(0编辑  收藏  举报

导航