lintcode题解 · 看云 - Google Chrome

 

原版文章:

题解:

寻找未排序数组的中位数,简单粗暴的方法是先排序后输出中位数索引处的数,但是基于比较的排序算法的时间复杂度为 O(nlogn)O(n \log n)O(nlogn), 不符合题目要求。线性时间复杂度的排序算法常见有计数排序、桶排序和基数排序,这三种排序方法的空间复杂度均较高,且依赖于输入数据特征(数据分布在有限的区间内),用在这里并不是比较好的解法。

由于这里仅需要找出中位数,即找出数组中前半个长度的较大的数,不需要进行完整的排序,说到这你是不是想到了快速排序了呢?快排的核心思想就是以基准为界将原数组划分为左小右大两个部分,用在这十分合适。快排的实现见 Quick Sort, 由于调用一次快排后基准元素的最终位置是知道的,故递归的终止条件即为当基准元素的位置(索引)满足中位数的条件时(左半部分长度为原数组长度一半)即返回最终结果。由于函数原型中左右最小索引并不总是原数组的最小最大,故需要引入相对位置(长度)也作为其中之一的参数。若左半部分长度偏大,则下一次递归排除右半部分,反之则排除左半部分。

Python代码:

class Solution:
    """
    @param nums: A list of integers.
    @return: An integer denotes the middle number of the array.
    """
    def median(self, nums):
        if not nums:
            return -1
        return self.helper(nums, 0, len(nums) - 1, (1 + len(nums)) / 2)

    def helper(self, nums, l, u, size):
        if l >= u:
            return nums[u]

        m = l
        for i in xrange(l + 1, u + 1):
            if nums[i] < nums[l]:
                m += 1
                nums[m], nums[i] = nums[i], nums[m]

        # swap between m and l after partition, important!
        nums[m], nums[l] = nums[l], nums[m]

        if m - l + 1 == size:
            return nums[m]
        elif m - l + 1 > size:
            return self.helper(nums, l, m - 1, size)
        else:
            return self.helper(nums, m + 1, u, size - (m - l + 1))

C++代码:

class Solution {
public:
    /**
     * @param nums: A list of integers.
     * @return: An integer denotes the middle number of the array.
     */
    int median(vector<int> &nums) {
        if (nums.empty()) return 0;

        int len = nums.size();
        return helper(nums, 0, len - 1, (len + 1) / 2);
    }

private:
    int helper(vector<int> &nums, int l, int u, int size) {
        // if (l >= u) return nums[u];

        int m = l; // index m to track pivot
        for (int i = l + 1; i <= u; ++i) {
            if (nums[i] < nums[l]) {
                ++m;
                int temp = nums[i];
                nums[i] = nums[m];
                nums[m] = temp;
            }
        }

        // swap with the pivot
        int temp = nums[m];
        nums[m] = nums[l];
        nums[l] = temp;

        if (m - l + 1 == size) {
            return nums[m];
        } else if (m - l + 1 > size) {
            return helper(nums, l, m - 1, size);
        } else {
            return helper(nums, m + 1, u, size - (m - l + 1));
        }
    }
};
Java

Java代码:

public class Solution {
    /**
     * @param nums: A list of integers.
     * @return: An integer denotes the middle number of the array.
     */
    public int median(int[] nums) {
        if (nums == null) return -1;

        return helper(nums, 0, nums.length - 1, (nums.length + 1) / 2);
    }

    // l: lower, u: upper, m: median
    private int helper(int[] nums, int l, int u, int size) {
        if (l >= u) return nums[u];

        int m = l;
        for (int i = l + 1; i <= u; i++) {
            if (nums[i] < nums[l]) {
                m++;
                int temp = nums[m];
                nums[m] = nums[i];
                nums[i] = temp;
            }
        }
        // swap between array[m] and array[l]
        // put pivot in the mid
        int temp = nums[m];
        nums[m] = nums[l];
        nums[l] = temp;

        if (m - l + 1 == size) {
            return nums[m];
        } else if (m - l + 1 > size) {
            return helper(nums, l, m - 1, size);
        } else {
            return helper(nums, m + 1, u, size - (m - l + 1));
        }
    }
}

源码分析:

以相对距离(长度)进行理解,递归终止步的条件一直保持不变(比较左半部分的长度)。

以题目中给出的样例进行分析,size 传入的值可为(len(nums) + 1) / 2, 终止条件为m - l + 1 == size, 含义为基准元素到索引为l的元素之间(左半部分)的长度(含)与(len(nums) + 1) / 2相等。若m - l + 1 > size, 即左半部分长度偏大,此时递归终止条件并未变化,因为l的值在下一次递归调用时并未改变,所以仍保持为size; 若m - l + 1 < size, 左半部分长度偏小,下一次递归调用右半部分,由于此时左半部分的索引值已变化,故size应改为下一次在右半部分数组中的终止条件size - (m - l + 1), 含义为原长度size减去左半部分数组的长度m - l + 1.

复杂度分析:

和快排类似,这里也有最好情况与最坏情况,平均情况下,索引m每次都处于中央位置,即每次递归后需要遍历的数组元素个数减半,故总的时间复杂度为 O(n(1+1/2+1/4+...))=O(2n)O(n (1 + 1/2 + 1/4 + ...)) = O(2n)O(n(1+1/2+1/4+...))=O(2n), 最坏情况下为平方。使用了临时变量,空间复杂度为 O(1)O(1)O(1), 满足题目要求。

个人思绪很混乱, 建议直接观看原文链接

个人理解如下:

代码加注释

import random

class Solution:
    """此方法只考虑列表长度为基数的情况"""
    def median(self, nums):
        if not nums:
            return -1
        return self.helper(nums, 0, len(nums) - 1, (1 + len(nums)) / 2)
    """
    1. 如果列表为空, 直接返回-1
    2. 反之, 将helper()函数返回
    """

    def helper(self, nums, l, u, size):
        if l >= u:
            return nums[u]
        """helper()函数的参数
            nums: 列表, l: 首元素下标, u: 尾元素下标, size: 中值的下标
            中值: 有序列表中间位置处的元素
        """

        m = l # 设定指针m, 起始为首元素下标: 0
        for i in range(l + 1, u + 1):
            # i指针为 从第二个元素到尾元素的下标
            if nums[i] < nums[l]:
                # nums[l]: 设定首元素为基准值, 遍历列表, 让每一个元素与之比较
                m += 1
                # 如果当前元素小于基准值, m指针后移一位
                nums[m], nums[i] = nums[i], nums[m]
                # 让i指针处的元素与m指针处的元素, 进行交换
        # 注: m指针始终指向比基准值小的元素,

        # swap between m and l after partition, important!
        # 循环结束, m指针左侧元素都比基准值小, 右侧元素都比基准值大
        # 将基准值插入到m指针处
        nums[m], nums[l] = nums[l], nums[m]

        if m - l + 1 == size:
            # 元素下标加一为: 当前元素到首元素的长度[包含首元素]
            # 如果左列表的长度, 等于中值的下标, 那么m指向的元素就是列表的中值
            return nums[m]
        elif m - l + 1 > size:
            # 如果左列表的长度, 大于中值的下标
            # 将左列表进行helper()的调用, 因为是左侧列表, 所以l的值不用变, m-1: m不为中值元素, 所以列表不用包含m
            return self.helper(nums, l, m - 1, size)
        else:
            # 如果左列表的长度, 小于中值的下标
            # 将右列表返回, 设定m+1为起始元素的下标, 因为是右列表所以u的值不用变, size要变为, 中值下标减去左列表的长度
            return self.helper(nums, m + 1, u, size - (m - l + 1))

if __name__ == "__main__":
    l = list(i for i in range(1, 12))
    print("洗牌之前的列表:" + str(l))
    random.shuffle(l)
    print("洗牌之后的列表:" + str(l))
    a = Solution()
    print(a.median(l))

原文地址:https://www.cnblogs.com/amou/p/9053734.html

时间: 2024-11-05 18:15:13

lintcode题解 · 看云 - Google Chrome的相关文章

Google Chrome插件分享

前言 浏览器是大家日常使用最多的工具之一,对于程序员来说,Google Chrome浏览器当然是大家优选的最爱之一.面对Chrome丰富的插件真的是爱不释手,如何把自己的Chrome调教成自己心仪的样子,现在和大家分享一下自己所安装的插件,先上个全家福如下: 插件 1.12306 春运订票 订票助手 火车票 对于春节抢过票的童靴们,这个一定不陌生,最早的抢票利器,对于我这种外出务工人员,逢年过节真的很需要一张回家的火车票,而且还是靠窗的硬座或是下铺的卧铺...你懂得,希望大家都可以抢票成功! 2

Google Chrome 浏览器必备扩展(适用于 Brave 和 Vivaldi 等)

// 前提 不同身份不同状态的人们,总是有着不同的上网需求! 浏览器插件,可以大大的扩展浏览器的功能,实现我们的上网需求. 访问 Chrome Web Store 浏览扩展,看到喜欢的就下载试试吧,没有用就立刻删掉吧. (不能访问 Chrome Web Store ,可以搜索 "谷歌访问助手" 或 "蓝灯" 的使用方法) (音乐:Are You Lonely) 下面 根据使用需求,对常用插件进行分类显示: 广告 用户诉求:摆脱烦人广告.恶意软件和跟踪,还我一个清爽网

Google Chrome Plus&mdash;&mdash;绿色便携多功能谷歌浏览器

一直就很喜欢用谷歌和火狐,不过浏览器默认功能是很少的,界面也不怎么好看,所以这个时候就需要浏览器三大神器:扩展,脚本,样式.Google Chrome Plus,我自己这么叫他,其实就是一个绿色便携多功能谷歌浏览器,是我的第一个定制版,是用shuax的补丁制作而成的,大家可以很方便的放进U盘使用,下面就详细的介绍一下我添加的东西. (不要被这么多的内容吓着,我只是写的比较详细罢了,并不复杂,看下去--) 分别从扩展,脚本,样式三个方面来介绍: [扩展]: 谷歌浏览器是一个多线程浏览器,每个扩展,

1.Google Chrome浏览器 控制台全解析

Google Chrome浏览器 控制台全解析 在Google Chrome浏览器出来之前,我一直使用FireFox,因为FireFox的插件非常丰富,更因为FireFox有强大的Firebug,对于前端开发可谓神器. 在Chrome出来的时候,我就喜欢上它的简洁.快速,无论是启动速度还是页面解析速度还是JavaScript执行速度(现在的FireFox4也比之前的FireFox3有很大的进步).不过当时由于Chrome的开发者工具还不是很完善,而我又不是很熟悉,加之对于Firebug的好感和依

Google Chrome浏览器调试功能介绍

作为Web开发人员,我为什么喜欢Google Chrome浏览器 [原文地址:http://www.cnblogs.com/QLeelulu/archive/2011/08/28/2156402.html ] 在Google Chrome浏览器出来之前,我一直使用FireFox,因为FireFox的插件非常丰富,更因为FireFox有强大的Firebug,对于前端开发可谓神器. 在Chrome出来的时候,我就喜欢上它的简洁.快速,无论是启动速度还是页面解析速度还是Javascript执行速度(现

Google Chrome调试js入门

1 我们要调的json数据见下图,这里不涉及和后台的交互. 2 我们在chrome中打开开发者工具,打开方式如下图,我们也可以使用快捷键F12来打开.选择我们要调试的文件,相信你能找到,^_^. 3 设置断点的设置,我在11行设置了断点,设置方法左侧点击即可,我太喜欢这个断点了,看起来真漂亮.刷新一下页面,执行如下图: 4 在向下执行之前,我们直接输入json(我们的对象名),看看对象结构,可以非常清晰的看到json到底是个什么东西,不用太多的解释,如下图: 5 按F10向下执行,我们依次输入我

Google Chrome七大新特性

Google Chrome 在日常生活中扮演的角色不只是一个功能强大的网络浏览器,它内置的 DevTools 同样也是网络开发者进行网络开发的重要工具. DevTools 在不断的进行版本更新,其中有很多重要的更新细节你可能会错过.在这里罗列了 Google Chrome 最值得关注的七大新特性. 开始——Chrome 的 DevTools Experiment Chrome 有一些非常棒的实验性特性,这些特性在默认情况下都是隐藏和禁用的.在浏览器地址栏输入chrome://flags/#ena

作为Web开发人员,我为什么喜欢Google Chrome浏览器

在Google Chrome浏览器出来之前,我一直使用FireFox,因为FireFox的插件非常丰富,更因为FireFox有强大的Firebug,对于前端开发可谓神器. 在Chrome出来的时候,我就喜欢上它的简洁.快速,无论是启动速度还是页面解析速度还是Javascript执行速度(现在的FireFox4也比之前的FireFox3有很大的进步).不过当时由于Chrome的开发者工具还不是很完善,而我又不是很熟悉,加之对于Firebug的好感和依赖,当时还是用回FireFox作为我的主浏览器.

Google Chrome浏览器的使用方法

Google Chrome浏览器 [原文地址:http://www.cnblogs.com/QLeelulu/archive/2011/08/28/2156402.html ] 在Google Chrome浏览器出来之前,我一直使用FireFox,因为FireFox的插件非常丰富,更因为FireFox有强大的Firebug,对于前端开发可谓神器. 在Chrome出来的时候,我就喜欢上它的简洁.快速,无论是启动速度还是页面解析速度还是Javascript执行速度(现在的FireFox4也比之前的F