插入排序核心思想:
将无序区域的第一个元素 插入到有序区域中属于自己的位置
1. 交换插排
public class _04_Insert<T extends Comparable<T>> extends Sort<T> {
@Override
protected void sort() {
for (int end = 1; end < array.length; end++) {
for(int i = end;i>0;i--){
if(cmp(array[i],array[i-1])<0){
swap(i,i-1);
}else{
break;
}
}
}
}
}
【_03_HeapSort】
稳定性:false 耗时:0.011s(11ms) 比较:51.06万 交换:2.00万
------------------------------------------------------------------
【_02_Select】
稳定性:true 耗时:0.396s(396ms) 比较:2.00亿 交换:2.00万
------------------------------------------------------------------
【_04_Insert】
稳定性:true 耗时:0.425s(425ms) 比较:1.00亿 交换:1.00亿
------------------------------------------------------------------
【_01_Bubble】
稳定性:true 耗时:1.281s(1281ms) 比较:2.00亿 交换:1.00亿
------------------------------------------------------------------
【_01_Bubble_3】
稳定性:true 耗时:1.848s(1848ms) 比较:2.00亿 交换:1.00亿
------------------------------------------------------------------
【_01_Bubble_2】
稳定性:true 耗时:1.85s(1850ms) 比较:2.00亿 交换:1.00亿
------------------------------------------------------------------
2.挪动插排
public class _04_Insert2<T extends Comparable<T>> extends Sort<T> {
//不挨个交换,找到目标位置index 将(index,end)的元素全部后移
@Override
protected void sort() {
for(int end = 1;end < array.length;end++){
T ele = array[end];
for(int i = end -1;i>=0;i--){
if(cmp(array[i],ele)>0){
array[i+1] = array[i];
}else{
//ele >= array[i]
array[i+1] = ele;
break;
}
}
}
}
}
【_03_HeapSort】
稳定性:false 耗时:0.011s(11ms) 比较:51.08万 交换:2.00万
------------------------------------------------------------------
【_02_Select】
稳定性:true 耗时:0.394s(394ms) 比较:2.00亿 交换:2.00万
------------------------------------------------------------------
【_04_Insert】
稳定性:true 耗时:0.576s(576ms) 比较:1.00亿 交换:1.00亿
------------------------------------------------------------------
【_04_Insert2】
稳定性:true 耗时:0.601s(601ms) 比较:1.52亿 交换:0
------------------------------------------------------------------
【_01_Bubble】
稳定性:true 耗时:1.292s(1292ms) 比较:2.00亿 交换:1.00亿
------------------------------------------------------------------
【_01_Bubble_3】
稳定性:true 耗时:1.832s(1832ms) 比较:2.00亿 交换:1.00亿
------------------------------------------------------------------
【_01_Bubble_2】
稳定性:true 耗时:1.856s(1856ms) 比较:2.00亿 交换:1.00亿
------------------------------------------------------------------
3. 二分搜索
二分搜索的前提是有序序列;只有有序序列才能够使用二分搜索
- 如果是无序数组,从第 0 个位置开始遍历搜索,平均时间复杂度:O(n)
- 如果是有序数组,可以使用二分搜索,最坏时间复杂度:O(logn)
3.1 递归实现 查找目标数据应插入的位置
public class _05_BinarySearch {
//此二分搜索方法用于寻找V在有序数组中的索引位置
//todo 1.递归实现
public static int search(int[] array, int v){
if(array == null || array.length == 0) return -1;
return search(array,v,0,array.length-1);
}
private static int search(int[] array,int v,int low,int high){
int mid = (low+high)>>1;
if(mid == high){
if(array[low] <= v){
return low+1;
}else{
return low;
}
}
if(array[mid] > v){
return search(array,v,low,mid-1);
}else if(array[mid] < v){
return search(array,v,mid+1,high);
}else {
return mid;
}
}
}
递归终止条件:mid == high
,不能写low == high
low == high
只包含这一种情况
mid == high
包含 low == high
和 high == low -1
mid == low
包含 low == high
和 low == high -1
当low == high -1的时候,mid == low,所以其实是在比较array[low]和goal的关系:
- 如果array[low] < goal ,那么low + 1,此时low == high,此时用low == high没问题;
- 如果array[low] > goal ,high = mid -1 也就是high = low -1,此时已经交叉了:mid = (low - 1 + low) /2 = low -1 ,所以按照mid == high 做终止条件!
简而言之:要么在low == high处终止,要么在 low = high - 1处终止;那么这两种情况都可以用 mid == high来概括
3.1 迭代实现 查找目标数据应插入的位置
// todo 2.迭代实现
public static int search2(int[] array, int v){
if(array == null || array.length == 0) return -1;
int high = array.length-1;
int low = 0;
int mid;
while(high > low){
mid = (high + low )>>1;
if(array[mid] == v){
return mid;
}else if(array[mid] > v){
high = mid - 1; //high = low 那么 low位置<V
}else{
low = mid + 1; //high = low 那么 low位置>V
}
}
//到这里说明 high = low
if(array[low] >= v){
return low;
}else{
return low+1;
}
}
4. 挪动插排 + 二分搜索
ublic class _07_Insert_BinarySearch<T extends Comparable<T>> extends Sort<T> {
private int searchIndex(T[]array,T v){
return searchIndex(array,v,0,array.length-1);
}
private int searchIndex(T[] array,T v,int low,int high){
if(array == null || array.length == 0) return -1;
int mid;
while(high > low){
mid = (high + low )>>1;
if(array[mid] == v){
return mid;
}else if(cmp(array[mid] ,v)>0){
high = mid - 1;
}else{
low = mid + 1;
}
}
//到这里说明 high = low
if(cmp(array[low],v)>0){ //如果这里改为>= 就不是稳定排序了
return low;
}else{
return low+1;
}
}
@Override
protected void sort() {
for (int i = 1; i < array.length; i++) {
//[0,i)是有序区域 arr[i] 是待插元素
T tmp = array[i];
int insertIndex = searchIndex(array, tmp,0,i-1); //logN
//将[insertIndex,i-1]范围内的有序元素 往后移动1位
for(int j = i;j>insertIndex;j--){
array[j] = array[j-1];
}//n
//将元素插进去
array[insertIndex] = tmp;
}
}
// n*(n+logN) => 因此时间复杂度还是O(n平方) 只不过优化了搜索 索引的位置
}
【_03_HeapSort】
稳定性:false 耗时:0.012s(12ms) 比较:51.06万 交换:2.00万
------------------------------------------------------------------
【_07_Insert_BinarySearch】
稳定性:true 耗时:0.291s(291ms) 比较:26.09万 交换:0
------------------------------------------------------------------
【_02_Select】
稳定性:true 耗时:0.394s(394ms) 比较:2.00亿 交换:2.00万
------------------------------------------------------------------
【_04_Insert】
稳定性:true 耗时:0.572s(572ms) 比较:9977.75万 交换:9975.75万
------------------------------------------------------------------
【_04_Insert2】
稳定性:true 耗时:0.661s(661ms) 比较:1.61亿 交换:0
------------------------------------------------------------------
【_01_Bubble】
稳定性:true 耗时:1.314s(1314ms) 比较:2.00亿 交换:9975.75万
------------------------------------------------------------------
【_01_Bubble_2】
稳定性:true 耗时:1.833s(1833ms) 比较:2.00亿 交换:9975.75万
------------------------------------------------------------------
【_01_Bubble_3】
稳定性:true 耗时:1.844s(1844ms) 比较:2.00亿 交换:9975.75万
------------------------------------------------------------------
Process finished with exit code 0
复杂度与稳定性
使用了二分搜索后,只是减少了比较次数,但插入排序的平均时间复杂度依然是 O(n2)
最坏、平均时间复杂度:O(n2)
最好时间复杂度:O(n)
空间复杂度:O(1)
属于稳定排序