因为之前看一道题目的时候用到了排序算法,所以就想在总结一下这几个常用算法的思想和实现方法。
在写之前先来看一下各种方法的效率比较。
1,冒泡排序法,因为代码注释比较详细,不在多说。主要是相邻元素之间比较然后交换。有两种改进思路也进行了实现。
/**
* 冒泡排序
* 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
* 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
* 针对所有的元素重复以上的步骤,除了最后一个。
* 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
* Created by liuchong on 2017/6/27.
*/
public class BubbleSort {
public void BubbleSort(int[] nums){
for(int i=0; i<nums.length; i++){
for(int j=1; j<nums.length-i; j++){
if(nums[j-1] > nums[j])
swap(nums, j);
}
}
}
public void swap(int[] nums, int i){
int tmp = nums[i];
nums[i] = nums[i+1];
nums[i+1] = tmp;
}
//冒泡排序算法有两种改进方式:
//1,如果又一次循环,并未发生任何交换,则说明数组已经排序完成。可以设置一个标志来表示
//2,记录某次遍历时最后发生数据交换的位置,这个位置之后的数据显然已经有序,不用再排序了。因此通过记录最后发生数据交换的位置就可以确定下次循环的范围了。
public void BubbleSort1(int[] nums){
//最后发生交换的标志位
int last = nums.length;
for(int i=0; i<nums.length; i++){
//再一次循环中是否发生交换的标志
boolean flag = true;
for(int j=1; j<last; j++){
if(nums[j-1] > nums[j]) {
swap(nums, j);
last = j;
flag = false;
}
}
if(flag)
break;
}
}
}
2,选择排序,这种方法非常直观,就是选择剩余数组中的最小(大)值,然后放在排序好的数组的最后面。这里可以用交换两个元素的值来实现。
/**
* 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
*再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
*以此类推,直到所有元素均排序完毕。
* Created by liuchong on 2017/6/27.
*/
public class SelectionSort {
public void SelectionSort(int[] nums){
for(int i=0; i<nums.length; i++){
int min = i;
for(int j=i+1; j<nums.length; j++){
if(nums[j] < nums[min])
min = j;
}
swap(nums, i, min);
}
}
public void swap(int[] nums, int i, int j){
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
3,直接插入排序,这种方法跟选择排序法“相反”,他是按照顺序选择元素,然后将其插入到已经排序好的数组的适当位置。而选择排序是选择适当的元素插入到最后。
/**
* 1,从第一个元素开始,该元素可以认为已经被排序
* 2,取出下一个元素,在已经排序的元素序列中从后向前扫描
* 3,如果被扫描的元素(已排序)大于新元素,将该元素后移一位
* 4,重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
* 5,将新元素插入到该位置后
* 重复步骤2~5
* Created by liuchong on 2017/6/27.
*/
public class InsertionSort {
public void InsertionSort(int[] nums){
for(int i=1; i<nums.length; i++){
if(nums[i] < nums[i-1]){
int tmp = nums[i];
int index = i;
for(int j=i-1; j>=0; j--){
if(nums[j] > nums[i]) {
nums[index] = nums[j];
index = j;
}else
break;
}
nums[index] = tmp;
}
}
}
}
4,希尔排序法。上面三种都是比较简单的,时间复杂度也很高。从希尔排序开始变得稍微有些技巧。如下面注释中所说希尔排序是基于插入排序的。因为插入排序在数组接近排序好的时候效率是非常快的,所以希尔排序就先使用大跨步进行提前排序,这样最后进行插入排序的时候效率就高多了。
这里可以参考这个链接,讲得很好
/**
* 希尔排序是插入排序的改进版,它的作法不是每次一个元素挨一个元素的比较。
* 而是初期选用大跨步(增量较大)间隔比较,使记录跳跃式接近它的排序位置;
* 然后增量缩小;最后增量为 1 ,这样记录移动次数大大减少,提高了排序效率。
* 希尔排序对增量序列的选择没有严格规定。
* 这样做的好处是每次排序完成,数组都会趋近于有序的,而最后一步就是简单插入排序,
* 插入排序对有序数组效率极高,几乎是线性的,所以效率提升
* 参考链接:http://bubkoo.com/2014/01/15/sort-algorithm/shell-sort/
* Created by liuchong on 2017/6/27.
*/
public class ShellSort {
public void ShellSort(int[] nums){
int gap = nums.length/2;
while(gap > 0){
for(int i=gap; i<nums.length; i++){
for(int j=i; j>=gap; j-=gap){
if(nums[j] < nums[j-gap])
swap(nums, j, j-gap);
else
break;
}
}
gap = gap/2;
}
}
public void swap(int[] nums, int i, int j){
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
5,归并排序。先将数组分裂成单个元素,然后再两两合并,其实就是将两个已经排序号的数组合并。使用递归的方法效果更好。
①把 n 个记录看成 n 个长度为1的有序子表;
②进行两两归并使记录关键字有序,得到 n/2 个长度为 2 的有序子表;
③重复第②步直到所有记录归并成一个长度为 n 的有序表为止。
/**
* 归并排序是采用分治法的一个非常典型的应用。归并排序的思想就是先递归分解数组,再合并数组。
* 先考虑合并两个有序数组,基本思路是比较两个数组的最前面的数,谁小就先取谁,取了后相应的指针就往后移一位。
* 然后再比较,直至一个数组为空,最后把另一个数组的剩余部分复制过来即可。
* 再考虑递归分解,基本思路是将数组分解成left和right,如果这两个数组内部数据是有序的,
* 那么就可以用上面合并数组的方法将这两个数组合并排序。如何让这两个数组内部是有序的?
* 可以再二分,直至分解出的小组只含有一个元素时为止,此时认为该小组内部已有序。然后合并排序相邻二个小组即可。
* Created by liuchong on 2017/6/28.
*/
public class merge_sort {
public static void MergeSort(int[] arr, int low, int high)
{
//使用递归的方式进行归并排序,所需要的空间复杂度是O(N+logN)
int mid = (low + high)/2;
if(low < high)
{
//递归地对左右两边进行排序
MergeSort(arr, low, mid);
MergeSort(arr, mid+1, high);
//合并
merge(arr, low, mid, high);
}
}
//merge函数实际上是将两个有序数组合并成一个有序数组
//因为数组有序,合并很简单,只要维护几个指针就可以了
private static void merge(int[] arr, int low, int mid, int high)
{
//temp数组用于暂存合并的结果
int[] temp = new int[high - low + 1];
//左半边的指针
int i = low;
//右半边的指针
int j = mid+1;
//合并后数组的指针
int k = 0;
//将记录由小到大地放进temp数组
for(; i <= mid && j <= high; k++)
{
if(arr[i] < arr[j])
temp[k] = arr[i++];
else
temp[k] = arr[j++];
}
//接下来两个while循环是为了将剩余的(比另一边多出来的个数)放到temp数组中
while(i <= mid)
temp[k++] = arr[i++];
while(j <= high)
temp[k++] = arr[j++];
//将temp数组中的元素写入到待排数组中
for(int l = 0; l < temp.length; l++)
arr[low + l] = temp[l];
}
}
6,快速排序。
快速排序和归并排序差不多,一个是将数组分裂成单个元素,然后再两两归并。另一个是选定一个元素,将数组的所有元素分成比他大的,比他小的,然后一直做下去。代码如下:
/**
* 对于一个数组,我们选定一个基准数据(例如:数组中的最后一个或者第一个元素),
* 剩下的数据组成一个新的数组,然后遍历这个新数组中的每一个元素,
* 分别与基准元素进行对比,分别将小于基准元素和不小于基准元素的数据区分开来,
* 这个时候基准元素在总的数组中的位置就确定了。然后,
* 在分别对这个两个数组进行相同的操作,直到每一个元素的位置都唯一确定下来。
* Created by liuchong on 2017/6/28.
*/
public class QuickSort {
public void QuickSort(int[] nums){
sort(nums,0,nums.length-1);
}
private void sort(int[] a,int low,int high){
//左
int l =low;
//右
int h = high;
//基准值
int k = a[low];
//判断一趟是否完成
while(l<h){
//若顺序正确就比较下一个
while(l<h&&a[h]>=k)
h--;
if(l<h){
int temp = a[h];
a[h] = a[l];
a[l] = temp;
l++;
}
while(l<h&&a[l]<=k)
l++;
if(l<h){
int temp = a[h];
a[h] = a[l];
a[l] = temp;
h--;
}
}
if(l>low) sort(a,low,l-1);
if(h<high) sort(a,l+1,high);
}
}
- ').addClass('pre-numbering').hide(); (this).addClass(′has−numbering′).parent().append( numbering); for (i = 1; i