排序算法之归并排序及利用归并排序求逆序数

来源:互联网 发布:淘宝平台 编辑:程序博客网 时间:2024/06/03 14:15

排序算法之归并排序

1. 自顶向下的归并排序

中心思想

将待排序的数组平均切分为两半,将前半部分和后半部分分别进行排序,再讲两个有序数组归并到一个数组中

特点

递归,需要额外的空间(辅助数组)来保存切分完成的子数组,主要难点在于合并

操作步骤

  • 将待排序数组均分为两半
  • 对前半部分进行排序
  • 对后半部分进行排序
  • 合并两个子数组
  • 递归调用以上过程

代码实现

public void sort(int[] a){    int N = a.length;;    int b = new int[N];//创建辅助数组    sort(a, 0, N-1);}public void sort(int[] a, int low, int high){    if (low >= high) {        return;    }    int mid = (low + high) / 2;//均分数组    sort(a, low, mid);//排前半部分    sort(a, mid, high);//排后半部分    merge(a, low, high, mid);//归并}private void merge(int[] a, int low, int high, int mid){    for(int i = low; i <= high; i++) {        b[i] = a[i];    }//将a[low...high]复制到b[low...high]    int j = low;//扫描左半部分    int k = mid + 1;//扫描右半部分    for(int i = low; i <= high; i++){        // 从b[low...high]中依次取出较小的元素放入a[low...high]        if (j > mid) {            a[i] = b[k++];//若左半部分元素用尽,取右半部分元素        } else if (k > high) {            a[i] = b[j++]//若右半部分元素用尽,取左半部分元素        } else if (b[j] < b[k]) {            a[i] = b[j++];//两半部分元素都未用尽时,取较小的        } else {            a[i] = b[k++];        }    }}

2. 自底向上的归并排序

特点

不切分数组,直接两两归并(将每个元素当做大小为1的子数组)、四四归并(将两个大小为2的子数组归并为一个大小为4的数组)…最后对整个数组归并

优点

相对于自顶向下的归并,代码量少

代码实现

public void sort(int[] a){    int N = a.length;    int b[] = new int[N];    sort(a, 0, N-1);}private void sort(int[] a, int low, int high){    //sz为子数组的大小,依次为1,2,4,8...N    for(int sz = 1; sz < N; sz = sz + sz){    //i为子数组的索引,而子数组大小为N,因而当i移动到靠近边界时边界元素的索引要满足i+sz<N        for(int i = low; i < N - sz; i = i + sz + sz){            //当数组大小不是2的偶数倍时,最后一个子数组会比前一个要小,导致该子数组的索引不满足i+2sz-1,而是N-1            merge(a, i, min(i+2sz-1, N-1), i+sz-1);            //当N为2的偶数倍时,mid=(i+(i+2sz-1)-1)/2;非偶数倍时,mid=(i+N-1-1)/2,而i<N-sz,可推出此种情况下,(i+(i+2sz-1)-1)<(i+N-1-1),即N为偶数倍,mid可取mid=(i+(i+2sz-1)-1)/2        }    }}

归并排序的应用-求逆序数

逆序数指的是逆序数对的个数,在一个序列a[0, 1, 2, …N]中,对任意的i和j,如果i < j且a[i] > a[j],则a[i]和a[j]为逆序数对,一个队列中所有的逆序数对数量之和即为该序列的逆序数。

蛮力法求逆序数

for(int i = 1; i < N; i++){    for(int j = i - 1; j >= 0; j-- ){        if(a[i] < a[j]){            count++;        }    }}

显然两个循环的存在导致此法复杂度为n的平方。

利用归并排序

merge过程中,由于两个子数组(假设为A和B)已各自有序,用i扫描A,用j扫描B,某次循环中,若a[i] > a[j],则该次循环中得到的逆序数count = A.length - i。对count进行累加后即可得到总逆序数。
由于归并排序时间复杂度为NlogN(数学问题,本文不做证明),因而优于蛮力法。

实现方式

只需在merge方法中计算逆序对数量即可

private void merge(int[] a, int low, int high, int mid){    for(int i = low; i <= high; i++) {        b[i] = a[i];    }//将a[low...high]复制到b[low...high]    int j = low;//扫描左半部分    int k = mid + 1;//扫描右半部分    for(int i = low; i <= high; i++){        // 从b[low...high]中依次取出较小的元素放入a[low...high]        if (j > mid) {            a[i] = b[k++];//若左半部分元素用尽,取右半部分元素        } else if (k > high) {            a[i] = b[j++]//若右半部分元素用尽,取左半部分元素        } else if (b[j] < b[k]) {            a[i] = b[j++];//两半部分元素都未用尽时,取较小的        } else {            a[i] = b[k++];            count = count + (mid - low + 1) - j;//累加该次循环的逆序对数量        }    }}
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 孩子被老师打了怎么办 孩子说老师打她怎么办 孩子的数学太差怎么办 2岁宝宝老要喝水怎么办 分手了还想她怎么办 5岁宝宝不会说话怎么办 2岁半宝宝说话晚怎么办 7岁儿童发烧39度怎么办 感冒发烧怎么办简单的退烧方法 生完孩子没奶水怎么办 梦见让狐狸咬了怎么办 1岁宝宝不吃辅食怎么办 母乳不够宝宝不吃奶粉怎么办 吃母乳的宝宝不吃奶粉怎么办 宝宝吃母乳不吃奶粉怎么办 1岁婴儿不吃辅食怎么办 不喝奶瓶的宝宝怎么办 母乳不足宝宝不吃奶粉怎么办 4岁宝宝注意力不集中怎么办 孩子上课不专心听讲怎么办 小孩上课不认真听讲怎么办 一年级孩子上课不认真听讲怎么办 打了孩子很自责怎么办 4岁宝宝讲话结巴怎么办 网销客户不说话怎么办 两岁宝宝不说话怎么办? 4岁儿童说话结巴怎么办 3岁宝宝说话结巴怎么办 6岁儿童舌头短怎么办 5岁宝宝说话结巴怎么办 两岁宝宝说话晚怎么办 6岁儿童说话结巴怎么办 2岁宝宝呕吐拉稀怎么办 2岁宝宝突然呕吐怎么办 2岁宝宝呕吐发烧怎么办 2岁宝宝呕吐厉害怎么办 1岁宝宝半夜呕吐怎么办 2岁半宝宝呕吐怎么办 2岁宝宝半夜呕吐怎么办 两岁宝宝一直吐怎么办 原画师老了以后怎么办