自己实现的各种排序算法

排序大的分类可以分为两种:内排序和外排序。在排序过程中,全部记录存放在内存,则称为内排序,如果排序过程中需要使用外存,则称为外排序。下面讲的排序都是属于内排序。

  内排序有可以分为以下几类:

  (1)、插入排序:直接插入排序、二分法插入排序(没实现)、希尔排序。

  (2)、选择排序:选择排序、堆排序。

  (3)、交换排序:冒泡排序、快速排序。

  (4)、归并排序
  (5)、计数排序

  (6)、基数排序
 **算法稳定性定义:
 算法稳定性 – 假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的!**

冒泡排序:

/**
     * 冒泡算法:每次把最大的一个数找出来放在最后面。
     * 边界问题:举例:6个数字需要遍历5次,所以第一个循环是i<length;
     * 而第一次遍历比较次数是5次,加上 下标从0开始,所以第2个循环是j<length - i
     * 时间复杂度:O(n^2)
     * 优化算法:如果某次遍历中没有发生交换,那就说明已经排序好了,就不必进行下一次的遍历;设置boolean变量来监控判断;
     * 根据稳定性定义可知:稳定性的;
     * @param sort
     */
    private static void buddleSort(int[] sort){
        boolean needNextPass = true;
        for(int i=1;i<sort.length&& needNextPass;i++){
            needNextPass = false;
            for(int j=0;j<sort.length-i;j++)
            {
                if(sort[j]>sort[j+1]){
                    int temp=sort[j+1];
                    sort[j+1]=sort[j];
                    sort[j]=temp;
                    needNextPass = true;
                }
            }
        }
    }
运行结果:
排序前:
2 9 5 4 8 1 
排序后:
1 2 4 5 8 9 

直接插入排序

    /**
     * 直接插入排序:将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据
     * 边界问题:6个数,插入5次,所以第一次循环是:1<=i<length;
     * 算法解释:从后往前找需要替换的那个数
     * 根据稳定性定义可知:稳定性的;
     *  时间复杂度:O(n^2) 
     * @param sort
     */

    private static void directInsertSort(int[] sort){
        for(int i=1; i<sort.length;i++){
            int index=i-1;
            int temp=sort[i];
            while(index >=0 && sort[index]>temp){
                sort[index+1]= sort[index];//依次后移每个,等待temp找到合适的直接插入
                  index--;
            }
            sort[index + 1]=temp;
        }
    }
运行结果:
排序前:
2 9 5 4 8 1 
排序后:
1 2 4 5 8 9 

选择排序

/**
     * 选择排序:每次遍历数组找出最小的,然后与开头的交换
     * 边界问题:6个数字,需要遍历5次,所以  0<=i<=length-1;
     * 第二个就是在已经排序好的基础上往后面找:所以是 :  i+1<=j<length;
     * 时间复杂度:O(n^2)
     * 根据稳定性定义可知:不稳定性的;
     * @param sort
     */

    private static void selectSort(int [] sort){
        for(int i=0;i<sort.length-1;i++)
            for(int j=i+1;j<sort.length;j++){
                if(sort[j]<sort[i]){
                    int temp=sort[i];
                    sort[i]=sort[j];
                    sort[j]=temp;
                }
            }
    }
运行结果:
排序前:
2 9 5 4 8 1 
排序后:
1 2 4 5 8 9 

归并排序

因为归并排序的稳定性,所以Arrays.sort()用的就是归并排序。

/**
     * 归并排序:将数组分为两半,对每部分递归应用归并排序;两部分排序好后,对他们进行归并。
     * 时间复杂度:O(nlogn)
     * 空间复杂度:O(n)
     * 稳定。
     * @param sort
     * @param low
     * @param high
     */
    public static void mergeSort(int[] sort,int low,int high){
        if(low < high){
            int mid = (low + high)/2;
            mergeSort(sort,low,mid);
            mergeSort(sort,mid+1,high);
            merge(sort,low,mid,high);
        }
    }
    public static void merge(int []sort,int low ,int mid, int high){
        int begin1 = low;
        int begin2 = mid+1;
        int [] temp = new int [high - low +1];
        int index =0;
        while(begin1 <=mid && begin2<= high){
            if(sort[begin1]<=sort[begin2])
                temp[index++] = sort[begin1++];
            else
                temp[index++] = sort[begin2++];
        }
        //遍历完其中的1半,那么直接复制另一半剩下的到数组中
        while(begin1 <= mid)
            temp[index++] = sort[begin1++];
        while(begin2 <= high)
            temp[index++] = sort[begin2++];
        //将申请的额外排好序的空间数组赋值给sort数组,完成排序
        for(int begin = low;begin <= high; begin++)
            sort[begin] = temp[begin-low];
    }
    运行结果:
排序前:
2 9 5 4 8 1 
排序后:
1 2 4 5 8 9 

快速排序

/**快速排序:快速排序 通过一趟排序将要排序的数据分割成独立的两部分,
     * 一部分数据《= 中间数据 《= 另一部分数据 
     *  然后再按此方法对这两部分数据分别进行快速排序,
     *  整个排序过程可以递归进行,以此达到整个数据变成有序序列。
     *  时间复杂度:O(nlogn)
     *  问题:不稳定。
     * @param sort
     * @param low
     * @param high
     */
    private static void quickSort(int[] sort,int low,int high){
        if(low < high)//递归结束的判断
        {
            int loc = Partition(sort,low,high);
            quickSort(sort,low,loc-1);
            quickSort(sort,loc+1,high);
        }

    }
    public static int Partition(int[] sort ,int low ,int high){
        int elem = sort[low] ; //取一个基准元素
        while(low < high){

            while(low < high && sort[high]>= elem)
                high--;

            sort[low] = sort[high];

            while(low<high && sort[low]<= elem)
                low ++;
            sort[high] = sort[low];
        }
        sort[low] =elem;
        return low ;
    }
运行结果:
排序前:
2 9 5 4 8 1 
排序后:
1 2 4 5 8 9 

希尔排序

    /**
     * 希尔排序:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。
     * 先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),
     * 即所有记录放在同一组中进行直接插入排序为止。该方法实质上是一种分组插入方法。
     * 时间复杂度:O(nlogn)
     * 问题:不稳定
     * 希尔排序时间复杂度
希尔排序的时间复杂度与增量(即,步长gap)的选取有关。例如,当增量为1时,希尔排序退化成了直接插入排序,此时的时间复杂度为O(N²),而Hibbard增量的希尔排序的时间复杂度为O(N3/2)。
     * @param sort
     */
    public static void ShellSort(int[] sort){
        int hop = sort.length/2;
        while(hop > 0)
        {
            for(int k =0;k< hop ;++k)
            {
                for(int i =k+ hop;i<sort.length;i+= hop)
                {
                    int j = i-hop;
                    int tmp = sort[i];
                    while(j>=0 && sort[j] > tmp)
                    {
                        sort[j+hop] = sort[j];
                        j-= hop;
                    }
                    sort[j+hop] = tmp;
                }
            }
            hop/=2;
        }
    }
运行结果:
排序前:
2 9 5 4 8 1 
排序后:
1 2 4 5 8 9 

计数排序(类似桶排序)

/**(准确的说,这仅仅是简单的计数排序,仅是统计桶的个数,而桶内数据并没有排序。。。)
     * 桶排序:假设待排序的数组a中共有N个整数,并且已知数组a中数据的范围[0, MAX)。
     * 在桶排序时,创建容量为MAX的桶数组r,并将桶数组元素都初始化为0;将容量为MAX的桶数组中的每一个单元都看作一个"桶"。
     * 在排序时,逐个遍历数组a,将数组a的值,作为"桶数组r"的下标。当a中数据被读取时,就将桶的值加1。
     * 例如,读取到数组a[3]=5,则将r[5]的值+1。
     * 要求:要求苛刻:1.数字最大值有限,不能太大;2.全为正数
     * 时间复杂度:O(max(n,sort.max))确实快,但是牺牲了空间
     * 空间复杂度:O(sort.max)
     * @param sort
     */
    public static void bucketSort(int sort[]){
        //寻找出最大值
        int max =0;
        for(int i=0;i<sort.length;i++){
            if(max<sort[i])
                max= sort[i];
        }
        //初始化为0,
        int []buckets = new int [max+1];
        //1.计数
        for(int i=0;i<sort.length;i++)
            buckets[sort[i]]++;
        //2.排序
        for(int i=0,j=0;i<=max;i++){
            while(buckets[i]-->0)
                sort[j++] = i;
        }
    }
运行结果:
排序前:
22 19 65 34 998 1 
排序后:
1 19 22 34 65 998 

基数排序

/**
     * 基数排序:多关键字排序(例如扑克牌的花色+数值;数字看成每位的多个关键字),分为:最高位优先和最低位优先的2种方法;
     * 基于最低位优先策略做的:方法核心:分配 + 收集;
     * 每次循环都做这2步
     * 根据稳定性定义可知:稳定性的;
     * @param sort
     */
    public static void radixSort(int []sort){
        //找到最大的数字,确定要排序几次
        int max = 0;
        for(int i=0; i<sort.length;i++){
            if(max < sort[i])
                max = sort[i];
        }
        //判断位数
        int times = 0;
        while(max>0){
            max/=10;
            times++;
        }
        //建立10个队列:注意下,因为java没有泛型数组:
        //List<ArrayList>[] queue = new ArrayList<>[10];会报错
        //改为:List[] queue = new ArrayList[10];会提示出警告:因为类型不安全,
        //所以要这样子做:
        List<ArrayList<Integer>> queue = new ArrayList<>();
        for(int i =0; i< 10;i++)
        {
            ArrayList<Integer> queue1 = new ArrayList<>();
            queue.add(queue1);
        }

        //进行times次分配和收集
        for(int i =0;i<times;i++){
            //分配
            for(int j = 0;j<sort.length;j++){
                int x = sort[j]%(int)Math.pow(10, i+1)/(int)Math.pow(10, i);//求每个个位/十位/百位
                ArrayList<Integer> queue2 = queue.get(x);
                queue2.add(sort[j]);
                queue.set(x, queue2);
            }
            //收集
            int count = 0;
            for(int j=0;j<10;j++){
                while(queue.get(j).size()>0){
                    ArrayList<Integer> queue3 = queue.get(j);
                    sort[count] = queue3.get(0);
                    queue3.remove(0);
                    count++;
                }
            }
        }

    }
运行结果:
排序前:
22 19 65 34 998 1 
排序后:
1 19 22 34 65 998 

最大堆排序

/**
     * 最大堆排序思想:① 初始化堆:将数列a[1...n]构造成最大堆。
        ② 交换数据:将a[1]和a[n]交换,使a[n]是a[1...n]中的最大值;然后将a[1...n-1]重新调整为最大堆。 
        接着,将a[1]和a[n-1]交换,使a[n-1]是a[1...n-1]中的最大值;然后将a[1...n-2]重新调整为最大值。 依次类推,直到整个数列都是有序的。
     * 分别对应2个方法。
     * 数组实现;
     * @param a
     * @param n
     */

    public static void heapSortAsc(int []a,int n){
        int i,tmp;
         // 从(n/2-1) --> 0逐次遍历。遍历之后,得到的数组实际上是一个(最大)二叉堆。
        for(i = n/2-1;i>=0;i--)
            maxHeapDown(a,i,n-1);
         // 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
        for (i = n - 1; i > 0; i--) {
             // 交换a[0]和a[i]。交换后,a[i]是a[0...i]中最大的。
            tmp = a[0];
            a[0] = a[i];
            a[i] = tmp;
            // 调整a[0...i-1],使得a[0...i-1]仍然是一个最大堆。
           // 即,保证a[i-1]是a[0...i-1]中的最大值。
            maxHeapDown(a,0,i-1);
        }
    }
    public static void maxHeapDown(int[] a,int start,int end){
        int c = start;  // 当前(current)节点的位置
        int l = 2*c+1;   // 左(left)孩子的位置
        int tmp = a[c]; // 当前(current)节点的大小
        for(;l<=end;c=l,l=2*l+1){
              // "l"是左孩子,"l+1"是右孩子
            if(l< end && a[l]<a[l+1])
                l++;// 左右两孩子中选择较大者,即m_heap[l+1]
            if(tmp>=a[l])
                break;// 调整结束
            else        //交换值
            {
                a[c] = a[l];
                a[l] = tmp;
            }
        }
    }
运行结果:
排序前:
22 19 65 34 998 1 
排序后:
1 19 22 34 65 998 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值