排序

来源:互联网 发布:泰迪罗宾地位知乎 编辑:程序博客网 时间:2024/06/07 05:46
package sort;/** * 插入排序 * 1)直接插入排序 * 2)折半插入排序 * 3)希尔排序 *  * 选择排序 * 1)简单选择排序 * 2)堆排序 *  * 交换排序 * 1)起泡排序 * 2)快速排序 *  * 归并排序 */public class sorts {    /**     * 直接插入排序    稳定排序        * 空间复杂度:o(1)     * 时间复杂度:     * 最坏情况: o(n^2)     * 最好情况: o(n)       * 平均情况: o(n^2)     * @param data  待排数据数组     */    public static void InsertSort(int[] data){                for(int i = 1; i < data.length; i++){//数组下标从0开始,第一个元素有序,所以从第二个开始处理            int tmp = data[i];//用来临时存放待排元素            int j = i - 1;//标记开始比较的位置            //完成了从待排元素之前的元素开始扫描,如果大于待排元素,则后移一位            while(j >= 0 && tmp < data[j]){                data[j+1] = data[j];                --j;            }            data[j+1] = tmp;//找到插入位置,将tmp中暂存的待排元素插入        }    }                /**     * 折半插入排序   稳定排序    折半查找法的基本要求是序列已经基本有序     * 空间复杂度:o(1)     * 时间复杂度:     * 最坏情况: o(n^2)     * 最好情况: o(n)       * 平均情况: o(n^2)     * @param data  待排数据数组     */    public static void binaryInsertSort(int[] data){        for(int i = 1; i < data.length; i++){//数组下标从0开始,第一个元素有序,所以从第二个开始处理            int tmp = data[i];            int low = 0;            int high = i - 1;            while(low <= high){//当low > high  折半查找结束                int mid = (low + high) / 2;                if(tmp < data[mid]){                    high = mid - 1; // 插入点在低半区                }else{ //关键字相同时,使low = mid + 1,到高半区,保证稳定性                    low = mid + 1;// 插入点在高半区                }            }            //依次向后移动记录            for(int t = i-1; t > high; t--){// 记录后移                data[t+1] = data[t];            }            data[high+1] = tmp; // 插入        }    }        /**     * 冒泡(起泡)排序   稳定排序       * 空间复杂度:o(1) tmp     * 时间复杂度:     * 最坏情况: o(n^2)     * 最好情况: o(n)       * 平均情况: o(n^2)     * @param data    待排数据数组     */    public static void BubbleSort(int[] data){        int n = data.length;        for(int k = 0; k < n-1; k++){//进行(n-1)趟冒泡            boolean flag = false;//变量flag用来标记本趟排序是否发生交换            for(int i = 1; i < n-k; i++){                if(data[i-1] > data[i]){                    int tmp = data[i];                    data[i] = data[i-1];                    data[i-1] = tmp;                    flag = true;//如果没发生交换,则flag为false,否则为true                }            }            if(!flag){                return;            }        }    }        /**     * 快速排序    对从data[l]到data[r]的元素进行排序      通常都选取第一个元素作为枢纽     * 空间复杂度:o(logn)  快速排序是递归进行的, 递归需要栈的辅助     * 时间复杂度:     * 最坏情况: o(n^2)     * 最好情况: o(nlogn)       * 平均情况: o(nlogn)  就平均时间而言,快速排序是所有排序算法中最好的  快速排序的排序趟数和初始序列有关     * @param data    待排数据数组      * @param l    待排数据数组起始位置     * @param r 待排数据数组终止位置     */    public static void QuickSort(int[] data, int l, int r){                int i = l;        int j = r;        if(l < r){//l >= r  相当于递归终止条件            int tmp = data[l];            //下面这个循环完成了一趟排序,即将数组中小于等于tmp的元素放在左边,大于等于tmp的元素放在右边            while(i != j){                while(j>i&&data[j]>=tmp) --j;//从右到左扫描找到一个小于tmp的元素                if(i<j){                    data[i] = data[j];                    i++;                }                while(j>i&&data[i]<=tmp) i++;                if(i<j){                    data[j] = data[i];                    j--;                }            }            data[i] = tmp;            QuickSort(data, l, i-1);//递归地对tmp左边元素进行排序            QuickSort(data, i+1, r);//递归地对tmp右边元素进行排序        }    }         /**     * 简单选择排序    从头到尾顺序扫描序列,找出最小的一个记录,和第一个记录交换,接着从剩下的记录中继续这种选择和交换,最终使序列有序        * 空间复杂度:o(1)  tmp     * 时间复杂度:o(n^2)     * @param data    待排数据数组     */    public static void SelectSort(int[] data, int n){        for(int i = 0; i < (n-1); i++){            int k = i;//k用来记录最小值的位置             for(int j = i + 1; j < n; j++){                if(data[j] < data[k]){                    k = j;                }            }            if(k != i){                int tmp = data[k];                data[k] = data[i];                data[i] = tmp;            }                    }    }}


快排划分函数非常重要:可以用来求第k大的数,前k小个数(剑指offer面试题40),数组中出现次数超过数组长度一半的数字(剑指offer面试题39)

public static void Partition(int[] data, int start, int end){int i = start;int j = end;int tmp = data[i];if (data == null || data.length <= 0 || start <0 || end >= data.length ) {return ;}while(i != j) {while (i < j && data[j] >= tmp) --j;//从右到左依次遍历找到小于tmp的元素if (i < j){data[i] = data[j];i++;} while (i < j && data[i] <= tmp) i++;//从左到右依次遍历找到大于tmp的元素if (i < j){data[j] = data[i];j--;}}data[i] = tmp;}


折半插入排序:
  与直接插入排序相比,折半插入排序寻找插入位置上面所花的时间大大减少。折半插入排序在记录移动次数方面和直接插入排序是一样的,所以时间复杂度和直接插入排序还是一样的。

希尔排序:
  又叫做缩小增量排序,其本质是插入排序,只不过是将待排序的序列按某种规则分成几个子序列,分别对这几个子序列进行直接插入排序。
希尔排序是不稳定的排序
空间复杂度:o(1) 
平均情况下时间复杂度: o(nlogn) 
注意:希尔排序的增量取法要注意,首先增量序列的最后一个值一定是1; 其次增量序列中的值没有除1之外的公因子,如8、4、2、1这样的序列就不要取(8、4、2有公共因子2)

 

堆排序:

  堆排序是不稳定的。可以把堆看成一棵完全二叉树,这棵完全二叉树满足:任何一个非叶子结点的值都不大于(或不小于)其左右孩子结点的值。若父亲大孩子节点,则这样的堆叫做大顶堆,反之叫小顶堆。

从无序序列所确定的完全二叉树的第一个非叶子节点,开始从右到左,从下到上,对每个非叶子节点进行调整,最终得到一个大顶堆。

对结点调整方法:
1)  将当前结点(假设为a)的值与其孩子结点进行比较,如果存在大于a值的孩子结点,则从中选出最大的一个与a交换。当a来到下一层的时候重复以上过程,直到a的孩子结点值都小于a的值为止。

2)  将当前无序序列中第一个元素,反映在树中是根结点(假设为a)与无序序列中最后一个元素交换(假设为b)。a进入有序序列,到达最终位置,无序序列中的元素减少1个,有序序列中元素增加1个。此时只有结点b可能不满足堆的定义,对其调整

3)  重复2)过程,直到无序序列中的元素剩下1个时排序结束。

适用范围:

记录数很多的情况,典型的例子是从10000个记录中选出前10个最小的。

 

 这幅图归并排序的空间复杂度为O(n)



原创粉丝点击