day05 插入,归并,快速,选择排序算法总结

前言

前面几天在学习基础的排序算法,从O(n²)级别到O(n)级别的算法,今天就不学习新的排序算法了,停下来把之前学习的进行巩固一下,并且好好分析一下这几种算法之间的区别,以及他们分别适用于什么样的情景。

 

1,选择排序

 

1.1 排序思想

选择排序的思想正如它的名字,重点就在选择。怎么个选择法呢?

选择排序会从当前数组中,每次选出一个最小的数字放在最前方。第一次遍历选择出一个最小的,第二次遍历选择出一个第二小的,以此类推,每次遍历,无序的元素就逐渐减少,当剩下最后一个元素时,整个数组就处在了一个有序的状态。

选择排序很简单,是属于O(n²)级别的排序算法,时间效率也是不如人意,但是它的特点就是简单,在一些情况下我们不需要去考虑资源的问题,那么我们就可以使用这种排序算法,因为我们不需要去花费太多的时间去设计算法。

 

1.3 代码实现

话不多说直接上代码:

	private static void selectSort(int [] arr) {
		int len = arr.length;
		for(int i = 0;i < len;i++) {
			int minIndex = i;
			for(int j = i + 1;j < len;j++) {
				if(arr[j] < arr[minIndex]) {
					minIndex = j;
				}
			}
			SortTestHelper.swap(arr, i, minIndex);
		}
	}

 

2,插入排序

 

2.1 算法思想

选择排序的重点是选择,那么插入排序的重点当然是插入了啦。具体怎么实现呢?

遍历数组,让需要插入的元素依次与它前面的数字进行比较,直到找到一个合适的位置。那么什么是合适的位置呢?那就是当它发现它前面的元素比它小的时候,这时候这个位置就是合适的了。如果它前面的元素比它大,那就交换位置,然后继续和前面的元素进行比较。

 

2.2 代码实现

话不多说,直接上代码:

	private static void insertSort(int [] arr) {
		int len = arr.length;
		int value;
		int j;
		for(int i = 1;i < len;i++) {
			value = arr[i];
			for(j = i;j > 0;j--) {
				if(value < arr[j-1]) {
					arr[j] = arr[j-1];
				}else {
					break;//这里不能用continue,continue不会结束本轮的for循环,这里需要结束内圈的整个循环
				}
			}
			arr[j] = value;
		}
	}

 

3,归并排序

 

3.1 归并排序思想

归并排序是一种递归排序的算法,什么是递归呢?说简单点就是自己调用自己,说的复杂点就是不断的压栈和出栈,这不是我们现在讨论的重点,有兴趣的同学可以自己去搜索相关的资料。

归并排序其实分为两个过程,一个是分割,另外一个就是排序。

先是将整个数组不断等份分割(这里的等分指的是除以二,两个部分不一定等长),当分成最小等分后。就进行第二个步骤,就是归并,自底向上进行合并。

 

 

 

3.2 代码实现

废话不多说,直接上代码:

这里温馨提醒一波,排序算法一定要注重边界问题,必须明白自己的边界才能准确的进行排序。

private static void mergeSort(int [] arr ,int left,int right) {
		if(left >= right) {
			return;
		}
		
		int mid = (right - left)/2 + left;
		mergeSort(arr,left,mid);
		mergeSort(arr,mid+1,right);
		
		merge(arr,left,right,mid);
	}

		
	private static void merge(int [] arr,int left ,int right,int mid) {
		
		int [] arr1 = SortTestHelper.copyArray(arr);
		int i = left;
		int j = mid + 1;
		
		for(int k = left;k <= right;k++) {
			if(i > mid) {
				arr1[k] = arr[j++];
				continue;//注意这里的continue
			}else if(j > right) {
				arr1[k] = arr[i++];
				continue;
			}
			
			if(arr[i] < arr[j]) {
				arr1[k] = arr[i++];
			}else {
				arr1[k] = arr[j++];
			}
		}
		
		int index = left;
		while(index <= right) {
			arr[index] = arr1[index];
			index++;
		}
	}	

 

 

 

4,快速排序

4.1 排序思想

快速排序和归并排序都是O(n)级别的排序算法,也是现在最重要的排序算法之一,希望大家都能熟练掌握这种排序算法。

快排其实分为好几种,分别是:基础快排,二路快排,三路快排

几种排序在前边的博客我都有详解,这里就不过多赘述了,有兴趣的小伙伴可以参考我之前的博客。快排的思想就是将一个数通过操作,放到它排好序后应该放在的位置上,然后再去排其他的数字。

 

*

* 双路排序是两个索引,所以三路排序就是三个索引了

* 单路和双路排序都是将区间划分成两个区域:小于(等于)参考值,大于(等于)参考值

* 三路排序最终的结果就是将区间划分成三个区域:小于参考值,等于参考值,大于参考值

*

* 所以我们这次返回的就是等于参考值的区间,我们返回一个数组,数组中存储两个元素,一个是等于我们参考值的区间的左边界,另外一个是右边界

* arr[left + 1 ... lt] < value

* arr[gt ... right] > value

* arr[lt+1 ... i) == value

 

这是三路快排的思想,也希望大家能熟悉三路快排,这个版本应该是最常用的,这个明白了,其他两个版本也就很简单了。

 

4.2代码实现

话不多说,直接上代码:

package com.smarking.lzy.part1;
/*
 * 三路快速排序
 * 
 * 双路排序是两个索引,所以三路排序就是三个索引了
 * 单路和双路排序都是将区间划分成两个区域:小于(等于)参考值,大于(等于)参考值
 * 三路排序最终的结果就是将区间划分成三个区域:小于参考值,等于参考值,大于参考值
 * 
 * 所以我们这次返回的就是等于参考值的区间,我们返回一个数组,数组中存储两个元素,一个是等于我们参考值的区间的左边界,另外一个是右边界
 * arr[left + 1 ... lt] < value
 * arr[gt ... right] > value
 * arr[lt+1 ... i) == value
 * 
 * 边界很重要!!!问题出错都是处在边界的理解,一定要好好理解边界问题!
 * */
public class QuitSortThird {
	
	public static void sort(int [] arr ,int left ,int right) {
		//设置递归的出口
		if(left >= right) {
			return;
		}
		
		int [] positions = partition(arr,left,right);
		sort(arr,left,positions[0] - 1);
		sort(arr,positions[1],right);
	}
	
	//重点,返回等于参考值的区间
	private static int [] partition(int [] arr,int left,int right) {
		//创建一个数组,用来存储区间的左右边界,默认第一个存左边界,第二个存有边界

		
		int [] positions = new int[2];
		//随机选择一个位置作为参照值
		int index = (int) (Math.random() * (right - left + 1)+left);
		//int index = 6;
		SortTestHelper.swap(arr, left, index);
		int value = arr[left];
		
		//定义三个索引,分别是左边界,有边界和用来遍历的索引
		int i = left + 1;//用来遍历整个数组的索引
		int lt = left;//左边界
		int gt = right + 1;//右边界
		
		while(i < gt) {
			if(arr[i] < value) {
				SortTestHelper.swap(arr, i, lt + 1);
				
				i++;
				lt++;
			}else if(arr[i] > value) {
				SortTestHelper.swap(arr, i, gt - 1);
				gt--;
				//i++;这个地方不能自增,因为右边的元素都是没有进行遍历的,如果自增,就会跳过该元素
			}else {
				i++;
			}
		}
		
		SortTestHelper.swap(arr, left, lt);
		positions[0] = lt;
		positions[1] = gt;
		return positions;
	}
	
	
	public static void main(String[] args) {
		int testTime = 10000;
		int maxValue = 100;
		int maxSize = 10000;
		boolean success = true ;
		for(int i = 0;i < testTime;i++) {
			int [] arr1 = SortTestHelper.generateRandomArray(maxSize, maxValue);
			//int [] arr1 = {1 ,2 ,-1, 5 ,-2, 4, 2, 5 };
			int [] arr2 = SortTestHelper.copyArray(arr1);
			int [] arr3 = SortTestHelper.copyArray(arr1);
			sort(arr1,0,arr1.length-1);
			SortTestHelper.comparator(arr2);
			if(!SortTestHelper.isEqual(arr1, arr2)) {
				success = false;
				SortTestHelper.printArray(arr3);
				SortTestHelper.printArray(arr1);
				break;
			}
		}
		
		if(success) {
			System.out.println("Nice!!!");
		}else {
			System.out.println("Fuck!");
		}
	}

}

 

 

 

你可能感兴趣的:(算法)