排序(二)堆排序、归并排序、快速排序

本文是是时间复杂度为O(nlogn)的排序算法

堆排序

思路:(以最大堆举例)
将数组array变成最大堆,array[0]则为数组中的最大数,调换array[0]和array[i](i为未排序的数组的末尾)。然后逐步减小i,即array[0]到array[i]为未排序的数组。先确定数组的末尾。

public class Solution{
    public void dui(int[] arr)
    {
        if(arr==null || arr.length==0) return;
        //构建最大堆
        for(int i=(arr.length-2)/2;i>=0;i--)
            f(arr,i,arr.length-1);
        //交换堆顶和未排序的最后的元素,并将未排序的重新构建最大堆
        for(int i=arr.length-1;i>0;i--)
        {
            swap(arr,0,i);
            f(arr,0,i-1);
        }
    }
    public void f(int[] arr,int i,int j)
    {
        //k默认指向左孩子
        if(j<=i) return;
        int temp = arr[i];
        int k = 0;
        //向孩子遍历,默认指向左孩子
        for(k=i*2+1;k<=j;k=k*2+1)
        {
            //让k指向大的孩子
            if((k+1<=j) && (arr[k]<arr[k+1]))
                k = k+1;
            //如果temp比子节点都大,就结束调整
            if(arr[k]<=temp)
                break;
            //否则让父节点的值等于大的子节点的值
            arr[(k-1)/2] = arr[k];
        }
        arr[(k-1)/2] = temp;
    }

    public void swap(int[] arr,int i ,int j)
    {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

堆详解:

堆的构建的时间复杂度为O(n),因为假设有n个节点,从底向上,高度为logn,最底层最多挪动1次,倒数第2层最多挪动2次,,,最后加起来为o(n)
插入、删除的时间复杂度为o(logn)。
整体时间复杂度为O(nlogn),因为函数f的时间复杂度是logn。

归并排序

1、递归实现

整体思路:
f(array,start,end)是对array[start,end)进行从小到大的排序
f(array,start,end):
对前一半数组排序
对后一半数组排序
将前后排好序的数组再整体排序

//归并排序
public class Solution{  
    public void guibing(int[] arr)
    {
        if(arr==null || arr.length<2) return;
        int[] copy = new int[arr.length];
        f(arr,0,arr.length-1,copy);
    }
    public void f(int[] arr,int s,int e,int[] copy)
    {
        if(e<=s) return;
        int mid = (s+e)/2;
        f(arr,s,mid,copy);
        f(arr,mid+1,e,copy);
        int p1 = s;
        int p2 = mid+1;
        for(int i=s;i<e+1;i++)
        {
            if((p1<=mid)&&(p2<=e))
            {
                if(arr[p1]<arr[p2])
                    copy[i]=arr[p1++];
                else
                    copy[i] =arr[p2++];
            }
            else if(p1==mid+1)
                copy[i] = arr[p2++];
            else 
                copy[i] = arr[p1++];
        }
        for(int i=s;i<e+1;i++)
            arr[i] = copy[i];       
    }
}

2、非递归实现

public class Solution{
    public void guibing(int[] arr)
    {
        if(arr==null || arr.length<1) return;
        int j = 0;
        //j为间隔
        //[i,i+j-1],[i+j,i+2*j-1]内部为已经排序好的,需要将两者合并。
        for(j=1;j<arr.length;j=j*2)
        {
            for(int i=0;i<arr.length;i=i+2*j)
            {
                if(i+j >= arr.length)
                    continue;
                else if(i+2*j>arr.length)
                {
                    merge(arr,i,arr.length-1,j);
                }
                else
                {
                    merge(arr,i,i+2*j-1,j);
                }                   
            }
        }
    }
    public void merge(int[] arr,int s,int e,int j)
    {
        int[] copy = new int[e-s+1];
        int p1 = s;
        int p2 = s+j;
        for(int i=0;i<copy.length;i++)
        {
            if(p1>s+j-1)
                copy[i] = arr[p2++];
            else if(p2>e)
                copy[i] = arr[p1++];
            else if(arr[p1]<arr[p2])
                copy[i] = arr[p1++];
            else
                copy[i] = arr[p2++];                
        }
        for(int i=0;i<copy.length;i++)
            arr[i+s] = copy[i];
    }
}

快速排序

基本思路:
选取数组中的一个数作为关键字,将数组初步排序成关键字左边的数都比关键字小,关键字右边的数都比关键字大,再对关键字的左右数组继续排序

快速排序基本算法

public class Solution{
    public void kuaisu(int[] arr)
    {
        if(arr==null || arr.length==0) return;
        sortcore(arr,0,arr.length-1);
    }
    public void sortcore(int[] arr,int low,int high)
    {
        int key = 0;
        if(high<=low) return;
        //找出关键字的索引位置key,key左边的都比arr[key]小,右边的都比arr[key]大
        key = partition(arr,low,high);
        //对左边的数组排序
        sortcore(arr,low,key-1);
        //对右边的数组排序
        sortcore(arr,key+1,high);       
    }
    public int partition(int[] arr,int low,int high)
    {
        //此处选取第一个数为关键字
        //一定要先记录下arr[key]=value,否则交换时会改变值
        int key = low;
        int value = arr[key];
        //交换令high右边的数都比value大,low左边的数都比value小
        //value在high和low索引间互相交换直到两者相等
        while(high>low)
        {
            while(high>low && arr[high]>=value)
                high--;
            swap(arr,low,high);
            while(high>low && arr[low]<=value)
                low++;
            swap(arr,low,high);
        }
        return low;
    }
    public void swap(int[] arr,int p1,int p2)
    {
        int temp = arr[p1];
        arr[p1] = arr[p2];
        arr[p2] = temp;
    }
}

非递归实现:

//用栈存储每次的low和high
public void kuaisu(int[] arr)
    {
        if(arr==null || arr.length==0) return;
        ArrayDeque<Integer> stack = new ArrayDeque();
        stack.push(arr.length-1);
        stack.push(0);
        int start = 0;
        int end = 0;
        int key = 0;
        while(!stack.isEmpty())
        {
            start = stack.pop();
            end = stack.pop();
            if(end>start){
                key = partition(arr,start,end);
                stack.push(key-1);
                stack.push(start);
                stack.push(end);
                stack.push(key+1);
            }
        }       
    }

快速排序的改进

见大话数据结构p424
1、对关键字的选择,如果第一个数离中位数偏差很远,运行效率就很低。对关键数的选择可以选择数组,start.mid,end中位于中间的数。更大的数可以选择九数选中
2、不用交换而用赋值来初步排序数组
3、在排序的过程中,对于length小的数组,直接用简单排序来排,选取简单排序中的直接插入排序的方法
4、将对右子数组进行递归变成迭代,缩减堆栈深度

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值