排序算法一直接插入排序
算法思想:
每次将一个待排序的记录按照其关键字大小插入到前边已排好序的子序列中,直到全部记录插入完成为止。**
图中下标为0-4的元素已经排好序了,下标为5-6的元素为待排序元素,现在指针或者下标指向5号元素13,我们需要将13插入到其左边排好序的元素中,即可完成对下标为5的元素的排序,之后依次对未排序元素进行上述操作即可。观察可知,待排序元素的左边的元素因为已经排好序了,所以总是有序的。
代码实现:
public class insertSort {
public static void main(String[] args) {
int[] nums = {3,8,4,5,6,8,9,0};
insort(nums,nums.length);
for(int num:nums){
System.out.print(num);
}
}
public static void insort(int[] nums, int n){
int i,j;
for(i=1;i<n;++i){
int temp = nums[i];
if(nums[i]<nums[i-1]){
for(j=i-1; j>=0 && nums[j]>temp ;--j){
nums[j+1] = nums[j];
}
nums[j+1] = temp;
}
}
}
}
另外还有带哨兵的简单插入排序的算法实现:
这种写法简化了第二重循环中的判断条件,数组的第一个元素并不存储待排序元素。
public class insertSort {
public static void main(String[] args) {
int[] nums = {0,3,8,4,5,6,8,9,0};
insort(nums,nums.length);
for(int num:nums){
System.out.print(num);
}
}
public static void insort(int[] nums, int n){
int i,j;
for(i=2;i<n;++i){
if(nums[i]<nums[i-1]){
nums[0] = nums[i];
for(j=i-1; nums[0]<nums[i] ;--j){
nums[j+1] = nums[j];
}
nums[j+1] = nums[0];
}
}
}
}
空间时间复杂度
空间复杂度: O(1)
时间复杂度:
主要来自于对比关键字、移动元素。若有n个元素,则需要n-1趟处理。
最好时间复杂度:原始数据有序,n-1趟处理总共需要n-1次关键字对比,时间复杂度为O(n)
最坏时间复杂度:原始数据逆序【对于不带哨兵的代码实现来说】
第1趟:对比关键字1次,移动元素2次;
第2趟:对比关键字2次,移动元素3次;
第
i
i
i趟:对比关键字
i
i
i次,移动元素
i
+
1
i+1
i+1次;
第
n
−
1
n-1
n−1趟:对比关键字
n
−
1
n-1
n−1次,移动元素
n
n
n次;
最坏情况时间复杂度为:O(n^2)
最坏时间复杂度:原始数据逆序【对于带哨兵的代码实现来说】
第1趟:对比关键字2次,移动元素3次;
第2趟:对比关键字3次,移动元素4次;
第
i
i
i趟:对比关键字
i
+
1
i+1
i+1次,移动元素
i
+
2
i+2
i+2次;
第
n
−
1
n-1
n−1趟:对比关键字
n
n
n次,移动元素
n
+
1
n+1
n+1次;
最坏情况时间复杂度为:O(n^2)
平均时间复杂度为:O(n^2)
算法稳定性: 因为对于两个相同关键字的排序并没有破坏数据原始的相对顺序,因此插入排序算法是稳定的。
简单优化
因为待排序关键字要插入的序列总是有序,所以可以通过二分查找来寻找待排序关键字的插入位置。
代码实现:
public static void insort2(int[] nums, int n){
int i,j,low,high, mid;
for(i=1;i<n;++i){
int temp = nums[i];
low =0; high = i-1;
while(low<=high){
mid = (low + high) /2;
if(nums[mid]>temp){
high = mid - 1;
}else {
low = mid + 1;
}
}
for(j=i-1;j>=high+1;--j){
nums[j+1] = nums[j];
}
nums[high+1] = temp;
}
}
比起直接插入排序,比较关键字的次数减少了,但是移动元素的次数没变,时间复杂度依然是O(n^2)