Notes: 排序算法小结 (Sorting)

最近两天,复习了几个经典的排序算法。在这里,写一份小结,粗略回顾。


1)Bubble Sort

顾名思义,就是把最小的数如冒泡一样,在每次的遍历当中,移到最顶的位置。

public void BubbleSort(int[] input){
		for(int i=0;i<input.length;i++){
			for(int j=input.length-1;j>i;j--){
				if(input[j]<input[j-1]){
					swap(input,j,j-1);
				}
			}
		}
	}

分析该排序算法的思路是考量比较的次数和交换的次数。比较发生了(n-1)+(n-2)+(n-3)+...+1 ~= O(n^2). 交换的次数在最坏的情况下,也就是从大到小的初始顺序下,同样发生了(n-1)+(n-2)+(n-3)+...+1 ~= O(n^2)次。


2)Selection Sort:


冒泡排序中,有许多的交换移动是可以避免的。我们可以选好了当前要确定的位置,然后用选择当前剩下的子数组中最小的值去填充需要确定的位置。省去不必要的交换。因为是选择剩下的子数组的最小值去填充,称谓选择排序。


public void selectSort(int[] input){
		for(int i=0;i<input.length;i++){
			int min_index = i;
			for(int j=i+1;j<input.length;j++){
				if(input[min_index]>input[j]){
					min_index = j;
				}
			}
			swap(input,i,min_index);
		}
	}


分析该排序算法,比较的次数同样是(n-1)+(n-2)+...+1=O(n^2)。交换的次数在最坏的情况下是O(n)。


3)Insertion Sort


插入排序就如整理扑克牌一样,把一张牌直接在一堆排中,找一个位置,使得在这个位置之后的都比这张牌大,在这位置前的都比这张牌小。但我们需要注意的是,这里的插入是指插入到已经排好序的序列中。一开始,我们假设第一张是已经排好序的。从数组的第二个元素也就是坐标为1的元素开始抽牌插入。


public void insertionSort(int[] input){
		int temp = 0;// the temporary store for the card I take to insert
		for(int i=1; i<input.length;i++){
			temp = input[i];
			int j=i;
			for(;j>0;j--){
				if(temp<input[j-1]){
					input[j] = input[j-1];
				}else{
					break;
				}
			}
			input[j] = temp;		
		}
	}

分析算法,可知在最坏的情况下比较和移动的次数都是O(n^2)。

4) Merge Sort


归并排序是一种分治算法(Divide and Conquer),分治算法的典型实现是递归(Recursion)。递归最重要的是想好结束的条件,而画好递归树验证逻辑。思想简单,不停地分隔数组。当分到长度为1时,停止分割,继而进行组合,组合时确保新的数组有序。最终组合成一个大的有序数组。


public int[] divideMerge(int[] input, int begin, int end){
		int[] return_list = new int[end-begin+1];
		if(begin == end){
			return_list[0] = input[begin];
			return return_list;
		}else{
			int mid_index = (begin+end)/2;
			int[] temp_array1 = divideMerge(input,begin,mid_index);
			int[] temp_array2 = divideMerge(input,mid_index+1,end);
			return_list = mergeArray(temp_array1,temp_array2);
		}
		return return_list;
	}
	
	private int[] mergeArray(int[] input1, int[] input2){
		int[] return_array = new int[input1.length+input2.length];
		int i = input1.length-1; 
		int j = input2.length-1;
		int k = return_array.length-1;
		while(i>=0 && j>=0 && k>=0){
			if(input1[i]<input2[j]){
				return_array[k--] = input2[j--];
			}else{
				return_array[k--] = input1[i--];
			}
		}
		while(i>=0 && k>=0){
			return_array[k--] = input1[i--];
		}
		while(j>=0 && k>=0){
			return_array[k--] = input2[j--];
		}
		return return_array;
	}

写merge算法的时候,注意合并数组的填入顺序应和扫描两个分数组的顺序一致,且和小于的逻辑一直。上述代码采用大于的逻辑来写,反方向写入。

分析:归并排序是第一个空间复杂度是O(n+log2(n))的算法,需要另外开辟存储空间。其中n是开辟的存储结果的数组,而log2(n)是由于递归时需要开辟的栈空间。算法的时间复杂度可以根据调用次数统计。如上述例子,4个长度的数组,需要调用:(0,1) --> (0,0)/ (1,1); (2,3) --> (2,2),(3,3). 再加上总的:(0,4)。总共是7次。为n*log2(n) - 1次。所以复杂度为O(nlog2(n))。(书上的解释是,所有元素扫描一次需要n,接着合并的时候,根据完全二叉树深度可知,归并排序需要进行log2(n)次)。


5)QuickSort


快速排序基于一个不断找轴值,使得轴值左边的数小于轴值,轴值右边的数要大于轴值。但两边各自的数不要求有序。在不停地递归当中,直到轴值begin和pivot相等、 pivot + 1和end相等。这样就确保所有的数字有序。


public void quickSort(int[] input, int begin, int end){
		if(begin==end){
			return;
		}else{
			int pivot = partition(input, begin, end);
			quickSort(input,begin,pivot);
			quickSort(input,pivot+1,end);
		}
	}
	
	private int partition(int[] input, int begin, int end){
		while(begin<end){
			while(begin<end && input[end] >= input[begin]){
				end--;
			}
			if(begin<end){
				swap(input,begin,end);
			}
			while(begin<end && input[end] >= input[begin]){
				begin++;
			}
			if(begin<end){
				swap(input,begin,end);
			}
		}
		return begin;
	}


分析快速排序可知,其算法复杂度是O(nlog2(n))。空间复杂度是:O(log2(n))。因为采用了递归。










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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值