归并排序,快速排序,堆排序

149 阅读2分钟

不同排序算法之间的比对

38ceb232f6314d3a8e71658d769c43f7.png

归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子 序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。


ae0e1fea375c4969a5ea1930f65599f0.png

快速排序

快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

从待排序区间选择一个数,作为基准值(pivot);

Partition: 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的(可以包含相等的)放到基准值的右边;

采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 == 1,代表已经有序,或者小区间的长度 == 0,代表没有数据。

void QuickSort(int left,int right)
{
    int i,j,t,temp;
    if(left>right){
        return;
    }
    temp=a[left];
    i=left;
    j=right;
    while(i!=j)
    {
        while(a[j]>=temp&&i<j)
        {
            j--;
        }
        while(a[i]<=temp)
        {
            i++;
        }
        if(i<j)
        {
            t=a[i];
            a[i]=a[j];
            a[j]=t;
        }
    }
    a[left]=a[i];
    a[i]=temp;
    QuickSort(left,i-1);
    QuickSort(i+1,right);
    }

堆排序

大顶堆 小顶堆

每个结点的值都大于其左孩子和右孩子结点的值,称之为大根堆;每个结点的值都小于其左孩子和右孩子结点的值,称之为小根堆。如下图

20180801211245720.png

1.父结点索引:(i-1)/2(这里计算机中的除以2,省略掉小数)

2.左孩子索引:2**i*+1

3.右孩子索引:2**i*+2

大根堆: arr(i)>arr(2i+1) && arr(i)>arr(2i+2)

小根堆: arr(i)<arr(2i+1) && arr(i)<arr(2i+2)

`

public static void heapSort(int[] arr) {
    //构造大根堆
    heapInsert(arr);
    int size = arr.length;
    while (size > 1) {
        //固定最大值
        swap(arr, 0, size - 1);
        size--;
        //构造大根堆
        heapify(arr, 0, size);

    }

}

//构造大根堆(通过新插入的数上升)
public static void heapInsert(int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        //当前插入的索引
        int currentIndex = i;
        //父结点索引
        int fatherIndex = (currentIndex - 1) / 2;
        //如果当前插入的值大于其父结点的值,则交换值,并且将索引指向父结点
        //然后继续和上面的父结点值比较,直到不大于父结点,则退出循环
        while (arr[currentIndex] > arr[fatherIndex]) {
            //交换当前结点与父结点的值
            swap(arr, currentIndex, fatherIndex);
            //将当前索引指向父索引
            currentIndex = fatherIndex;
            //重新计算当前索引的父索引
            fatherIndex = (currentIndex - 1) / 2;
        }
    }
}
//将剩余的数构造成大根堆(通过顶端的数下降)
public static void heapify(int[] arr, int index, int size) {
    int left = 2 * index + 1;
    int right = 2 * index + 2;
    while (left < size) {
        int largestIndex;
        //判断孩子中较大的值的索引(要确保右孩子在size范围之内)
        if (arr[left] < arr[right] && right < size) {
            largestIndex = right;
        } else {
            largestIndex = left;
        }
        //比较父结点的值与孩子中较大的值,并确定最大值的索引
        if (arr[index] > arr[largestIndex]) {
            largestIndex = index;
        }
        //如果父结点索引是最大值的索引,那已经是大根堆了,则退出循环
        if (index == largestIndex) {
            break;
        }
        //父结点不是最大值,与孩子中较大的值交换
        swap(arr, largestIndex, index);
        //将索引指向孩子中较大的值的索引
        index = largestIndex;
        //重新计算交换之后的孩子的索引
        left = 2 * index + 1;
        right = 2 * index + 2;
    }

}
//交换数组中两个元素的值
public static void swap(int[] arr, int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}`