剑指Offer——归并排序思想应用

前言

在学习排序算法时,初识归并排序,从其代码量上感觉这个排序怎么这么难啊。其实归并排序的思想很简单:将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。归并排序是建立在归并操作上的一种有效排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。下面结合一编程实例进行系统学习。

时间复杂度:O(nlogn)

空间复杂度:O(n)

稳定性:稳定

数组中的逆序对

题目描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007。

输入描述:

题目保证输入的数组中没有的相同的数字

数据范围:

对于%50的数据,size<=10^4

对于%75的数据,size<=10^5

对于%100的数据,size<=2*10^5

输入例子:

1,2,3,4,5,6,7,0

输出例子:

7

这里的解题思路是:先把数组分隔成子数组,统计出字数组内部逆序对的数目;然后再统计出相邻两个子数组之间的逆序对数目。在统计逆序对过程中,还需要对数组进行排序。不难发现,这个排序过程实际上就是归并排序。

归并排序中用到了递归的写法,其实自己对递归并不感冒。递归方式虽使代码看起来更加整洁简练,但是由于其使用到了栈,而栈的内存大小是一定的,故当递归深度过于大时,就会出现栈溢出StackOverflow的异常情况。递归的方式可由非递归即循环方式替代。

package cn.edu.ujn.offersword;
public class C5_36_InversePairs {
/**
 * @date 2016-09-16
 * @number 03
 * @author SHQ
 */
public static void main(String[] args) {
int [] array = {1,2,3,4,5,6,7,0};
System.out.println(InversePairs(array));
}
    private static int InversePairs(int [] array) {
        if(array == null || array.length == 0)
        {
            return 0;
        }
        int len = array.length;
        int[] copy = new int[len];	// 设置一辅助数组,用于存放排序结果
        for(int i = 0; i < len; i++)
        {
            copy[i] = array[i];
        }
        int count = InversePairsCore(array, copy, 0, len-1);
        return count;
    }

    private static int InversePairsCore(int[] array, int[] copy,int low, int high)
    {
        if(low == high)
        {
            return 0;
        }
        int mid = (low + high) >> 1;//使用左移运算符 >>,运算速度高于除法运算符
        int leftCount = InversePairsCore(array, copy, low, mid) % 1000000007;
        int rightCount = InversePairsCore(array, copy, mid+1, high)% 1000000007;
        int count = 0;
        int i = mid;
        int j = high;
        int locCopy = high;
        while(i >= low && j > mid)
        {
            if(array[i] > array[j])
            {
                count += j - mid;
                copy[locCopy--] = array[i--];
                if(count >= 1000000007) // 数值过大求余
                {
                    count %= 1000000007;
                }
            }
            else
            {
                copy[locCopy--] = array[j--];
            }
        }
        for(; i >= low; i--)
        {
            copy[locCopy--] = array[i];
        }
        for(; j > mid; j--)
        {
            copy[locCopy--] = array[j];
        }
        for(int s = low; s <= high; s++)
        {
            array[s] = copy[s];
        }
        return (leftCount + rightCount + count) % 1000000007;
    }
}

注 归并排序原始算法

package cn.edu.ujn.offersword;

import java.util.Arrays;

public class MergeSort {
    /**
     * 归并排序
     * 简介:将两个(或两个以上)有序表合并成一个新的有序表 即把待排序序列分为若干个子序列,每个子序列是有序的。
     * 然后再把有序子序列合并为整体有序序列
     * 时间复杂度为O(nlogn)
     * 空间复杂度为O(n)
     * 稳定排序方式
     * @param nums 待排序数组
     * @return 输出有序数组
     */
    public static int[] sort(int[] nums, int low, int high) {
        int mid = (low + high) / 2;
        if (low < high) {
            // 左边
            sort(nums, low, mid);
            // 右边
            sort(nums, mid + 1, high);
            // 左右归并
            merge(nums, low, mid, high);
        }
        return nums;
    }  

    public static void merge(int[] nums, int low, int mid, int high) {
        int[] temp = new int[high - low + 1];
        int i = low;// 左指针
        int j = mid + 1;// 右指针
        int k = 0;

        // 把较小的数先移到新数组中
        while (i <= mid && j <= high) {
            if (nums[i] < nums[j]) {
                temp[k++] = nums[i++];
            } else {
                temp[k++] = nums[j++];
            }
        }  

        // 把左边剩余的数移入数组
        while (i <= mid) {
            temp[k++] = nums[i++];
        }  

        // 把右边边剩余的数移入数组
        while (j <= high) {
            temp[k++] = nums[j++];
        }  

        // 把新数组中的数覆盖nums数组
        for (int k2 = 0; k2 < temp.length; k2++) {
            nums[k2 + low] = temp[k2];
        }
    }  

    // 归并排序的实现
    public static void main(String[] args) {
        int[] nums = { 2, 7, 8, 3, 1, 6, 9, 0, 5, 4 };
        MergeSort.sort(nums, 0, nums.length-1);
        System.out.println(Arrays.toString(nums));
    }
}

在求链表长度时,不要出现以下语句。会抛出空指针异常。

    	int len1 = 0;
    	int len2 = getLength(pHead2);
    	int difLen = 0;
    	// pHeadTmp1指向较长链表
    	ListNode pHeadLong = pHead1;
    	while(pHeadLong != null){
    	len1++;
    	pHeadLong = pHeadLong.next;
    	}
    	// pHeadTmp2指向较短链表
    	ListNode pHeadShort = pHead2;
    	if(len1 > len2){
    	difLen = len1 - len2;
    	}else{
// 此处会抛异常(在本地无异常)
    	pHeadLong = pHead2;
    	pHeadShort = pHead1;
    	difLen = len2 - len1;
    	}

相应的,可单独实现一函数计算链表长度,如下:

    private static int getLength(ListNode pHead){
    	int len = 0;
    	while(pHead != null){
    	len++;
    	pHead = pHead.next;
    	}
    	return len;
    }

美文美图

剑指Offer——归并排序思想应用的更多相关文章

  1. 结合《剑指offer(第二版)》面试题51来谈谈归并排序

    一.题目大意 给定一个数组A,对于数组A中的两个数字,如果排在前面的一个数字大于(必须大于,等于不算)后面的数字,则这两个数字组成一个逆序对.要求输出数组A中的逆序对的总数.例如,对于数组{7,5,6 ...

  2. 剑指 Offer 51. 数组中的逆序对 + 归并排序 + 树状数组

    剑指 Offer 51. 数组中的逆序对 Offer_51 题目描述 方法一:暴力法(双层循环,超时) package com.walegarrett.offer; /** * @Author Wal ...

  3. 对面试题(剑指offer)产生的一些思考。

    零散的思绪.另外,推荐<剑指offer>.本文初期大部分思考都从剑指引发. 面试题不单单只是用来面试.其中有很多编程的经验可以学习.就如同我们当年的考试:) 1:鲁棒性的一个方面:边界条件 ...

  4. 剑指Offer——分治算法

    剑指Offer--分治算法 基本概念 在计算机科学中,分治法是一种很重要的算法.字面上的解释是"分而治之",就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更 ...

  5. 剑指offer题目java实现

    Problem2:实现Singleton模式 题目描述:设计一个类,我们只能生成该类的一个实例 package Problem2; public class SingletonClass { /* * ...

  6. 《剑指offer》内容总结

    (1)剑指Offer——Trie树(字典树) Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种.典型应用是统计和排序大量的字符串(但不仅限于字符串),所以经常 ...

  7. 【Java】 剑指offer(51)数组中的逆序对

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成 ...

  8. 剑指offer算法总结

    剑指offer算法学习总结 节选剑指offer比较经典和巧妙的一些题目,以便复习使用.一部分题目给出了完整代码,一部分题目比较简单直接给出思路.但是不保证我说的思路都是正确的,个人对算法也不是特别在行 ...

  9. 剑指offer第五章

    剑指offer第五章 1.数组中出现次数超过一半的数 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字. 例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组 ...

随机推荐

  1. codeforces 809E Surprise me!

    Tired of boring dates, Leha and Noora decided to play a game. Leha found a tree with n vertices numb ...

  2. ●线段树题之wows

    ●模拟考试的一道似乎是学长出的题,还不错,挺考代码能力的.以此记录. ●题目以被上传(改了改说法),6128 Lence的方块们 ● (像手纸一样长的贴图……) ●题目大意: 有横向排布的n个点,每个 ...

  3. 17.10.31&11.01

    10.31模拟考试 Prob.1(AC)裸的矩阵幂 Prob.2(WA)(类似括号匹配求合法方案数) 卡特兰数的一个模型运用.可以推出一个式子(推导方法一个erge讲的,一个骚猪讲的) Prob.3( ...

  4. poj 2960 S-Nim

    S-Nim Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4113   Accepted: 2158 Description ...

  5. 2015 多校联赛 ——HDU5353(构造)

    Each soda has some candies in their hand. And they want to make the number of candies the same by do ...

  6. BZOJ1187 [HNOI2007]神奇游乐园(插头dp)

    麻麻我会写插头dp了! 推荐陈丹琦论文:https://wenku.baidu.com/view/3e90d32b453610661ed9f4bd.html 破题调一年 #include <cs ...

  7. Python 编程规范

    原文传送符:请点击

  8. js 输入密码框遇到的问题

    上次公司项目需要用到密码输入框,如图所示: 首先设计是设置六个div,然后放置六个input. 动态方面然根据键盘onkey事件进行判断,当按键放松时使前一个input失去焦点,下一个input获取焦 ...

  9. python字典无限遍历

    #无限遍历dict,通过key获取value,嵌套字典存在多个相同的key,可获取多个key class getValues(object): def __init__(self): pass #无限 ...

  10. 解决Popup StayOpen=true时,永远置顶的问题

    Popup设置了StayOpen=true时,会置顶显示. 如弹出了Popup后,打开QQ窗口,Popup显示在QQ聊天界面之上. 怎么解决问题? 获取绑定UserControl所在的窗口,窗口层级变 ...