排序算法

390 阅读6分钟

1、冒泡排序

一种简单的排序算法。它反复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。这个工作重复地进行直到没有元素再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为元素会经由交换慢慢“浮”到数列的顶端。

1、从数组头开始,比较相邻的元素。如果第一个比第二个大(小),就交换它们两个;
2、对每一对相邻元素作同样的工作,从开始第一对到尾部的最后一对,这样在最后的元素应该会是最大(小)的数;
3、重复步骤1~2,重复次数等于数组的长度,直到排序完成。
//java代码
public int[] sort(int[] array){
    int len = array.length;
    //判断数组长度,如果长度小于或者等于1,及返回原来的数组
    if(len <= 1){
        return array;
    }
    //长度大于1
    for(int i = 0;i < len;i++){
        //每循环一次,那么就有一个数组已经到相应的位置
        //所以下一次就少排序一个数字 j < len - 1 -i
        for(int j = 0;j < len - 1 -i;j++){
            if(array[j + 1] < array[j]){
                int temp = array[j + 1];
                array[j + 1] = array[j];
                array[j] = temp;
            }
        }
    }
    return array;
}

2、简单选择排序

首先,找到数组中最大(小)的那个元素; 其次,将它和数组的第一个元素交换位置(如果第一个元素就是最大(小)元素那么它就和自己交换); 再次,在剩下的元素中找到最大(小)的元素,将它与数组的第二个元素交换位置。如此往复,直到将整个数组排序。 这种方法叫做选择排序,因为它在不断地选择剩余元素之中的最大(小)者。

//java代码 
public int[] sort(int[] array){
     int len = array.length;
    //判断数组长度,如果长度小于或者等于1,及返回原来的数组
    if(len <= 1){
        return array;
    }
    
    for(int i = 0;i < len; i++){
        int minIndex = i;//记录最小的数字的下标
        //不用和自己比较,则每循环一次,少比较一次,则j = i + 1
        for(int j = i+ 1;j < len; j++){
            if(array[j] < array[i]){
                minIndex = j;
            }
        }
        //交换
        int temp = array[i];
        array[i] = array[minIndex];
        array[minIndex] = temp;
    }
    
    //第二种  替换上面for循环
    /**
    *   for(int i = 0;i < len; i++){
    *       for(int j = i +1;j < len; j++){
    *           //每次找到一个比array[i]的都进行交换
    *           if(array[j] < array[i]){
    *                int tem = arr[j];
    *                arr[j] = arr[i];
    *                arr[i] = tem;
    *           }
    *       }
    *   }
    */
    return array;
}

3、简单插入排序

对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。 为了给要插入的元素腾出空间,我们需要将插入位置之后的已排序元素在都向后移动一位。 插入排序所需的时间取决于输入中元素的初始顺序。例如,对一个很大且其中的元素已经有序(或接近有序)的数组进行排序将会比对随机顺序的数组或是逆序数组进行排序要快得多。 总的来说,插入排序对于部分有序的数组十分高效,也很适合小规模数组。

//java代码

public int[] sort(int[] array){
    int len = array.length;
     //判断数组长度,如果长度小于或者等于1,及返回原来的数组
    if(len <= 1){
        return array;
    }
    int currentValue;//记录当前要排序的数据
    for(int i = 0;i < len - 1;i++){
        int preIndex = i;//已经排好序的数据的下标
        currentValue = array[preIndex + 1];//待排序的数据
        //在已被排序过数据中倒序寻找合适的位置,如果当前待排序数据比比较的元素要小,
        //将比较的元素元素后移一位
        while(preIndex > 0 && array[preIndex] > currentValue){
            array[preIndex + 1] = array[preIndex];
            preIndex-- ;
        }
        //while循环结束时,说明已经找到了当前待排序数据的合适位置,插入
        array[preIndex + 1] = currentValue;
    }
    
    return array;
}

4、希尔排序(缩小增量排序)

一种基于插入排序的快速的排序算法。简单插入排序对于大规模乱序数组很慢,因为元素只能一点一点地从数组的一端移动到另一端。例如,如果主键最小的元素正好在数组的尽头,要将它挪到正确的位置就需要N-1 次移动。
希尔排序为了加快速度简单地改进了插入排序,也称为缩小增量排序,同时该算法是突破O(n^2)的第一批算法之一。
希尔排序是把待排序数组按一定数量的分组,对每组使用直接插入排序算法排序;然后缩小数量继续分组排序,随着数量逐渐减少,每组包含的元素越来越多,当数量减至 1 时,整个数组恰被分成一组,排序便完成了。这个不断缩小的数量,就构成了一个增量序列。
//java 
public int[] sort(int[] array){
    int len = array.length;
    if(len == 0)
        return array;
    int currentValue; //记录当前值
    int spike = len / 2;//第一次增量
    while(spike > 0){
        for(int i = spike;i < len;i++){
            currentValue = array[spike]; //要排序的数据
            //组内已经排好序的数据
            int preIndex = i - spike;
            //在组内已被排序过数据中倒序寻找合适的位置,如果当前待排序数据比比较的元素要小,
            // 并将比较的元素元素在组内后移一位
            while(preIndex > 0 && currentValue < array[preIndex]){
                array[preIndex + spike] = array[preIndex];
                preIndex = preIndex - spike; 
            }
            array[preIndex + spike] = currentValue;
        }
        spike = spike / 2;
    }
    return array;
}

5、归并排序

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法的一个非常典型的应用。
对于给定的一组数据,利用递归与分治技术将数据序列划分成为越来越小的半子表,在对半子表排序后,再用递归方法将排好序的半子表合并成为越来越大的有序序列。
为了提升性能,有时我们在半子表的个数小于某个数(比如15)的情况下,对半子表的排序采用其他排序算法,比如插入排序。
若将两个有序表合并成一个有序表,称为2-路归并,与之对应的还有多路归并。
//java
public int[] sort(int[] array){
    int len = array.length;
    if(len < 2) return array;
    //分成左和右 子数组
    int mid = len / 2;
    int[] left = Arrays.copyOfRange(array,0,mid);
    int[] right = Arrays.copyOfRange(array,mid,len);
    //继续分 直到分成长度小于2的子数组 然后合并子数组
    return merge(sort(left),sort(right));
}

private int[] merge(int[] left,int[] right){
    //创建一个新的数组 长度为量个数组之和
    int len = left.length + right.length;
    int[] result = new int[]; //创建一个合并数组
    for(int i = 0,leftIndex = 0,rightIndex = 0;i < len;i++){
        if(leftIndex > left.length){//左子数组已经添加完,只剩下右子数组
            result[i] = right[rightIndex++];
        }else if(rightIndex > right.length){//右子数组已经添加完,只剩下左子数组
            result[i] = left[leftIndex++];
        }else if(left[leftIndex++] > right[rightIndex++]){
            //左子数组 大
            result[i] = left[leftIndex++];
        }else{
            //右子数组 大
            result[i] = right[rightIndex++];
        }
    }
    
    retrun result;
}