排序

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)



  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值