[算法天天见](二)进阶排序

一、说明

进阶排序主要会展示归并排序算法和快速排序算法。归并排序算法由于不是原地排序算法所以它的空间复杂度不是O(1),它的时间复杂度是O(nlogn)。整个算法如果采用递归方式去写的话是很简单的,具体可参看代码部分。
快速排序的思路和归并排序是一样的,就是把数组分成两部分分别使其有序最后在组合起来就整体有序了。不同的是快速排序并不是平均拆分数组,而是选择一个中心点,排序时让中心点左边的数都比中心点小,中心点右边数都比中心点大,这个排序算法的时间复杂度是O(n), 但是由于数组拆分是基于中心点的,所以它的拆分的时间复杂度和它选择的这个点有很大关系,具体情况可以看代码分析。

二、算法

归并排序

	   	/**
		 *
		 * 归并排序
		 * 用到的分治的思想
		 * 假如一个数组 前半部分和后半部分都是有序的, 那么它的排序时间复杂度是O(n)
		 * 那么有:
		 * A(m, n) = A(m, k) + A(k + 1, n)
		 * 当 m = k 时  说明数组只有一个数字那么它自然有序了 
		 * 同理n = k + 1 也是
		 * 所以我们只需要把数组 拆成 长度为一个 数组集,排序后再组合起来那么整个数组就有序了
		 * 那么剩下我们就要看看把数组 拆成 长度为一的数组集,需要的时间复杂度是多少
		 * 
		 * 假如数组长度 为 n, 我们每次 拆一半,最终得到 数组长度为1 
		 * 数学等式可以写为:
		 * n*(1/2)^x = 1 => 2^x = n => x = log(2)n (注意n是log的真数) => O(logn)
		 * 
		 * 所以整个算法的时间复杂度为: O(nlogn)
		 * 
		 *  
		 * @param s
		 * 
		 */		
		public function MergeSort(s:Array):void
		{
			Merge(s,0, s.length - 1);
		}
		private function Merge(s:Array, m:int, n:int):Array
		{
			var temp:Array = [];
			//假如相等,说明不能再分了就终止了
			if(m == n)
			{
				temp[0] = s[m];
				return temp;
			}
			var k:int = Math.floor((m + n) / 2);
			
			var t1:Array = Merge(s, m, k);
			var t2:Array = Merge(s, k + 1, n);
			//这里合并成一个
			var q:int = 0, p:int = 0;
			for(var i:int = 0; p <= k - m && q <= n - k - 1; i++)
			{
				if(t1[p] < t2[q])
				{
					temp[i] = t1[p];
					p++;
				}else
				{
					temp[i] = t2[q];
					q++;
				}
			}
			//把剩下的补齐
			while(p <= k-m)
			{
				temp[temp.length] = t1[p];
				p++;
			}
			
			while(q <= n - k - 1)
			{
				temp[temp.length] = t2[q];
				q++;
			}
			return temp;
		}

快速排序

		/**
		 *快速排序
		 * 用到的分治的思想
		 * 假如一个数组 前半部分和后半部分都是有序的, 那么它的排序时间复杂度是O(n)
		 * 那么有:
		 * A(m, n) = A(m, k) + A(k + 1, n)
		 * 当 m = k 时  说明数组只有一个数字那么它自然有序了 
		 * 同理n = k + 1 也是
		 * 所以我们只需要把数组 拆成 长度为一个 数组集,排序后再组合起来那么整个数组就有序了
		 * 那么剩下我们就要看看把数组 拆成 长度为一的数组集,需要的时间复杂度是多少
		 * 
		 * 注意拆分的策略和归并排序不一样, 这里的拆分和选择的对比点有很大关系 
		 * 我们每次选择最后一个点
		 * 假如选择点能平分数组 那么时间复杂度就和归并排序一样 O(logn)
		 * 
		 * 假如数组是有序的 [1,2,3,4,5,6,7,8],
		 * 那么我们拆分的情况就是  
		 * [1,2,3,4,5,6,7,8] => [1,2,3,4,5,6,7] | 8 => [1,2,3,4,5,6] | 7,8 => ... => [] | 1,2,3,4,5,6,7,8
		 * 可以看出总共要进行n次拆分 所以时间复杂度是O(n)
		 * 
		 * 而如果输入是完全无序的[8,7,6,5,4,3,2,1]
		 * 那么我们的拆分情况是
		 * [1,2,3,4,5,6,7,8] => 1 | [2,3,4,5,6,7,8] => 1,2 | [3,4,5,6,7,8] => ... => 1,2,3,4,5,6,7,8 | []
		 * 也是O(n)
		 * 
		 * 所以可以看出,中心点的选择是很重要的,我们需要尽量选择可以平分数组的点
		 * 它最好的情况是 O(nlogn), 最差的情况是 O(n²)
		 * 
		 * @param s
		 * 
		 */		
		public function QuickSort(s:Array):void
		{
			Quick(s, 0, s.length - 1);
		}
		private function Quick(s:Array, m:int, n:int):void
		{
			//
			if(m >= n)
			{
				return;
			}
			//每次都选最后一个作为中心点
			var j:int = m;//已排序区域起始序号
			var value:int;
			//排序
			for(var i:int = m; i < n; i++)
			{
				if(s[i] < s[n])//假如元素比中心点的这个小, 则把它交换到已排序区域的最后一位
				{
					value = s[j];
					s[j] = s[i];
					s[i] = value;
					j++;
				}else//否则假如元素比它大,则不做任何交换
				{
					
				}
			}
			//排序完成之后把最后一个值,与已排序列表的最后一位交换
			value = s[j];
			s[j] = s[n];
			s[n] = value;
			trace(s);
			Quick(s, m, j - 1);
			Quick(s, j + 1, n);
		}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值