java实现排序(1)-插入排序

引言

排序模块会把所有的排序算法都整理一遍,方便自己以后回身学习。在本篇博文中主要介绍了时间复杂度为O(N^2)的插入排序算法,并用demo进行实现。不过在开始之前需要学习一些基础的数学知识,希望对大家有所帮助。笔者目前整理的一些blog针对面试都是超高频出现的。大家可以点击链接:http://blog.csdn.net/u012403290

技术点

1、4个符号(O, Ω,Θ, o)
在本排序集合模块中,这4个符号用于表示两个函数之间的相对增长率,他们分别读作O(大O), Ω(omega),Θ(theta),o(小O)。

2、4个定义
①如果存在正常数c和n使得当N>=n时,T(N)<=cf(N),则记为T(N)=O(f(N))。

解释下什么意思,如果存在两个函数T(N)和f(N)且两个函数图的线是相交的,那么存在一个点n,使得两者的值相等。其中c是一个常数。比如说10N 和N^2这两个函数,当且仅当N=10的时候两者相交,如下图:
这里写图片描述
在10这个点之前,T(N)的值比f(N)小,但是f(N)的相对增长率大,在10之后会一直比T(N)大,也就是n点之后f(N)的增长率比T(N)大, 写为10N = O(N^2)。

②如果存在正常数c和n使得当N>=n时,T(N)>=cg(N),则记为T(N)=Ω(g(N))。

和上面①的定义是差不多的,只不过是相对增长率反过来了,n点之后g(N)的增长率比T(N)小。上面的例子在这里就变成了T(N)= N^2,g(N)=10N了,也就是N^2 = Ω(10N)。

③当T(N)=O(h(N))且T(N) = Ω(h(n))时,可以用T(N) = Θ(h(N))。

现在很好理解了,意思就是两个函数的增长率是相同的,那么就可以用Θ表示。

④如果存在正常数c和n使得当N>n时,T(N)< cp(N),则记为T(N)=o(p(N))。
乍眼一看是不是感觉和第①条很像?其实两者最大的区别就是两个函数图有没有增长率相等的情况,读作p(N)的增长率比T(N)大,比如说下面这张图:
这里写图片描述
现学现用的话那么就是T(N) = O(p(N))且T(N)!=Θ(p(N))
这些定义在描述算法的效率问题上会有非常大的作用。

3、上界与下界
简单来说,在T(N) = O(f(N))中,f(N)就是T(N)的一个上界;同理,在T(N)上面的等式也可以表示成f(N) = Ω(T(N)),所以T(N)就是f(N)的一个下界。

插入排序

比较专业来说,就是:插入排序对于N个元素的排序需要进行N-1趟处理,p表示当前处理元素位置(或者是处理趟数),p=1到N-1趟中的任何一步都保证从0到p位置的顺序是排好的。
通俗来说就是对于一个要排序的数据,将数据分为已排序和未排序两部分,按顺序将一个未排序的数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,未排序的数据减一,直到数据全部是排序的。下面这张图描述了过程:
这里写图片描述
第一趟:处理8, 34是属于已排好序的(一个值不用排序),8<34,那么继续往前遍历,发现前面没数据了,那么就把8插入到34前面。
第二趟:处理64,64>34(从后面往前面比较),因为64比已排序好的最大的还要大,所以直接插入到34的后面。
第三趟:处理51,51<64,那么继续遍历,接着51>34,因为51比前面排好序最大的要大,所以停止遍历,把51插入到34的后面
第四趟:处理32,32<64,那么继续遍历,32<51,那么还是继续遍历,32<34,那么还是继续遍历。32>8,遍历结束,把32插入到8后面。
第五趟:处理21,21<64,那么继续遍历,21<51,那么继续遍历,21<34,那么继续遍历,21<32,那么继续遍历。21>8,遍历结束,把21插入到8后面。
排序结束。

代码实现

以下就是我实现的插入排序,排序对象是int型的数组:

package com.brickworkers;
/**
 * 
 * @author Brickworker
 * Date:2017年4月19日下午3:25:27 
 * 关于类SortTest.java的描述:插入排序
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class SortTest{

    public static void sort(int[] t) {
        int j;
        for (int p = 1; p < t.length; p++) {// 遍历数组,从1下标开始,0位置一个元素不用排序
            // 用temp暂存要排序的值
            int temp = t[p];
            // 代处理的数据,在已排好序的数组中从后往前进行比较遍历
            // 条件是:代处理的数据是否比有序的数据中某一个值小,如果小那么就需要对数组进行移位
            for (j = p; j > 0 && temp < t[j - 1]; j--) {
                // 数组移位,为数据的插入做准备
                t[j] = t[j - 1];
            }
            // 把数据插入到合适的j位置
            t[j] = temp;
        }
    }

    public static void main(String[] args) {
        int[] ints = { 1, 5, 4, 2, 6, 4, 6, 47, 5, 4, 12, 41, 66, 4, 2, 10, 42 };
        sort(ints);
        for (int i : ints) {
            System.out.print(i + " ");
        }
    }
}

//输出:1 2 2 4 4 4 4 5 5 6 6 10 12 41 42 47 66 

我们对上面的算法进行分析,由于两个for循环的嵌套,所以插入排序的时间复杂度为O(N^2)。如果是处理已排好序的数据来说,那么插入排序就是O(N),因为第二个循环在判断的时候就结束了。考虑最差情况就是待处理数据是有序但是和预期相反,那么N个元素的执行次数就是:

1+2+….+(N-1) = (1+N-1)*N/2 =O(N^2)。

尾记

编程,算法是必须的;算法,数学是必须的。我有个朋友为了写好代码,学过数学(线代,离散),学过经济,学过画画,学过文学。所以,不要觉得自己学这个没用,看那个没用,其实只是你还暂时没有用到罢了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章