插入排序
排序原理:
1.把所有的元素分为两组,已经排序的和未排序的;
2.找到未排序的组中的第一个元素,向已经排序的组中进行插入;
3.倒叙遍历已经排序的元素,依次和待插入的元素进行比较,直到找到一个元素小于等于待插入元素,那么就把待插入元素放到这个位置,其他的元素向后移动一位;
原理如图:
第一次操作: 指针指向第二个元素, 将其与前一个元素进行比较
如果满足条件, 则进行交换, 同时指针前移一位, 重复上述步骤, 直到指针到达数组的头
否则说明已经成有序列, 退出当前循环;
第二次操作: 指针指向第三个元素, 将其与前一个元素进行比较…
/**
* @author 悠一木碧
* 插入排序类
*/
public class Insert {
/**
* 升序排序
* @param a
*/
public static void sort(Comparable[] a) {
if (null == a) {
throw new NullPointerException("空指针数组!");
}
int len = a.length;
// 1. 从第二个元素开始进行排序
// 2. 用temp记录当前索引位置, 索引必须 > 0(当索引为0的时候不需要再往前比较了)
// 3.如果满足条件, 则进行交换, 同时将索引前移一位----temp--
for (int i = 1; i < a.length; i++) {
int temp = i;
while (temp > 0 && bigger(a[temp - 1], a[temp])) {
exchange(a, temp - 1, temp);
temp--;
}
}
}
/**
* 判断a 是否大于 b
* @param a
* @param b
* @return
*/
private static boolean bigger(Comparable a, Comparable b) {
return a.compareTo(b) > 0;
}
/**
* 交换索引index1, index2位置的元素
* @param a
* @param index1
* @param index2
*/
private static void exchange(Comparable[] a, int index1, int index2) {
Comparable comparable = a[index1];
a[index1] = a[index2];
a[index2] = comparable;
}
}
按照大O推导法则,保留函数中的最高阶项那么最终插入排序的时间复杂度为O(N^2).
希尔排序
希尔排序是插入排序的一种,又称“缩小增量排序”,是插入排序算法的一种更高效的改进版本。
之前的插入排序中, 我们一次次比较, 一次次进行元素交换
比如要将图片中的1放到3所在的位置, 我们需要进行两次比较, 两次交换
而希尔排序就是利用这一点进行优化, 直接进行比较并交换
排序原理:
1.选定一个增长量increment,按照增长量increment作为数据分组的依据,对数据进行分组;
2.对分好组的每一组数据完成插入排序;
3.减小增长量,最小减为1,重复第二步操作。
关于增长量increment的确定:
我们使用
int len = a.length;
int increment = 1;
while (increment < len / 2) {
increment = increment * 2 + 1;
}
进行确定, 这样使得increment都是奇数, 且increment > len / 2, 即增长量的初始值在数组长度的一半以上
确定好了增长量increment的初始值后, 再一个需要确定的就是increment的变化方式了
增长量变化趋势: 大 ----> 小----------最终increment = 1 即可
写成代码可以是这样:
// 确保increment >= 1
while (increment > 0) {
...
...
...
increment = increment / 2;
}
现在进行深入分析希尔排序和插入排序的相同点, 不同点
代码实现:
/**
* @author 悠一木碧
* 希尔排序类
*/
public class Shell {
/**
* 对数组内元素进行排序(升序)
* @param a
*/
public static void sort(Comparable[] a) {
if(null == a){
throw new NullPointerException("空指针数组!");
}
// 1.通过数组的长度确定增长量initialIncrement的初始值, 初始化值为1, 通过循环条件(<len / 2)进行确定
// 2.确定初始值后, 确定循环条件, 由于increment需要逐渐减小, 变换方法为increment /= 2
// 3.初始下标为0 + increment, 1+increment, 2+increment.....区间[increment, len-1]内
// 4.数与数之间的间隔为increment, 从当前的数开始, 与前一个数进行比较(假设前面的数成有序列)
// 5.如果不符合期待的大小关系, 则进行交换, 否则退出循环(说明已经成有序列了)
int len = a.length;
int increment = 1;
while (increment < len / 2) {
increment = increment * 2 + 1;
}
while (increment > 0) {
for (int i = increment; i < len; i++) {
int temp = i;
while(temp >= increment && greater(a[temp - increment], a[temp])){
exchange(a, temp - increment, temp);
temp -= increment;
}
}
increment /= 2;
}
}
/**
* 判断x是否大于y
*
* @param x
* @param y
* @return
*/
private static boolean greater(Comparable x, Comparable y) {
return x.compareTo(y) > 0;
}
/**
* 交换a数组中, i,j索引处的值
*
* @param a
* @param i
* @param j
*/
private static void exchange(Comparable[] a, int i, int j) {
Comparable temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
注:
在希尔排序中,增长量h并没有固定的规则,有很多论文研究了各种不同的递增序列,但都无法证明某个序列是最好的
归并排序(和快速排序一样, 应用了分组的思想, 采取递归的策略)
待补充:
代码实现:
/**
* @author 悠一木碧
* 归并排序
*/
public class Merge {
// 辅助数组
private static Comparable[] assitArray;
/**
* 对数组内的元素进行排序
* @param array
*/
public static void sort(Comparable[] array){
if(null == array){
throw new NullPointerException("空指针数组!");
}
int len = array.length;
assitArray = new Comparable[len];
sort(array, 0, len-1);
}
/**
* 对区间[staIndex, endIndex]中的数组元素进行排序
* 递归至 staIndex == endIndex停止
* 在staIndex = endIndex - 1的时候开始调用merge进行归并
* @param array
* @param staIndex
* @param endIndex
*/
private static void sort(Comparable[] array, int staIndex, int endIndex){
if(staIndex < endIndex){
int mid = (staIndex + endIndex) / 2;
sort(array, staIndex, mid);
sort(array, mid + 1, endIndex);
merge(array, staIndex, mid, endIndex);
}
}
/**
* [left, mid], [mid + 1, right]两个子组进行有序组合
* 1. 定义辅助数组的初始指针, 定义原数组的两个区间的初始指针
* 2. 依次移动两个区间的指针, 比较他们指向元素的大小, 按大小放入至辅助数组中
* 3. 将排好序的辅助数组中的元素复制到原数组中
* 两个区间的指针必定会有一个提前走完, 剩下没走完的指针, 将其剩余的元素放到辅助数组中即可
* @param left
* @param mid
* @param right
*/
private static void merge(Comparable[] array, int left, int mid, int right){
int i = left;
int index1 = left;
int index2 = mid + 1;
while(index1 <= mid && right >= index2){
if(bigger(array[index1], array[index2])){
assitArray[i++] = array[index2++];
} else{
assitArray[i++] = array[index1++];
}
}
while(index1 <= mid){
assitArray[i++] = array[index1++];
}
while(index2 <= right){
assitArray[i++] = array[index2++];
}
if (right - left + 1 >= 0)
System.arraycopy(assitArray, left, array, left, right + 1 - left);
}
/**
* 判断a > b
* @param a
* @param b
* @return
*/
private static boolean bigger(Comparable a, Comparable b){
return a.compareTo(b) > 0;
}
/**
* 元素a[index1] 和 元素a[index2]进行交换
* @param a
* @param index1
* @param index2
*/
private static void exchange(Comparable[] a, int index1, int index2){
Comparable comparable = a[index1];
a[index1] = a[index2];
a[index2] = comparable;
}
}
快速排序(递归, 双指针)
待补充:
代码实现:
/**
* @author 悠一木碧
* 快速排序
*/
public class Quick {
public static void sort(Comparable[] a) {
if(null == a){
throw new NullPointerException("传入数组参数为空!");
}
int len = a.length;
if(0 != len){
sort(a, 0, len - 1);
}
}
/**
* 对区间[staIndex, endIndex]中的数组元素进行排序
* 将其拆分为[lowerLimit, demarcationIndex-1], [demarcationIndex+1, upperLimit]两个区间排序
* 递归至 staIndex == endIndex停止--------分成只有一个元素的时候停止
* @param array
* @param lowerLimit
* @param upperLimit
*/
private static void sort(Comparable[] array, int lowerLimit, int upperLimit){
if(lowerLimit >= upperLimit){
return;
}
int demarcationIndex = grouping(array, lowerLimit, upperLimit);
sort(array, lowerLimit, demarcationIndex - 1);
sort(array, demarcationIndex + 1, upperLimit);
}
/**
* 对索引在区间[lowerLimit, upperLimit]内的数组元素进行切分, 并返回切分后分界元素的索引
* 这里是将左边第一个元素作为分界元素, 所以先让右边的指针先动
* @param array
* @param lowerLimit
* @param upperLimit
* @return 返回的是变换后的索引
*/
private static int grouping(Comparable[] array, int lowerLimit, int upperLimit){
// 1. 定义两个指针, 分别指向待切区间的最小索引 和 最大索引的下一个位置
int left = lowerLimit;
int right = upperLimit + 1;
while (true) {
// 2. 先移动右指针, 自右向左扫描, 直到找到比分界元素小的元素停止
// 3. 然后移动左指针, 自坐向右扫描, 直到找到比分界元素大的元素停止
while(less(array[lowerLimit], array[--right])){
if(left == right){
break;
}
}
// 5. 两个指针重合的时候, 交换分界元素和指针当前指向的元素并返回索引
if(left == right){
exchange(array, lowerLimit, right);
return right;
}
while(less(array[++left], array[lowerLimit])){
if(left == right){
break;
}
}
// 4. 交换左右指针指向的元素
if(left == right){
exchange(array, lowerLimit, right);
return right;
} else{
exchange(array, left, right);
}
}
}
/**
* 判断x是否小于y
* @param x
* @param y
* @return
*/
private static boolean less(Comparable x, Comparable y) {
return x.compareTo(y) < 0;
}
/**
* 交换a数组中, i,j索引处的值
* @param a
* @param i
* @param j
*/
private static void exchange(Comparable[] a, int i, int j) {
Comparable temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}