初级排序算法之选择排序和插入排序及其优化
前情摘要
现在我们学了一些基础的数据结构,如链表,队列,栈,等等。现在我们开始学习一些非常基础的排序算法。今天我们要讲的是选择排序,插入排序以及对插入排序的优化——希尔排序。
选择排序
基础原理:
比如说我们的输入模型是有限的数组,对数组中的元素进行排序。对于选择排序来说就是从该数组中找出第一个最小的元素和数组中的第一个元素交换。(如果数组中的第一个元素就是最小的,我们就让它自己和自己交换)。然后在从数组中剩下的元素中找出第二小的元素和数组中的第二个元素进行交换。若干次之后我们就会得到一个已经排好序的数组。
动画实现:
选择排序
代码实现
public static void select_sort(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;}//找到最小元素。
}
int temp = nums[i];//将最小元素与第一个元素交换,将第二小的元素和第二个数组中的元素进行交换。
nums[i] = nums[min];
nums[min] = temp;
}
}
复杂度分析
该代码进行了N次交换和N2/2次比较。总的时间复杂度为O(N2)。
优点:交换次数很少,为线性时间,事实选择排序算法可能是所有算法中交换元素次数最少的算法。
缺点:浪费时间,和用户输入的模型无关,排序一个有序的数组和一个随机排序的数组花费的时间一样长。以后我们学习的算法中更善于利用用户数据的初始状态,比如部分有序的数组耗费时间将会短一点。
插入排序:
基础原理:
我们假设数组中的前p个元素已经有序,我们把第P+1元素作为待处理的元素,如果第p个元素大于第p+1个元素,我们遍把第p+1个元素插入到前面有序的部分,如果第p个元素小于第p+1个元素,则从0到第p+1个元素全都有序。以此类推,我们就能得到有序的数组。一般我们让p=1。
动画实现:
插入排序
代码实现
public static void insert_sort(int[] nums) {
for (int i = 1; i < nums.length; i++) {
int x = nums[i];//假设第一项已经有序,我们从第二项开始算起。
int j = i - 1;
for (; j >= 0 && nums[j] > x; j--) {//临界条件
nums[j + 1] = nums[j];//如果前面的元素大于后面的元素,我们就把后面的元素往前移一位
}
nums[j + 1] = x;//把待处理元素插入到合适的位置。
}
}
时间复杂度分析;
平均时间复杂度:O(N2);
最优的时间复杂度:O(N);
插入排序对于待处理的数组中的元素部分有序的算法尤其适用。一般情况下,插入排序和选择排序各有优缺点,但是插入排序的效果要比选择排序的效率高。但是插入排序交换元素的次数较多。选择排序算法是所有排序算法中交换元素次数最少的,线性阶的算法。接下来我们在讲一下插入排序的改良版——希尔排序。
希尔排序:
希尔排序是插入排序的改良版,希尔排序也叫插入排序的大跨步版,插入排序每次只能走一格,但是希尔排序可以自由设置步数,使数组中的元素形成部分有序的局面,然后在用插入排序算法,这样大大提高了插入排序的效率。
代码实现:
public static void shell_sort(int[] nums){
int N = nums.length;
int step = 1;
while(step < N/3)step = step*3+1;//step为间隔的元素。
while(step>=1){
for(int i =1;i < nums.length;i++){
int x = nums[i];
int j = i-step;
for(;j>=0&&nums[j]>=x;j-=step){
nums[j+step] = nums[j];9//其实就相当于把每隔step步的元素给挑出来。进行排序后再放回去。
}
nums[j+step] = x;
}
step=step/3;
}
}
就现在来说还没有研究表明,什么样的间隔是最有效的,一般来说我们按照上图中的间隔,上图的间隔方法,取自算法第四版。
希尔排序在某种情况下甚至比我们后面要讲到的某些非常高效的算法还要快。
好了,这就是初级排序方法之选择排序和插入排序以及希尔排序的基本知识点了。以后我们会学习归并排序。
本人良弓,初来乍到,请多关照~