本教程带你从初级到高级全面掌握 Javascript 的使用方法
这是一个很好的简单课程,只需2小时你就可以学习TypeScript基础知识。
本教程带您从零开始学习 Vue 框架的使用,让您轻松应对 Vue 项目的开发。
本教程涵盖Ajax的实现原理,及Ajax封装,最后是框架实现方法。
对比 ES5 进行学习 ES6+,理解 ES6+ 语法背后的思想
Yarn得相关基础知识和高级进阶
从零开始学习 ECharts ,掌握 ECharts 核心内容
本课程从盒模型、文字、颜色、过渡、动画、布局、伪类等方面介绍 CSS3 的使用。
本文详细介绍了雪碧图的由来历史以及各种使用方式
由于移动互联网的兴起,移动端项目占据了很大一部分比重,本章将详细讲解几种常见布局
最新一代的HTML标准,增加了许多实用的特性
前端项目中 Sass 的快速入门教程
从零讲解 HTML,掌握基础 HTML 知识内容
本教程带你从初级到高级全面掌握canvas的使用方法
从零开始学习 uni-app 框架,轻松上手应用开发
本教程使您掌握 Nginx 安装、配置、核心模块的详解、实际使用的能力。
从协议原理开始到 Web 服务器以及 Web 安全一网打尽
从 Docker 的基础概念开始,从实际问题入手带你学习 Docker
本教程由浅入深,系统性的讲解Linux Shell脚本编程。
本教程从安装 Linux 开始,囊括 Linux 基础命令操作以及进阶系统管理
本教程使您掌握实际使用gradle进行项目构建、测试、打包、发布的能力。
课程主要讲解Vim的安装配置,四种模式、基本操作,以及包管理工具和寄存器等内容。
本教程从什么是 REST 开始带你领略 Web 开发中无处不在的规范
DW 是一款同时具有网页制作和网页管理功能的网站开发工具,可以快速进行网站建设
本课程涵盖 Markdown 的基本及扩展语法。
从最基础的安装 Maven 开始到 Maven 在开发中的实际应用
本教程从Eclipse安装开始带你轻松掌握Eclipse常用开发技巧
本教程带你轻松掌握最实用的 GitHub 知识
Android Studio 编程技巧一网打尽
工作经常用到的 PyCharm 编辑器使用技巧一网打尽
花里胡哨展示sublime编辑器的各种功能
Postman 由Google 开发用来做接口请求测试,前后端开发人员都可以使用
从入门到精通。
本教程从语法基础、进阶知识等各方面详解 C 语言。
本教程从 Go 语言的基本语法掌握到进阶编程实践
从 Kotlin 的基础语法到高级特性一网打尽
本教程从 Ruby 的各种对象开始学习到 Ruby 的实际使用
本教程主要讲解 ThinkPHP 框架如何上手开发应用
深入浅出讲解 Java 语言基础知识,带你入门 Java 语言
为你解析最实用的 Android 技术,让你平滑上手,顺利进阶,为开发保驾护航
分析讲解常见算法的思想及使用
通俗易懂的带你了解 Java 数据结构
本教程展现了Lambda表达式的基础语法以及在程序中的应用
本教程为Java并发原理入门教程,在Java程序开发中占据着举足轻重的地位
带你分析最常见的九个设计模式
本课程简洁明了展示最基本的并发工具类相关概念及应用方法。
JVM 入门教程,对JVM结构进行分模块讲解,简单易懂。
超系统的RabbitMQ基础知识课程,你还在等什么?
Java 网络编程核心要点详解
带你系统梳理后端高频面试题,轻松丰富你的校招&社招阶段
循序渐进讲解 Spring Boot 企业级应用开发
通俗易懂 渐进式讲解 Spring 企业级开发应用
由浅入深讲解 Hibernate 企业级 JDBC 应用框架
本教程整理出"百分之二十"的知识,帮你办到"百分之八十"事情
通俗易懂讲解 Spring MVC 框架应用
本课程以图文并茂的方式带你学习 Swagger 核心知识和应用剖析
由浅入深的 学习 ZooKeeper 的基本使用以及高级使用
由浅入深的讲解 Netty 的核心知识体系,快速上手使用和理解 Netty
本课程涵盖了 Spring Security 框架的基本原理和集成方法
系统介绍 Hystrix 支持特性与实际应用场景实战
本教程带你从 Python 的基础语法开始学习 Python。
本教程从爬虫基础知识到进阶技巧到实际应用。
本教程涵盖 Python 的面向对象、标准库解析、异常处理直至最后的领域应用
用 Python 代码实现常用算法并汲取算法核心思想。
从 Web 基础到 Django 框架的实际开发应用
Flask 框架快速入门实现一个 TodoList 功能
本教程从基础的数据类型开始到 NumPy 的高级应用一网打尽
从爬虫基础开始到使用 Scrapy 框架抓取各大网站数据
通过本教程对 TensorFlow 框架快速入门
本教程带你使用Python快速操作Excel、Word、PPT,处理各种文件
本教程从基础的数据类型开始到 Pandas 的高级应用一-网打尽
本教程主要讲解 MySQL 增删改查等基础操作
本教程讲解使用 SQL 访问和处理数据系统中的数据的方法。
那些你还不理解的 MySQL 高阶特性一网打尽
在上一节介绍了一些初级的编程题后,对贪心算法有了初步的理解,接下来我们会用 leetcode 上 3 道中级和困难题目来帮助大家进一步掌握和应用贪心算法。
这是 leetcode 上算法部分第55题,为中级编程题。题目描述如下:
给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。
示例 1:
输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
示例 2:
输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。
这道题稍微有点复杂,官方给出的解法正是贪心思路:首先思考对于数组中任意一个位置 y, 如何判断它是否可达?根据题目表示,只要存在一个位置 x,它本身可达,且它跳跃的最大长度为 x + nums[x],如果这个值大于等于 y,即 x + nums[x] >= y,即位置 y 也可达。对于每一个可以到达的位置 x,它使得 x+1, x+2, ... , x+nums[x] 这些连续的位置都可以到达。
这样以来,我们依次遍历数组中的每一个位置,并实时维护最远可以到达的位置。
对于当前遍历到的位置 x, 如果它在最远可以到达的位置的范围内,那么我们就可以从起点通过若干次跳跃到达该位置,因此我们可以用 x + nums[x] 更新最远可以到达的位置。
在遍历的过程中,如果最远可以到达的位置大于等于数组中的最后一个位置,那就说明最后一个位置可达,我们就可以直接返回 True 作为答案。反之,如果在遍历结束后,最后一个位置仍然不可达,我们就返回 False 作为答案。这便是官方给出的贪心思路,非常清晰明了。
最后根据上述思路我们给出完整的 Python 解答,这个题解来自 leetcode 题解中的一个优秀回答,和官方给出的贪心思路一致,都是维护一个从起始点出发可以达到的最远坐标。
def canJump(nums):
# 如果数组中没有0,则一定可以到达
if 0 not in nums: return True
# 如果只有一个元素,也可以到达
if len(nums) < 2: return True
# 维护最长距离
max_distance = 0
for i in range(len(nums) - 1):
# i <= max_distance 表明当前位置可以到达
if i <= max_distance:
# 更新最大距离
max_distance = max(i + nums[i], max_distance)
else:
# 如果当前位置无法到达则结束
break
# 返回能到达的最大距离是否能到最后一位
return max_distance >= len(nums) - 1
我们来看看官方给出的示例代码,也比较精简,思路和原理一样,只不过代码写法不同。原代码为 java,现转成 Python,内容如下:
def canJump(nums):
# 维持最大距离
max_distance = 0
for i in range(len(nums)):
# 非常重要,该点必须可达
if i <= max_distance:
# 该点可达,更新能到的最大距离
max_distance = max(i + nums[i], max_distance)
# 如归最大距离能到最后一个位置,直接返回 True
if max_distance >= (len(nums) - 1):
return True
else:
# 如果i位置无法到达,那么可以直接退出循环,返回False
break
# 无法到达最后,返回False
return False
这是 leetcode 上算法部分的第765题,属于困难级别,题目描述如下:
N 对情侣坐在连续排列的 2N 个座位上,想要牵到对方的手。 计算最少交换座位的次数,以便每对情侣可以并肩坐在一起。 一次交换可选择任意两人,让他们站起来交换座位。
人和座位用 0 到 2N-1 的整数表示,情侣们按顺序编号,第一对是 (0, 1),第二对是 (2, 3),以此类推,最后一对是 (2N-2, 2N-1)。
这些情侣的初始座位 row[i] 是由最初始坐在第 i 个座位上的人决定的。
示例 1:
输入: row = [0, 2, 1, 3]
输出: 1
解释: 我们只需要交换row[1]和row[2]的位置即可。
示例 2:
输入: row = [3, 2, 0, 1]
输出: 0
解释: 无需交换座位,所有的情侣都已经可以手牵手了。
Tips:
len(row)是偶数且数值在[4, 60]范围内;可以保证
row是序列0...len(row)-1的一个全排列。
这道题虽然给出困难级别,但是主要是针对使用 O(n) 的算法。如果使用简单的贪心算法,时间复杂度为 ,且解题思路非常清晰。
官方给出的第三种解法正是贪心算法,其思路为:每处理一对元素,如果第二个元素不是第一个元素的情侣,那么就在全局找到第一个元素的情侣,交换他们的位置;依此操作,知道最后一对情侣被安排好。
其中对于位置编号为 x 的人,其情侣编号为 x^1 (^表示异或),这样的写法比较精妙,难以想到,也算是一个小的技巧。有了这个思路,来直接写出相应的 Python 代码,如下:
def minSwapsCouples(row):
if len(row) <= 2:
return 0
res = 0
for i in range(0, len(row), 2):
# row[i]的情侣编号
couple_id = row[i] ^ 1
# 如果旁边正好是他的情侣,直接下一对继续判断
if row[i + 1] == couple_id:
continue
# 如果不是,则需要一次交换
res += 1
# 遍历后续的人,找到row[i]的情侣,然后交换和row[i+1]的位置
for j in range(i + 2, len(row)):
if row[j] == couple_id:
row[i + 1], row[j] = row[j], row[i + 1]
break
# 返回总的交换次数
return res
从上面可以看到,算法使用了两个 for 循环,时间复杂度为 O(n^2)。第二个 for 循环是找对应 row[i] 的情侣,有没有办法加快查找速度呢?在 leetcode 的题解中,有人给出了一个优化的解法:将编号和其当前所在位置的映射关系单独使用一个列表保存,这样我们查找比如编号为 i 的所在的位置时只需要 O(1) 的复杂度即可:
for j in range(len(row)):
seatmap[row[j]] = j
之所以能这样做的原因就在于题目说明中的第二点:row 中元素的编号是序列 0...len(row)-1 的一个全排列。这是一个典型的用空间换时间的方式。这样优化后的时间复杂度为 O(n),空间复杂度也为 O(n)。具体的实现代码如下:
def minSwapsCouples2(row):
res = 0
seatmap= [0 for _ in range(len(row))]
for j in range(len(row)):
# 序号为row[j]的人的座位号为j
seatmap[row[j]] = j
for i in range(0, len(row), 2):
# 得到row[i]对应的情侣编号
x = row[i]^1
if x == row[i+1]:
continue
# 找到序号为x的人对应的座位号
index = seatmap[x]
# 交换座位使情侣坐一起
row[i+1], row[index] = row[index], row[i+1]
# 更新seatmap,序号为row[index]的人现在在index上
seatmap[row[index]] = index
# 对于编号为x人交换到了i+1,这个动作可以不做,因为已经配对后续不会再找编号为x的人
# seatmap[x] = i + 1
res += 1
return res
这题是 leetcode 算法部分的135题,难度级别为困难。题目描述如下:
老师给孩子们分发糖果,有 N 个孩子站成了一条直线,老师根据每个孩子的表现,预先给他们评分。你需要按照以下要求,帮助老师给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果;
相邻的孩子中,评分高的孩子必须获得更多的糖果;
那么这样下来,老师至少需要准备多少颗糖果呢?
示例 1:
输入: [1,0,2]
输出: 5
解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。
示例 2:
输入: [1,2,2]
输出: 4
解释: 你可以分别给这三个孩子分发 1、2、1 颗糖果。第三个孩子只得到 1 颗糖果,这已满足上述两个条件。
对于这题,官方给出了 4 种解法,其中解法 2 是使用了贪心算法的思想。现在我们重点介绍下官方的解法 2 以及其贪心点。
在解法 2 中,使用两个一维数组 left 和 right。数组 left 用来存储每名学生只与左边邻居有关的所需糖果数。也就是假设规则为:如果一名学生评分比他左边学生高,那么他应该比他左边学生得到更多糖果。类似的,right 数组用来保存只与右边邻居有关的所需糖果数。也就是假设规则为:如果一名学生评分比他右边学生高,那么他应该比他右边学生得到更多糖果。
首先,在 left 和 right 数组中,给每个学生 1 个糖果。然后从左向右遍历整个数组,只要当前学生评分比他左邻居高,我们在 left 数组中更新当前学生的糖果数 left[i] = left[i-1] + 1,这是因为在每次更新前,当前学生的糖果数一定小于等于他左邻居的糖果数。
接下来用同样的方法从右到左,只要当前学生的评分比他右边(第 i+1 个)学生高,就更新 right[i] 为 right[i] = right[i + 1] + 1。现在,对于数组中第 i 个学生,为了满足题中条件,我们需要给他 max(left[i], right[i]) 个糖果。因此,最后我们得到最少糖果数:
最少糖果数 = ,其中, nn 是评分数组的长度。
上述算法在遍历过程中,每一步都尽量少给糖,必须加的时候加一个,这便体现了贪心思想:且在每次选择时,以局部最优为导向,而不考虑此次操作对以后操作的影响。有了上面的题解思路,我们的 Python 的代码就能理解写出,如下:
def candy(ratings):
# 初始化值
left = [1] * len(ratings)
right = [1] * len(ratings)
# 从左到右,只要当前学生评分比他左邻居高,则该学生糖果数相比左邻居加1
for i in range(1, len(ratings)):
if ratings[i] > ratings[i - 1]:
left[i] = left[i - 1] + 1
# 从右到左,只要当前学生评分比他右邻居高,则该学生糖果数相比右邻居加1
for i in range(len(ratings) - 2, -1, -1):
if ratings[i] > ratings[i + 1]:
right[i] = right[i + 1] + 1
# 最后得到最少糖果数
return sum([max(left[i], right[i]) for i in range(len(ratings))])
这里的贪心过程会有些难以理解,需要仔细揣摩。
本小节继续实战了贪心算法相关的编程题,这一次难度相比上一节有所加深。在 leetcode 上大约 80 道标签为贪心算法的编程题,解题思路都是类似的。再掌握了贪心的解题思路后,大家可以多多尝试解答 leetcode 上的其他编程题,达到熟能生巧的地步。
0/1000