来吧 少年 排序吧(1)

为了明天即将到来的面试,为了加强记忆,在这里紧急的总结下各自排序的相关知识,

话说CSDN博客里可不可以@啊。

虽然这样的帖子成千上万,不过写出来是给自己看得。

首先,第一个: 插入排序(insertion sort)

这个原理很简单,用两个指针,一个指向要排序的数字,另外一个遍历数组,来找出第一个比《要排序的数字》大或者小的数字,将要排序的数字放入合适的位置。如此遍历数组中所有的数,直到数组有序。

对于随机乱序的数组,该算法的时间复杂度为O(n^2),并且稳定。希尔排序为该排序的变种。

比较适合针对本身已经有序的数组,插入N个元素(N个数远远小于数组长度)后,使原数组仍然有序。

给一个该排序的Java实现:

public int[] insertionSort(int[] array){
		int tmp = 0;
		for (int i = 1; i < array.length; i++){
			int j;
			tmp = array[i];
			for(j=i ; j > 0 && tmp < array[j-1] ; j--){
                                array[j] = array[j-1];
			} 
			array[j] = tmp; 
		}
		return array;
	}

然后,我们复习一下稍微复杂点的,冒泡排序(bubble sort)

不过好像也复杂不到哪里去,bubble最大的优点就是易于实现,原理简单,以及稳定(其实我觉得如果要使用不如用插入,实现也简单,算法复杂度也一样,不过真的很少用吧),该算法时间复杂度为 O(n^2),排序中算是比较慢的。感觉主要用途是教学..

原理也很简单,想象一下水中的气泡,大的气泡会浮的快些,小的慢看起来慢些,如果同一时间上浮,那么最后这些气泡会是有序的。

在这里,我们将选取一个元素,然后从该元素开始遍历整个数组,如果发现两个元素顺序相反,我们就将其调换,由此,我们可知,冒泡排序效率之所以低就是因为交换的次数过多导致的。

实现代码如下

	public int[] bubbleSort(int[] array){
		int tmp = 0;
		for (int i = 1; i < array.length; i++){
			for(int j = 0; j < array.length - i; j++){
				if (array[j] > array[j+1]){
					tmp = array[j+1];
					array[j+1] = array[j];
					array[j] = tmp;
				}
			}
		}
		return array;
	}

以前经常会把前两种排序弄混,虽然原理很相似,不过注意,插入排序会直接把数组分成有序的部分和无序的部分,然后从无序的部分不断的提取元素插入有序数组。而插入排序则是不断的对无序元素进行调换,最后使元素有序。


然后,我们看一下最简单的不稳定排序 - 选择排序

选择排序的原理就是,每次选出未排序序列中的最大/最小值,将其放入数组的最后/前。n趟之后,数组有序。

这种排序方法的比较方法是恒定的O(n^2),其不稳定的地方主要体现在交换次数上,最好的情况及数组有序,我们需要交换0次,而最差情况是数组逆序,需要交换n-1次,因此,在n值较小时,选择排序效率高于冒泡排序。

java code:

	public int[] selectionSort(int[] array){
		int min = 0;
		for (int i = 0; i < array.length; i++){
			for(int j = i; j < array.length; j++){
				min = array[i];
				if (array[j] < min){
					int tmp = array[j];
					array[j] = min;
					min = tmp;
				}
				array[i] = min;
			}
		}
		return array;
	}

接下来是排序中的重头戏,个人认为是当今算法界的明星。排序方法中最常用,同时也是通常是最有效率的算法- 快速排序:

该算法是冒泡排序的改进,其原理采用了算法学中的经典思想:分而治之的思想: 首先,选出一个数(这个数一般选自数组的head或者tail,我们称之为pivot)同时,根据这个数字,我们把数组分成 比该数大 或者 比该数小 两个子数组,然后递归调用,知道数组不可再分,即使数组有序。

这个图非常好:


快速排序也是一种不稳定的排序方法,很容易想象到,在这个排序过程中,pivot在有序数组中的位置决定了其程序的执行时间,最好的情况应该是,每一次方法的调用,都会很幸运的选到这个数组的中位数,理想情况下,其复杂度为O(nlogn)。而最坏情况,每一次我们都只能选到最大值或者最小值,此时,排序便会退化为冒泡排序,执行时间O(n^2)。

另外,快速排序中必须要知道的一点就是partition,也就是分区的方法。这个算法有同学描述为挖坑添数法,个人觉得十分形象:

具体步骤为定义i,j两个指针,一个指向数组head 一个指向数组tail,当我们取出了pivot之后,我们即认为原来pivot所占的位置已经空了,我们需要另外一个数字来占据它。如果我们选取了head作为pivot,那么我们首先向前移动指向tail的指针j,直到我们遇到第一个比pivot小的数,用其添掉原来pivot的坑,而这时这个较小的数字原来所占据的位置又变成了一个坑,此时,我们便移动指向head的指针i,寻找第一个比pivot大的数,并用其添坑。做完后,j继续移动,如此反复知道i= j,最后一个坑我们便用pivot来添,而i或者j的值,便是partition的分界线。

好了上代码:

	public int[] quickSort(int[] array){
		sortPartition(array,0, array.length-1);
		return array;
	}
	
	public void sortPartition(int[] array, int start, int end){
		int i = start;
		int j = end;

		if(i >= j){
			return;
		}
		int pivot = array[start];

		while (i < j){
			while (i < j && array[j] >= pivot){
				j--;
			}
			if(i < j){
				array[i] = array[j]; 
			}
			
			while (i < j && array[i] < pivot){
				i++;
			}
			if(i < j){
				array[j] = array[i];
			}
			
		}
		array[i] = pivot;
		
		
		sortPartition(array, start, i-1);
		sortPartition(array, i+1, end);
		
	}

好了,然后是感谢时间,

感谢@MoreWindows的白话经典算法系列,我在复习快速排序算法的时候,参考了他的这篇文章:

http://blog.csdn.net/morewindows/article/details/6684558

下一次,计划总结一下堆排序,顺便分析一下堆这个数据结构。另外总结一下外部排序的算法。

另外,祈了个福,希望明天面试顺利!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值