LeetCode免费中等题描述(1-5页)

记录1-5LeetCode免费中等题的描述,总计202题。

2. Add Two Numbers

求以链表形式保存的两个数的和,以链表形式输出,输入和输出都是数字的倒序。

3. Longest Substring Without Repeating Characters

求字符串的不包含重复字符的最长子串长度。

5. Longest Palindromic Substring

求字符串的最长回文子串。

6. ZigZag Conversion

给定字符串和指定行数,将字符串按照类似“W”的方式展开,按行拼接输出。

8. String to Integer(atoi)

实现字符串转整数,去前导空格,前导零,出现合法数字后截断非数字字符。

11. Container With Most Water

给定正整数数组A,对下标ijmin(A[i],A[j])*abs(i-j)的最大值。

12. Integer to Roman

正整数转罗马数字

15. 3Sum

给定一个整数数组,求在数组中取三个数相加和为0的所有非重复元组。

16. 3Sum Closest        

给定一个整数数组和目标整数,求在数组中取三个数相加离目标整数最近的整数。

17. Letter Combinations of a Phone Number

对于输入数字字符串,求所有可能的9键手机输入法对应的英文字符串。

18. 4Sum

给定一个整数数组和目标整数,求所有和为目标整数的非重复四元组。

19. Remove Nth Node From End of List

移除链表的倒数第N个节点。

22. Generate Parentheses

对给定数字N,表示有N对括号,求所有可能的合法排列。

24. Swap Nodes in Pairs

将链表的每两个相邻元素交换,例如1234->2143

29. Divide Two Integers

不用乘除和模运算,求两数相除的结果。

31. Next Permutation

对给定数组,原地将数组存成按字典顺序的下一个数组,字典序最大则输出最小。

33. Search in Rotated Sorted Array

旋转无重复有序数组查找。

34. Find First and Last Position of Element in Sorted Array

对有序可重复数组在O(logN)时间内查找某数出现的下标范围。

36. Valid Sudoku

判断已经填了一些数的数独当前是否合法。

39. Combination Sum

给定一个整数集合和目标整数,集合中的数可以多次使用,求所有和为目标整数的非重复元组。

40. Combination Sum II

给定一个整数数组和目标整数,数组中的数只能使用一次,求所有和为目标整数的非重复元组。

43. Multiply Strings

字符串形式的大数乘法。

46. Permutations

求指定无重复数组的所有可能的排列。

47. Permutations II

求指定可重复数组的所有可能的无重复排列

48. Rotate Image

原地旋转二维数组90度。

49. Group Anagrams

将字符串数组按照变位词分组。

50. Pow(x, n)

实现doubleint

54. Spiral Matrix

将二维数组按螺旋顺序输出。

55. Jump Game

给定数组中的值表示可以前进的步数,判断是否可以到达数组尾部。

56. Merge Intervals

合并多个区间。

59. Spiral Matrix II

对数字N,输出1~N*N的整数对应的N*N大小的二维螺旋数组。

60. Permutation Sequence

对数字NK,输出1~N总共N个数字的所有排列中的第K个。

61. Rotate List

指定旋转步数,对链表进行旋转。

62. Unique Paths

计算M*N的矩阵,有几种方法从左上角走到右下角,只能右移或下移。

63. Unique Paths II

给定有障碍物的M*N矩阵,有几种方法从左上角走到右下角,只能右移或下移。

64. Minimum Path Sum

给定M*N矩阵,从左上角走到右下角,只能右移或下移,求最大路径和。

71. Simplify Path

简化Unix风格的文件路径,去掉多余斜杠,处理“.”和“..”。

73. Set Matrix Zeroes

给定二维数组,在原地将含0的行或列整体赋值为0

74. Search a 2D Matrix

每行递增排序的二维数组,并且每一行的开头大于前一行的末尾,查找是否包含目标值。

75. Sort Colors

对只含012的数组排序,进阶:one-passO(1)空间。

77. Combinations

求从1~N的数中取K个数的所有组合。

78. Subsets

求无重复数组的所有非重复子集。

79. Word Search

给定二维字母数组和单词,从某个位置开始,可以向四周移动,走过的位置不能再走,求是否能找到该单词。

80. Remove Duplicates from Sorted Array II

O(1)空间,原地将排序数组中出现次数大于2的删除,求剩余长度。

81. Search in Rotated Sorted Array II

旋转可重复有序数组查找。

82. Remove Duplicates from Sorted List II

删除链表中的重复元素。

86. Partition List

给定链表和数字X,将链表中小于X的数移到大于等于X的数的前面,保持原有顺序。

89. Gray Code

格雷码中连续值进在一个二进制位上不同,给定非负整数N,打印格雷码序列,格雷码以0开头。

90. Subsets II

求可重复数组的所有非重复子集。

91. Decode Ways

大写字母A~Z对应1~26,给定数字字符串,求该串对应的字母串的数量。

 

92. Reverse Linked List II

将链表的指定区间翻转。

93. Restore IP Addressed

给出IP地址去掉小数点后的数字字符串,求所有可能的原IP地址。

94. Binary Tree Inorder Traversal

二叉树中序遍历,进阶:用迭代的方式遍历。

95. Unique Binary Search Trees II

对给定数字N,求1~N总共N个数字对应的所有可能的BST

96. Unique Binary Search Tree

对给定数字N,求1~N总共N个数字对应的所有可能的BST的数量。

98. Validate Binary Search Tree

判断二叉树是否是BST

102. Binary Tree Level Order Traversal

二叉树层次遍历。

103. Binary Tree Zigzag Level Order Traversal

二叉树特殊层次遍历,首层顺序,第二层逆序,以此类推。

105. Construct Binary Tree from Preorder and Inorder Traversal

由先序遍历和中序遍历还原二叉树结构。

106. Construct Binary Tree from Inorder and Postorder Traversal

由中序遍历和后序遍历还原二叉树结构。

109. Convert Sorted List to Binary Search Tree

将排序的数组转换为每个节点的左右子树高度差不超过1BST

113. Path Sum II

给定二叉树和某个整数S,求所有和为S的根到叶节点的路径。

114. Flatten Binary Tree to Linked List

原地将二叉树转换为所有节点的左节点都为空的二叉树,左节点在右节点前面。

116. Populating Next Right Pointers in Each Node

为二叉树实现记录next节点(层次遍历的下一个节点)。perfect binary tree (ie, all leaves are at the same level, and every parent has two children).

117. Populating Next Right Pointers in Each Node II

为二叉树实现记录next节点(层次遍历的下一个节点),允许节点的左右节点不完整。

120. Triangle

给定正三角形形状的二维数组,每个节点可以连通下一层的最近两个节点,求顶部根节点到最下层叶节点的路径和的最小值。

127. Word Ladder

给定开始词和结束词以及单词数组,所有单词长度相等,每次变换只能从变成单词数组中的词,只有一个字母不同才能变换,求从开始词到结束词需要变换的路径长度。

129. Sum Root to Left Numbers

给定数值二叉树,从根到叶节点路径上的数转换为十进制数,求所有路径对应的数的和。

130. Surrounded Regions

给定二维字符数组,其中有两种棋子XO,将被包围的O转换为X

131. Palindrome Partitioning

将字符串划分为多个字符串,保证其中每个串都是回文串,求所有的划分方法。

133. Clone Graph

实现无向图拷贝函数。

134. Gas Station

给定两个数组gascostgas表示到达该点可以加多少油,cost表示从此点出发要消耗多少油,只能向下一个点前进,判断从哪个点出发可以遍历所有的点。

137. Single Number II

线性时间,O(1)空间,求每一个元素只可能出现一次或三次的整数数组中出现一次的数。

138. Copy List with Random Pointer

某个链表包含指向该链表中某个节点的随机指针,实现深拷贝。

139. Word Break

给定字符串s和字符串数组dict,判断s是否可以有dict中的单词组成。

142. Linked List Cycle II

找到链表的环的入口,不能修改列表,进阶:O(1)空间。

143. Reorder List

特殊方式对链表重排列,不能修改值,L0L1→…→Ln-1Ln转换为L0LnL1Ln-1L2Ln-2→…。

144. Binary Tree Preorder Traversal

二叉树先序遍历,进阶:用迭代的方式遍历。

147. Insertion Sort List

用插入排序的方式对链表进行排序。

148. Sort List

O(NlogN)时间,O(1)空间对链表排序。

150. Evaluate Reverse Polish Notation

给出表达式对应的树形结构的后序遍历顺序,求表达式对应的值。

151. Reverse Words in a String

句子以空格为分隔符,从最后一个单词开始,倒序输出各个单词,进阶:使用CO(1)空间完成。

152. Maximum Product Subarray

求最大的连续子数组的乘积。

153. Find Minimum in Rotated Sorted Array

找到无重复旋转数组的最小值。

162. Find Peak Element

对数时间,给定相邻数组值不等的数组,寻找任意一个大于左右邻居的数,数组边缘默认大于越界邻居。

165. Compare Version Numbers

对比两个版本号的大小。

166. Fraction to Recurring Decimal

计算两个数的除法,如果是无限循环小数要标记循环节。

173. Binary Search Tree Iterator

O(1)时间,O(树高)空间,对BST设计迭代器next函数和hasNext函数。

177. Nth Highest Salary

使用SQL查询薪水是第N高的人的薪水。

178. Rank Scores

使用SQL查询分数表,将分数按降序排列,并记录相应分数的排名。

179. Largest Number

给定非负整数数组,数组中的数可以拼接为更大的数,求拼接的数的最大值。

180. Consecutive Numbers

使用SQL查询数值表,如果某个数值连续出现了三次,则进行记录。

184. Department Highest Salary

使用SQL查询,给定人员工资表,部门表,求每个部门工资最高的人的名字和工资。

187. Repeated DNA Sequences

DNA序列是由ACGT字母组成的字符串,求所有出现次数至少为2次的长度至少为10的子串。

192. Word Frequency

使用Bash统计文本词频,按词频大小逆序分行输出词语和词频。

194. Transpose File

使用Bash对一个多行由空格隔开的文本矩阵进行转置。

 

199. Binary Tree Right Side View

求二叉树层次遍历时每一层的最右节点。

200. Number of Islands

给定值为01的二维数组,1为陆地,0为水,求岛屿数目。

201. Bitwise AND of Numbers Range

求给定范围内所有数进行与的位操作的结果。

207. Course Schedule

给定正整数N表示有0~N-1总共N门课程,另外给出课程间的依赖关系,求是否能按照一定的顺序学完所有课程。

208. Implement Trie (Prefix Tree)

实现前缀树的插入和搜索,以及前缀查找。

209. Minimum Size Subarray Sum

给定数字s和正整数数组nums,求使得连续子数组的和大于等于s的最小尺寸的子数组,进阶:如果想出O(N)解法,尝试O(NlogN)的解法。

210. Course Schedule II

给定正整数N表示有0~N-1总共N门课程,另外给出课程间的依赖关系,求是否能按照一定的顺序学完所有课程,并给出学习的顺序。

211. Add and Search Word – Data structure design

字典树的插入和查找,查找时可以使用“.”匹配任意字符。

213. House Robber II

给定非负整数数组,首尾相连,从中取一些数,相邻的数不能同时取,求所能取到的数的和的最大值。

215. Kth Largest Element in an Array

求整数数组中第K大的数。

216. Combination Sum III

给定数字KN,求所有K个不重复的1~9的数和为N的不重复的组合。

220. Contains Duplicate III

给定长度为N的数组,整数KT,如果可以在数组中找到两个数,下标差小于等于K,值的差的绝对值小于等于T,返回true,否则返回false

221. Maximal Square

给定包含数字01的二维数组,找到其中最大的由1构成的正方形。

222. Count Complete Tree Nodes

计算完全二叉树(除了最后一层都是全满,最后一层有一个划分点,左边全满,右边全空)的节点数量。

223. Rectangle Array

给定两个矩形的左下角和右上角的坐标,求重合面积。

227. Basic Calculator II

给定包含数字和空格以及加减乘除符号的字符串,求对应表达式的计算结果。

228. Summary Ranges

给定排序的无重复整数数组,将其中相邻的递增1的子数组只记录头和尾。

229. Majority Element II

找到数组中出现次数大于N/3的数,O(1)空间。

230. Kth Smallest Element in a BST

BST中第K小的数。

236. Lowest Common Ancestor of a Binary Tree

给定二叉树,其中各值唯一,求两个节点的最低公共祖先。

238. Product of Array Except Self

给定一个数组,对每个位置求除了该位置的所有数的乘积,不能用除法,并且在O(N)时间完成,进阶:O(1)空间,输出数组不算。

240. Search a 2D Matrix II

每行每列都是递增排序的二维数组,查找是否包含目标值。

241. Different Ways to Add Parentheses

给定一个包含加减乘的表达式字符串,求所有可能的加括号方式对应的运算结果。

260. Single Number III

线性时间,O(1)空间,给定整数数组中有两个数只出现一次,其他的数都是出现两次,求只出现一次的两个数。

264. Ugly Number II

丑数是1和因子只含235的正整数,求第N个丑数。

274. H-Index

给定一个研究者的N篇论文的引用数,求该研究者的最大h-index(如果有h篇文章的引用数至少为hN-h篇的引用数小于等于h)。

275. H-Index II

给定一个研究者的N篇论文的引用数,按升序排列,求该研究者的最大h-index(如果有h篇文章的引用数至少为hN-h篇的引用数小于等于h),进阶:O(logN)时间。

279. Perfect Squares

给定正方形的面积和S,求最少需要几个正方形。

284. Peeking Iterator

对于一个包含next函数和haveNext函数的迭代器,使用继承的方式设计一个包含peek函数的新的迭代器。

287. Find the Duplicate Number

O(1)空间,小于O(N2)时间,不修改数组,在长度为N+1的只包含1N的数组中,寻找唯一的出现次数大于1的数,其他数只出现一次。

289. Game of Life

给定值为01的二维数组,表示活细胞和死细胞,根据周围细胞的情况,两者可以转换,求转换后的结果,进阶:原地转换,边界。

 

299. Bulls and Cows

判断两串数字中位置相同,值相同的数的数量,除此之外判断值相同,位置不同的数的数量。

300. Longest Increasing Subsequence

求整数数组中最长递增子序列长度。

304. Range Sum Query 2D – Immutable

给定整数二维数组,求多组矩形内数字之和。

306. Additive Number

给定数字字符串,判断是否可以将字符串划分为多个字符串,每个字符串转换为数字,每个数字都等于前两个数字之和,进阶:大数。

307. Range Sum Query – Mutable

整数数组单点修改,求指定区间和,线段树(树状数组)。

309. Best Time to Buy and Sell Stock with Cooldown

给定整数数组,表示每一时刻的股票价格,需要卖掉当前持有的股票才能买新的,卖完要隔一天才能买,求最大收益。

310. Minimum Height Trees

给定N个点,N-1条边的可转换为树的图,求以哪些点作为根可以得到最小高度的树。

313. Super Ugly Number

给定一个质数数组,超级丑数是1和能因子只在该数组中的数的正整数,求第N个超级丑数。

318. Maximum Product of Word Lengths

给定字符串数组,在其中挑两个没有重复字符的字符串,求可能的最大长度乘积。

319. Bulb Switcher

给定N个灯泡开关,初始全关,第一次操作每个开关状态变换,第二次操作每隔一个开关状态变换,以此类推,求最后亮的灯泡数量。

322. Coin Change

给定一些面额的硬币无限量和想要凑的值,求需要的最少硬币数量。

324. Wiggle Sort II

给定整数数组,重新排序使得nums[0] < nums[1] > nums[2] < nums[3]....

328. Odd Even Linked List

给定链表,第一个节点是奇节点,在O(1)空间,O(N)时间将链表转换为所有奇节点按顺序在前,偶节点按顺序在后。

331. Verify Preorder Serialization of a Binary Tree

给定二叉树的先序遍历结果,以字符串表示,“#”表示空节点,判断字符串是否可以还原二叉树。

332. Reconstruct Itinerary

输入是多个字符串对,表示从A地移动到B地,需要找到从“JFK”出发的,遍历所有边的,字典序最小的移动路线。

334. Increasing Triplet Subsequence

O(N)时间,O(1)空间,判断整数数组中是否包含长度为3的递增子序列。

337. House Robber III

正整数二叉树型结构,不能取相邻节点,取若干点求和,求所能取到的最大值。

338. Counting Bits

给定非负整数N,求0~N总共N+1个整数中每个整数的二进制表示中1的数量,进阶:严格线性时间(没有sizeof(int)的常数),一轮,O(N)空间,不用类似__builtin_popcount的库函数。

341. Flatten Nested List Iterator

定义NestedInteger数据结构,它可以是单个整数,也可以是NestedInteger数组,由isInteger函数判断。设计NestedIterator结构,输入是NestedInteger数组,包含nexthaveNext函数。

343. Integer Break

对给定正整数N,可以将其表示为多个正整数的和,求所有可能的表示方法中,所有数字乘积最大的乘积。

347. Top K Frequent Elements

找到整数数组中出现频率最高的K个数。

355. Design Twitter

设计名为Twitter的数据结构,包含postTweet(userId, tweetId)getNewsFeed(userId)follow(followerId, followeeId)unfollow(followerId, followeeId)方法,每个用户的列表里要显示自己和他关注的人的总共最近10twitter

357. Count Numbers with Unique Digits

计算0~10N-1中不包含重复数字的数的数量。

365. Water and Jug Problem

给两个桶的容量,无限的水,每次可以加满一桶或清空一桶,也可以两桶间倒水,求是否可以得到指定数量的水。

368. Largest Divisible Subset

给定无重复的正整数数组,求最大子集,使得集合内的数对其中之一可以被另一个整除。

372. Super Pow

给定整数a和以数组形式表示的大数b,求ab mod 1337

373. Find K Pairs with Smallest Sums

给定两个递增排序数组AB和整数K,从这两个数组中各取一个数,求所有取法中两数和位于前K小的数对。

375. Guess Number Higher or Lower II

给定整数N,表示需要在1~N总共N个整数中猜一个数,每次猜错数需要付费所猜的数字的钱,可以得到猜多了还是猜少了,猜对免费,求准备好多少钱才能保证猜对。

376. Wiggle Subsequence

相邻两个数字的差异正负交替称为摆动序列(首个差异正负均可,只有两个数也算),求给定数组的最长摆动子序列的长度。

377. Combination Sum IV

给定无重复正整数数组和目标整数,从数组中选取数字求和为目标整数,每个数字可以使用多次,求取法数量。

378. Kth Smallest Element in a Sorted Matrix

每行每列都是递增的整数矩阵,找到其中第K小的数。

 

380. Insert Delete GetRandom O(1)

实现RandomizedSet数据结构,使得插入,删除,随机读取都是O(1)时间复杂度。

382. Linked List Random Node

对单链表实现随机选取节点,各节点选中的概率相等,进阶:链表长度超大且未知,O(1)额外空间。

384. Shuffle an Array

为数组实现shufflereset功能。

385. Mini Parser

定义NestedInteger数据结构,它可以是单个整数,也可以是NestedInteger数组,由isInteger函数判断。由字符串表示的结构还原NestedInteger结构,例如"[123,[456,[789]]]"对应三层结构。

386. Lexicographical Number

给定正整数N,求1~N总共N个正整数的字典序排列,N可能接近5000000

388. Longest Absolute File Path

给定字符串表示某个文件夹中的目录和文件情况,包含\n\t,包含文件后缀的是普通文件,没有小数点的是文件夹,求其中文件对应到路径表示的最长长度,例如“dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext”中只有一个文件,路径是“dir/subdir2/file.ext”。

390. Elimination Game

给定数字N,相当于有1~N总共N个数字,第一次从前往后,去掉首个数字,隔一个去掉一个数字,第二次从后往前去掉数字,交替进行,求最后剩的数字。

392. Is Subsequence

判断字符串s是否是字符串t的子序列。

393. UTF-8 Validation

判断一串数字是否能解析成多个UTF-8字符的拼接。

394. Decode String

将压缩后的字符串还原,可嵌套,例如 "3[a2[c]]" 对应 "accaccacc"

395. Longest Substring with At Least K Repeating Characters

给定小写字符串S和整数K,在S中寻找子串,该子串中所有字母出现的次数都大于等于K,求这样的子串的最长长度。

396. Rotate Function

给定长度为N的数组,它有N个旋转数组,用0~N-1对应乘以旋转数组,求最大值。

397. Integer Replacement

给定一个正整数N,如果它是偶数,用N/2替换,如果它是奇数,可以用N+1N-1替换,求最小的替换次数,使得最后得到1

398. Random Pick Index

给定整数数组和目标整数,需要得到数组中等于目标整数的某个下标,如果有多个数则随机取一个。

399. Evaluate Division

给定一批式子,式子类似为字符串a和字符串b以及a/b的结果,求一批查询的结果,查询例如a/c的结果,如果之前有a/bb/c的结果,可以推出a/c的结果,查不到返回-1

402. Remove K Digits

给定数字字符串SK,可以删掉S中的K个数,删完后去掉前导零,求删除后得到的最小的数。

406. Queue Reconstruction by Height

给定一批数对(h, k),需要对它们重新排列,使得每个位置上的数对,h表示当前位置上的值,k表示它前面有几个数大于等于当前数。

413. Arithmetic Slices

在数组中找长度大于等于3的等差数列的数量。

416. Partition Equal Subset Sum

对非空只有正整数的数组,判断是否可以划分为两个部分,它们的和相等。

417. Pacific Atlantic Water Flow

给定数值二维数组表示高度,每个位置可以移动到高度更低的位置,查找可以移动到左边缘或上边缘,同时可以移动到右边缘和下边缘的位置。

419. Battleships in a Board

给定二维字符数组“X”和“.”,找1*11*NN*1的“X”的数量,两个合法的“X”集合不会相连。进阶:一轮,O(1)空间,不可修改原数组。

421. Maximum XOR of Two Numbers in an Array

O(N)求非负整数数组取两个数进行异或运算的最大值。

423. Reconstruct Original Digits from English

给定字母字符串,将其中的字母按照一定顺序排列,可以得到英文的“zero”,“one”,,“nine”,对应数字0~9,求升序排列的对应的数字字符串,输入长度小于50000

424. Longest Repeating Character Replacement

给一串大写字母和一个数字K,可用任意大写字母替换原串中某字母K次,求替换后最长相同字母子串长。

430. Flatten a Multilevel Doubly Linked List

Input:

 1---2---3---4---5---6--NULL

              |

             7---8---9---10--NULL

                    |

                  11--12--NULL

 

Output:

1-2-3-7-8-11-12-9-10-4-5-6-NULL

433. Minimum Genetic Mutation

给定ACGTDNA字符串,有开始值start和结束值end,还有合法变换的字符串数组bank,每次变换只能改变一个字符,并且必须从bank里面取,求startend的最小变换次数。

435. Non-overlapping Intervals

给定间隔数组,找到需要移除的最小间隔数,以使其余的间隔不重叠,必须交叉才算重叠,相邻不算。

436. Find Right Interval

给定间隔数组,没有起点相同的间隔,如果间隔ij不重合,并且整体在j的右边,则间隔i在间隔j的右边。需要对间隔数组中的每个间隔,找到它最近的右边的间隔的下标,如果没有则对应-1

442. Find All Duplicates in an Array

给定长度为N的整数数组,其中所有数都在1~N之间,有的数出现两次,其他的出现一次,求所有出现两次的数,O(N)时间,O(1)空间。

445. Add Two Numbers II

求以链表形式保存的两个数的和,以链表形式输出,进阶:不修改输入(不翻转链表)。

449. Serialize and Deserialize BST

BST设计序列化和反序列化方法。

450. Delete Node in a BST

删除BST的节点,O(树高)时间。

451. Sort Characters By Frequency

按照字符串中各个字母出现的次数由多到少,重新排列字符串。

452. Minimum Number of Arrows to Burst Balloons

给定间隔数组,对一个数字,如果某个间隔包含该数字,则被消去,求最少需要几个数字可以清空间隔数组。

454. 4Sum II

4个等长整数数组,在4个数组中各取一个数求和为0,求取法数量。

456. 132 Pattern

给定整数数组A,判断其中是否存在三个位置i<j<k,使得A[i]<A[k]<A[j]A的长度小于15000

457. Circular Array Loop

给定正负整数数组,正整数表示前进几格,负整数表示后退几格,数组头尾相连,如果从某个点出发,只向前或只向后走能回到原点,则说明有循环,判断是否有循环,ON)时间,O(1)空间。

462. Minimum Moves to Equal Array Elements II

对给定整数数组,每次可以修改一个元素加1或减1,求使得所有元素相等至少要修改几次。

464. Can I Win

给定整数SN,两个人玩游戏,每次可以选一个1~N的数,已经选过的数不能选,如果选完之后所有已经选的数的和大于等于S,则胜利,求先选的人是否有必胜策略,S<=300N<=20

467. Unique Substrings in Wraparound String

给定一个小写字符字符串,假设字符a和字符z相连,例如“abcde”,“def”,“yzabcd”都是目标字符串,求给定字符串中总共有多少个目标字符串子串,重复的不计数。

468. Validate IP Address

判断字符串是否是IPv4IPv6地址。

470. Implement Rand10() Using Rand7()

Rand7()随机获得1~7的函数,得到Rand10()随机获得1~10的函数。

473. Matchsticks to Square

给定正整数数组,表示多根一定长度的火柴棍,判断这些火柴棍是否可以恰好组成一个正方形。

474. Ones and Zeros

给定只包含01的字符串的数组,和整数mn,表示有m0n1,求最多可以组成多少个字符串。

一些LeetCode中等题做法(三)

记录一些LeetCode中等题解法。

148. Sort List

题目:O(NlogN)时间,O(1)空间对链表排序。

做法:思路是使用归并排序,并且在递归中使用常数空间,即使运行过程中实际记录的空间不是常数,也算O(1)空间。在将两个排序链表合并成一个时,可以使用一个临时节点简化代码,再用一个指针指向其尾部,两个排序链表头部的较小值连接在临时链表的尾部,然后移动排序链表的头部,最后把剩余的部分接在最后。在将当前链表拆分成两个链表时,可以使用两个指针指向链表头部,一个递增1,一个递增2,当递增2的指针或它的next为空时停止,这时递增1的指针就在链表的中间,截断就可以将原链表分成等长的两部分。

221. Maximal Square

题目:给定包含数字01的二维数组,找到其中最大的由1构成的正方形。

做法:比较明显的方式是对每个位置记录从矩形左上角到这个位置的所有1的数量,然后对每个值为1的位置,查看各个长度的正方形区域内的1的数量是否填满该正方形,如果已经找到某个符合条件的长度,则下次要递增。可以找到一个更好的方法,使用动态规划,第一行和第一列中值为1的点dp[i][j]=1,其余点如果值为1dp[i][j]=min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])+1dp数组的值表示,从该点开始往左上角看,最大的正方形的边长,如果当前点(i, j)值为1,并且它左边,左上,上边的三个点都可以找到某个边长为L的正方形,则恰好已经填满了从(i, j)往左上角看的L+1长的正方形。如果这三者某个小于L,分别会在长度为L+1的正方形的左下角,左上角,右上角缺某些格子。

229. Majority Element II

题目:找到数组中出现次数大于N/3的数,O(1)空间。

做法:需要使用两个变量mn记录出现次数最多的两个数,变量cmcn记录这两个变量当前出现的剩余次数,当发现三个互不相等的数时,这个剩余次数要减小。遍历数组,对当前数x,如果x等于已经记录的mn,则增加对应的cmcn,如果mn都已经填了数字,而x不等于mn,则cmcn同时减1,如果mn有空位,而x不等于另一个,则把x填到空位,增加对应cmcn,如果mn都是空的,则填到m里。

236. Lowest Common Ancestor of a Binary Tree

题目:给定二叉树,其中各值唯一,求两个节点的最低公共祖先。

做法:使用某种方式遍历树,找到根到这两个节点的路径,然后用两个指针指向路径头部,递增可以找到最低公共祖先。也可以用递归等方式,递归终止条件是当前节点为空,或当前节点等于要找的两个节点之一。在当前节点的左右分别寻找,如果左右子树之一包含了要找的两个节点,则该子树的根就是最低公共祖先,如果左右子树都没有同时包含两个节点,则它们分布在左右子树,应该返回当前节点。

238. Product of Array Except Self

题目:给定一个数组,对每个位置求除了该位置的所有数的乘积,不能用除法,并且在O(N)时间完成,进阶:O(1)空间,输出数组不算。

做法:数组为A,输出数组为B,临时变量初始为1,第一轮运算,数组B依次为1A[0]A[0]*A[1]A[0]*A[1]*…*A[n-1],可以发现得到的数组B与自己的逆序相乘,得到的就是输出,所以第二轮运算,重置临时变量,逆序乘一遍就得到结果。

241. Different Ways to Add Parentheses

题目:给定一个包含加减乘的表达式字符串,求所有可能的加括号方式对应的运算结果。

做法:首先把表达式字符串拆成多个数字或运算符号元素,递归计算当前元素数组,遍历当前元素数组的所有运算符号,拆分元素数组,拆分的左边或右边返回该部分所有可能的输出,合并时两两加减乘作为当前元素数组的输出。

274. H-Index

题目:给定一个研究者的N篇论文的引用数,求该研究者的最大h-index(如果有h篇文章的引用数至少为hN-h篇的引用数小于等于h)。

做法:对该研究者,如果某篇论文的引用数大于N,将该论文引用数改为N,不会影响h-index,用一个数组A记录引用数是0…N的数量。如果A[N]>=N,则h-indexN,如果A[N-1]+A[N]>=N-1,则h-indexN-1,所以从N0,对比与数组A后缀和的大小关系,可以找到h-index

275. H-Index II

题目:给定一个研究者的N篇论文的引用数,按升序排列,求该研究者的最大h-index(如果有h篇文章的引用数至少为hN-h篇的引用数小于h),进阶:O(logN)时间。

做法:考虑left=0right=N-1,它们的中点mid,引用数数组为A,有N-mid篇论文的引用数大于等于A[mid],对于其它的leftrightmid也满足。想要找的是这样一个hA[h]>=N-h,但是A[h-1]<N-(h-1),如果A[mid]>=N-mid,则h<=mid,否则h>mid。为了便于理解,可以在二分的时候分为[left,mid][mid+1,right]两个区间,暂时不对mid-1做处理,最后终止条件时要额外判断right-left等于1的情况。在二分的时候,如果想直接写right=mid-1left=mid+1,表面上可能会导致leftright之间没有要找的值,while(left<=right)。这里要分几种情况,对于某个较大的区间[left,right]和它的mid,如果要找的值h恰好是mid,判断之后发现mid满足条件,应该在区间[left,mid-1]内找,那在找的过程中,每次都要执行left=mid+1,因为里面没有h,最后总会达到left=mid-1的情况,这时由于left==right,所以还要再加一次,left恰好又对应上了实际正确的值。如果对于某个一开始就是left==right的区间,它的mid满足条件,则只是right=mid-1left依然对应的是正确的位置。

对应找到的代码来看,第一个判断表示对于所有满足条件的midright一定会在它的左边一格,那在最后输出的时候,right就是满足条件的最小mid1left是不满足条件的最大的mid1

class Solution {
public:
    int hIndex(vector<int>& citations) {
        int left=0, len = citations.size(), right= len-1,  mid;
        while(left<=right)
        {
            mid=left+ (right-left)/2;
            if(citations[mid] >= (len-mid)) right = mid - 1;
            else left = mid + 1;
        }
        return len - left;
    }
};

289. Game of Life

题目:给定值为01的二维数组,表示活细胞和死细胞,根据周围细胞的情况,两者可以转换,求转换后的结果,进阶:原地转换,边界。

做法:原地转换的关键在于,对每个值增加一个位表示下一个状态的值,可以用位运算得到某个位置的老状态和新状态,处理结束后用位移运算更新状态。

310. Minimum Height Trees

题目:给定N个点,N-1条边的可转换为树的图,求以哪些点作为根可以得到最小高度的树。

做法:找到所有叶节点删除,剩下的图中的叶节点再删除,直到剩下的点小于等于两个时输出。由于边相对来说较少,所以用邻接链表来存边,并记录每个节点相邻边的数量。首先找到相临边数量为1的点Ps,遍历这些点的相邻点Ns,减小它们的度1,如果度等于1,则加入到待探索列表中。对于每一轮寻找叶节点,有两种方式,一种是在邻接链表(或hashmap)中删除上一轮找到的边,然后找度为1的节点。一种是不对邻接链表进行删除,每轮准确维护没有当过叶节点的点的度,对于当前轮,假设所有点的度都是准确的。考虑每个当前待探索列表中的每个点,把这个点的所有邻居的点的度减1,其中分为两种情况,一种是它们上一轮的度是1,由它们使得当前节点被放入待探索列表,减1后就是0,相当于它们被删除了,还有一种是还没有被放入探索列表的节点,它们的度减1相当于删除了当前这个叶节点使得它们的度减小了,经过这一过程,度为1的点就是下一轮的叶节点。

318. Maximum Product of Word Lengths

题目:给定小写字母字符串数组,在其中挑两个没有重复字符的字符串,求可能的最大长度乘积。

做法:遍历字符数组,将每个字符串转换为最多26位的整数,每一位对应一个小写字母,双重循环遍历生成的整数数组,可以用位运算判断两个字符串是否有重复字母。

322. Coin Change

题目:给定一些面额的硬币无限量和想要凑的值,求需要的最少硬币数量。

做法:一维动态规划,dp[0]=0,表示凑0需要0个硬币,从1开始考虑直到需要凑的硬币面值amount,对应变量i,遍历所有硬币j,如果该硬币面额小于等于当前考虑的面额idp[i]=min(dp[i], dp[i-coins[j]] + 1),最后dp[amount]就是需要的硬币数。

382. Linked List Random Node

题目:对单链表实现随机选取节点,各节点选中的概率相等,进阶:链表长度超大且未知,O(1)额外空间。

做法:遍历单链表,记录当前已经遍历的数量count,用一个变量out记录产生的随机输出,每次随机生成一个0~count-1的数,如果它为0,则修改out为当前遍历到的节点,也就是有1/count的概率修改out。遍历第一个点,必然修改out,遍历第二个点,有1/2的概率修改,则两个点作为输出的概率相等,遍历第三个点,有1/3的概率修改,则1/2*2/3=1/3的概率为第一或第二个点,1/2*1/3+1/2*1/3=1/3的概率为第三个点,第N-1个点时,各个点的概率均为1/(N-1),第N个点时,有1/N的概率修改为第N个点,前面每个点出现的概率为1/(N-1)*(N-1)/N=1/N,修改为第N个点的概率为1/(N-1)*N*(N-1)=1/N

390. Elimination Game

题目:给定数字N,相当于有1~N总共N个数字,第一次从前往后,去掉首个数字,隔一个去掉一个数字,第二次从后往前去掉数字,交替进行,求最后剩的数字。

做法:对于2N+12N,第一轮去掉数字后剩的结果是一样的,f(2N+1)=f(2N)f(N)表示从1~N的数字,按照条件去掉数字后,剩余的数字。考虑123456->246->4,思考f(2N)f(N)的对应关系,f(2N)去掉一轮数之后,剩余的数是2*(1…N),接下来是要从后面开始去掉数字。考虑从N1去掉数字与从1N去掉数字的差别,将对应位置上的数X替换为N+1-X,则数组变为N…1,这时是先从后面开始去掉,所以最后剩下的数就是f(N),假设它是从前往后数第M个位置上的数。所以如果不对1…N进行替换,最后剩的也是第M个位置上的数,而两次替换中,同一个位置上的数和为N+1,所以1…N从后面开始去掉数字,最后剩的数是N+1-f(N),可以得到式子f(2N)=2*(N+1-f(N))

392. Is Subsequence

题目:判断字符串s是否是字符串t的子序列。

做法:两个指针指向st的头,如果两者相等,同时递增1,如果不等,仅t递增1,如果s的指针超过末尾,则返回true,否则返回false

436. Find Right Interval

题目:给定间隔数组,没有起点相同的间隔,如果间隔ij不重合,并且整体在j的右边,则间隔i在间隔j的右边。需要对间隔数组中的每个间隔,找到它最近的右边的间隔的下标,如果没有则对应-1

做法:由于各个间隔的起点不同,并且最后需要输出的是下标,所以需要建立间隔起点到下标的对应关系,可以使用map,也可以自定义结构,需要按照起点递增进行排序。对每个间隔,考虑它的末尾end,需要找到第一个大于end的间隔起点,也就是lower_bound,如果没找到,则对应-1,如果找到了则对应下标。

442. Find All Duplicates in an Array

题目:给定长度为N的正整数数组,其中所有数都在1~N之间,有的数出现两次,其他的出现一次,求所有出现两次的数,O(N)时间,O(1)空间。

做法:可以找到两个方法,一个是利用数组下标是0~N-1,值是1~N,遍历数组,如果当前位置P的数X与位置X-1的数不同,则交换这两个数的位置,使得位置X-1上的数是X,如果数组中没有重复的数,最后每个位置上的数都是下标加1。如果相同,实际上找到了一个出现两次的数X,继续遍历下一个数,这个位置之后有可能会被其他数换走,但是位置X-1上的数已经是X,不可能再被换。最后如果某个位置的值不等于下标加1,说明它没办法填充进想去的位置,是出现了两次的数。第二个方法是,由于所有的值都是正数,可以把它们转换为负数记录信息,每次取数的时候要用绝对值。位置与值是一一对应的,在遍历数组时,把当前值对应的下标上的数设置为负值,并且对每个当前值,需要查看对应下标的数是否已经是负值,如果是负值,说明它出现了两次。

445. Add Two Numbers II

题目:求以链表形式保存的两个数的和,以链表形式输出,进阶:不修改输入(不翻转链表)。

做法:可以用两个栈将链表存进去,然后依次计算,新的值插入链表头。可以找到一个不用栈的方法,就是第一次计算时,直接将对应位置的数相加,不进位,存链表尾部,第二次依次计算的时候,相当于从栈里获取两个数相加,插入链表头。但是这两个方法还是相当于从链表的尾部开始计算,和翻转链表很像,可以找到一个从前往后思考的方法。为了处理进位问题,首先使用一个值为0的节点dummy作为开头,并维护链表的尾部tail,增加一个指针,指向最后一个不是9的数lastnot9。根据链表L1L2的长度区别,将较长的部分全部连接在dummy后面,维护taillastnot9,当剩余部分两者长度相等时,进行后续的计算。每次相加的结果,如果小于9,则直接链接在tail后面,并维护lastnot9,如果等于9,只要链接在tail后面,如果大于等于10,则lastnot9位置上的数要加1,并且lastnot9与当前位置中间都是9,需要全部设置为0,将lastnot9维护在最后一个0的位置,如果中间没有9,则lastnot9位置暂时不变,当前数减去10之后得到的数要链接在tail后面,对于当前两数和减去10之后,剩下的数最大是8,所以lastnot9还需要移动到当前位置。最后如果dummy0,则输出它的next,否则直接输出dummy

464. Can I Win

题目:给定整数SN,两个人玩游戏,每次可以选一个1~N的数,已经选过的数不能选,如果选完之后所有已经选的数的和大于等于S,则胜利,求先选的人是否有必胜策略,S<=300N<=20

做法:可以使用DFS,需要利用N<=20,将每次已经选择的数用位运算转换为一个整数,已经算过的就不用算了。如果剩下的数有大于等于还缺的数时,则返回true,否则尝试选取每一个剩下的数,可选的数都无法返回true,则返回false

467. Unique Substrings in Wraparound String

题目:给定一个小写字符字符串,假设字符a和字符z相连,例如“abcde”,“def”,“yzabcd”都是目标字符串,求给定字符串中总共有多少个目标字符串子串,重复的不计数。

做法:我想到的方法是,对每个字符,记录由它结束的最长递增子串长度,最后把26个字符的长度加起来就是结果。想出来的过程是写了两串,假设只有“abcde5种字母,写“abcdeabcd”和“bcdeabcdeabcd”。如果某个字符与前一个字符不构成递增,则当前长度要刷新,如果构成递增,则修改当前长度,根据当前长度和当前字符之前记录的最长长度的大小关系,判断是否修改长度数组。

470. Implement Rand10() Using Rand7()

题目:由Rand7()随机获得1~7的函数,得到Rand10()随机获得1~10的函数。

做法:常见的方法是,执行两次Rand7()得到ab,计算(a-1)*7+(b-1),如果结果r小于40,则返回r/4+1,如果当r大于等于40时,重新计算。如果当r大于等于40时,不是直接丢弃,而是利用48与它的差值,可以得到一个0~8的数c,与一个新的rand7()=d,计算(c-1)*7+(d-1),范围是0~62,然后得到0~2的数e,下一次的范围是0~20,只有20需要排除,概率较低,可以减少调用Rand7()次数的期望。在讨论里有人提到了一个平均调用次数期望很低的方法,连续调用Rand7()很多次,每次调用的结果减1再乘以7的次幂求和,就能得到一个很大的数,由这个数截断最后的一位,就能得到很多个Rand10()的结果,取完了再重新计算一批。

473. Matchsticks to Square

题目:给定正整数数组,表示多根一定长度的火柴棍,判断这些火柴棍是否可以恰好组成一个正方形,火柴棍数量不超过15

做法:我想到的方法是暴力DFS,先对数组倒序排序,做了一些后发现不能每次凑Sum/4,要一起凑完才知道能不能组成正方形,实际需要凑的是3Sum/4。记录哪些火柴棍已经用了,以及每份还缺多少。用了一些剪枝,一是如果当前还剩的最短火柴棍都超过了与Sum/4的距离,则返回false,二是得按照3份的顺序来凑,如果前一个没凑满,就不能凑下一个。看了答案发现有更简短的DFS方法,还有一个DP方法。DFS方法是对每根火柴棍,考虑放到4堆里面,放完如果4堆相等就可以组成正方形,当时我算了一下,415很大,估计时间会超就没用。刚刚用了答案里面的Java代码,执行时间114ms,我的DFS16ms,不确定具体的差别。DP方法是利用最多15根火柴棍,使用位运算将每根火柴棍是否选取对应一个整数,火柴棍选取情况和当前选取情况已经凑了几条边作为key,该key是否成立作为value,建立hashmap进行持久化记录。递归的过程对每个可选的火柴棍进行尝试,还需要记录的值是当前已使用边的总和,根据这个就可以调整是否新凑完一条边。在对可选的火柴棍尝试时,如果当前选择的边大于当前在尝试凑的边的剩余值,则不能选择它进行递归,实际上每次选择边都是指定填充确定位置的边。

474. Ones and Zeros

题目:给定只包含01的字符串的数组,和整数mn,表示有m0n1,求最多可以组成多少个字符串。

做法:遍历字符串数组,对每个字符串计算其中有zeros0ones1,如果zeros>mones>n则跳过,接下来有两种相似的DP方法,都是用大小为(m+1)*(n+1)的二维数组记录各种mn可以组成字符串的数量。一是先复制一份dp数组,jk遍历0…m0…n,如果dp[j][k]>0,并且j+zeros<=m(k+ones)<=n,则dp[j+zeros][k+ones]=max(dp[j+zeros][k+ones], dp[j][k]+1),遍历完之后,dp[zeros][ones]=1,把新生成的dp赋值给原dp数组。如果不复制一份,使用该方法时后面的数会受前面的数的值的变化影响,得出错误结果。第二个方法是倒序遍历,和上面相对应就是jk遍历m…zerosn…ones,其中的j+zerosk+ones要改成减号,这样就不用复制一份。

一些LeetCode中等题做法(二)

记录一些LeetCode中等题解法,少量分析。

116. Populating Next Right Pointers in Each Node I

题目:为二叉树实现记录next节点(层次遍历的下一个节点)。perfect binary tree (ie, all leaves are at the same level, and every parent has two children).

做法:外层循环遍历每一层的最左节点,除了最后一层,对于每一个最左节点,由于不是最后一层,所以肯定有左节点和右节点。首先将该节点P的左节点的next指向右节点,如果每层处理结束后,该层下面一层所有的next节点都维护完毕(在初始遍历根节点时,只需要根节点的左节点的next指向右节点就维护完毕)。对于当前层的每一个节点P,如果它有nextP->right->next=P->next->left,这时此节点P的左右孩子的next都已经记录,然后由于此层的next已经记录,需要将当前节点P移动至next节点,继续进行即可。最后当前层遍历结束,就维护了下一层的next节点。

117. Populating Next Right Pointers in Each Node II

题目:为二叉树实现记录next节点(层次遍历的下一个节点),允许节点的左右节点不完整。

做法:外层循环需要从每一层的第一个节点开始,首先假设在遍历当前层的时候,当前层的next已经维护好,在遍历过程中,维护好下一层的next节点。每层的第一个节点需要在维护下一层next节点时,对于第一个非空的左节点或右节点进行记录。由于该节点最后需要返回,作为下一层的开始,但是该节点的next需要不断地链接在最后一个next后面,所以要用两个指针,一个记录链表头,一个记录链表尾,不断在链表尾插入新来的next节点。

173. Binary Search Tree Iterator

题目:O(1)时间,O(树高)空间,对BST设计迭代器next函数和hasNext函数。

做法:维护一个栈,栈顶是当前的最小值,如果栈为空,则没有next,初始情况的最小值是从根节点开始,找到最左节点,栈中记录了从根节点到该节点的路径。每次求next,需要弹出栈顶的元素作为返回值,然后需要维护栈,要用临时变量指向弹出的元素,进行一些操作。栈顶元素是没有左节点的(或者所有左节点都已经弹出),如果该元素没有右节点,则弹出该元素后,栈顶的左子树已经全部弹出,栈顶就是当前的最小值。如果该元素有右节点,则首先需要将右节点入栈,然后将这个右节点的所有左节点依次入栈,最后栈顶是当前的最小值。

192. Word Frequency

题目:使用Bash统计文本词频,按词频大小逆序分行输出词语和词频。

做法:

cat words.txt | tr -s ' ' '\n' | sort | uniq -c | sort -nr | awk '{ print $2, $1 }'

| 管道命令,将前一个操作的输出用于后一个操作的输入。

cat words.txt 显示文件内容

tr -s ' ' '\n' tr命令是字符替换命令,tr SET1 SET2表示在SET1中的字符被替换为SET2对应位置上的字符,-s表示连续的SET1中的字符缩减为1个。于是该命令表示将连续空格替换为’\n’

sort 按行排序,sort -r 倒序,sort -nr 按数字倒序。

uniq 去除相邻重复行,uniq -c 去除相邻重复行并且计数,数量在前,行内容在后。

awk '{ print $2, $1 }' 对每行的元素,先输出第二个,再输出第一个。常用格式 awk ”模式awk {操作}awk “模式” {操作},输入文件每行作为一个元素,如果指定模式或操作,需要对匹配模式的行进行操作,默认操作是输出,默认样式是匹配所有字符串。$1对应分隔符划分的第一个元素,$2是第二个,分隔符默认是空格和制表符。如果没有用管道,末尾要加上操作哪个文件。

194. Transpose File

题目:使用Bash对一个多行由空格隔开的文本矩阵进行转置。

做法:

awk '
{
    for (i = 1; i <= NF; i++) {
        if(NR == 1) {
            s[i] = $i;
        } else {
            s[i] = s[i] " " $i;
        }
    }
}
END {
    for (i = 1; s[i] != ""; i++) {
        print s[i];
    }
}' file.txt

awk '{操作}END{后操作}' 文件名,先执行前面的操作,这里的操作是以行为单位,$1$2等对应的是行内的第一第二个元素,然后执行END内操作。NF是每行的元素个数,NR是当前累计的行数。所以该代码对第一行的每个元素,建立对应位置的数组,对后面的行的每个元素,找到相应的数组,加个空格再记录当前元素,得到的数组顺序输出就是原内容的转置。

142. Linked List Cycle II

题目:找到链表中的环入口,没环输出NULL,不能修改链表,进阶:O(1)空间。

做法:用map保存节点可以在O(n)空间完成,可以找到O(1)空间的解法。

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *slow = head, *fast = head;
        while (fast && fast->next) {
            slow = slow->next;
            fast = fast->next->next;
            if (slow == fast) break;
        }
        if (!fast || !fast->next) return NULL;
        slow = head;
        while (slow != fast) {
            slow = slow->next;
            fast = fast->next;
        }
        return slow;
    }
};

x为链表入口到循环入口的距离,y为循环的长度,i为使得slowfast相遇的在0y-1范围内的值。slow走过的长度为x+ay+ifast走过的长度为x+by+i,有2(x+ay+i)=x+by+i,可得x+i=(b-2a)y,将x转换为模y的形式可以发现必然有一个a=0的解,则时间复杂度是O(N)。现在对比从链表头移动x次和fast移动x次,x+by+i+x=x+by+(i+x)=x+by+(b-2a)y=x+2(b-a)y,和链表头移动x次是相等的,并且位于循环的入口。

287. Find the Duplicate Number

题目:O(1)空间,小于O(N2)时间,不修改数组,在长度为N+1的只包含1N的数组中,寻找唯一的出现次数大于1的数,其他数只出现一次。

做法:我想到的办法是O(NlogN)的,二分查找leftright中间值mid,统计数组中位于区间[left,mid](mid,right]的数的数量LR,如果L<=Rleft=mid+1,否则right=mid。这题可以找到一个更优的O(N)的解法。

class Solution {
    public int findDuplicate(int[] nums) {
        // Find the intersection point of the two runners.
        int tortoise = nums[0];
        int hare = nums[0];
        do {
            tortoise = nums[tortoise];
            hare = nums[nums[hare]];
        } while (tortoise != hare);
        // Find the "entrance" to the cycle.
        int ptr1 = nums[0];
        int ptr2 = tortoise;
        while (ptr1 != ptr2) {
            ptr1 = nums[ptr1];
            ptr2 = nums[ptr2];
        }
        return ptr1;
    }
}

这题的做法和142. Linked List Cycle II的做法很像,用一个递增1的和一个递增2的指针找到它们相等的位置,然后把递增1的指针重置,然后和递增2的指针位置一起递增1,直到相等。这题考虑数组的下标与数组的值对,(i,v)表示节点i到节点v有一条边,下标范围是[0,N],值范围是[1,N],需要证明从0出发的链表一定有环。假设没有环,从0出发前进N+1次,每次得到的都得是不同的节点,而可选的边的末尾只有N个可能的值,矛盾,所以从0出发的链表一定有环。使用142题的方法可以找到环的入口,环的入口意味着有两个下标都能得到同一个值,也就是找到了数组中的重复值。

162. Find Peak Element

题目:对数时间,给定相邻数组值不等的数组,寻找任意一个大于左右邻居的数,数组边缘默认大于越界邻居。

做法:整体思路是二分不断获得更小区间,在递归或使用头尾指针二分时,对于一段位于数组内部的区间(不包含原数组头和尾),如果该区间中的元素构成递增数列或递减数列,则无法在该区间内找到所需的数,所以设计的方法要避免这种情况。另外,如果一段区间的头和尾都大于越界邻居时,就算该区间内部元素构成递增或递减数列,总可以在头或尾找到一个符合条件的数。首先考察数组的头尾,由于位于头尾的数默认是大于越界邻居的,于是在初始时限定了不是普通的递增或递减数列,并且满足头部递增,尾部递减。考察区间中间的元素,如果它大于该元素左侧一格的元素,则取右半边的区域,该区域满足头部递增(大于越界邻居),尾部递减,如果它小于左侧一格的元素,则取左半边区域,同样满足头部递增尾部递减,当区间只剩一个数时,它大于左侧和右侧越界邻居,则可以输出对应索引。

264. Ugly Nubmer II

题目:丑数是1和因子只含235的正整数,求第N个丑数。

做法:根据已有的丑数数组,求下一个丑数,需要把所有的丑数都乘以235,然后找到其中最小的数,这个数就是下一个丑数,依次计算就可以得到第N个丑数。但是如果所有丑数都要乘以235,效率太低,考虑当前已有丑数的最大值M,已有丑数中乘以2仍然小于等于M的不需要再考虑了,只有乘以2后大于M的第一个数是需要考虑的,35也同样。所以需要维护的是三个下标abc,分别表示乘以235大于当前丑数数组最大值的三个数的下标,当前丑数数组初始值为一个数字1。每次遍历需要考虑下标为abc的数乘以235的最小值,取为下一个丑数,如果这个新的丑数等于下标为a的数乘以2,则a需要递增,对bc也同样进行考察是否需要递增。

313. Super Ugly Number

题目:给定一个质数数组,超级丑数是1和能因子只在该数组中的数的正整数,求第N个超级丑数。

做法:与因子为235的丑数类似,为给定质数数组中的每个质数设置一个下标变量,遍历时考虑多个值。

307. Range Sum Query - Mutable

题目:整数数组单点修改,求指定区间和,线段树(树状数组)。

做法:明显的办法是O(1)修改,O(N)求和,可以找到一个稍微好一点的办法,是将原数组划分为sqrt(N-1)+1个sqrt(N)大小的区间,预处理O(N),更新时对受影响的一个区间更新O(1),区间和对包含的完整区间直接求和,不完整区间单个计算O(sqrt(N))

更好的办法是用线段树,将原数组划分为长度为NN/221的区间,每个区间记录区间和,修改时对根到叶节点的路径上的区间进行修改,区间求和需要将指定区间划分为已经记录和的多个区间求和。

线段树使用数组记录树形结构,每一个数组元素根据下标可以算出它在树形结构中的位置,数组元素表示对应区间内所有数的和。这里讨论的是递归形式的线段树,对于数组长度为N的数组,树的划分是固定的,接下来计算空间复杂度。假设给定的数组元素个数为2n,对应的线段树是一个完整的二叉树,每层节点数量分别为20212n,每层节点对应的区间长度是2n 2n-120,树总共有n+1层。对应树形结构,根节点下标为0,它的孩子下标为2*0+12*0+2(还有另一种方法是根节点是1,孩子下标2*12*1+1,浪费一个空间看着顺眼点,并且在计算的时候用位运算可以加速),下标为i的节点的孩子坐标为2*i+1,和2*i+2,总共能延伸n次,每次延伸获得的两个节点下标最大是2*i+2,令f(0)=0f(n)=f(n-1)*2+2,可得f(n)+2=2*(f(n-1)+2),则f(n)=2n+1-2,每层节点编号的最大值是022n+1-2。对于一个2n长度的数组,需要使用的空间是2n+1-1(要加上0),并且当数组长度在(2n-1,2n]内时,从根节点到叶节点在最坏情况下需要探索n次(探索n-1次最多获得2n-1个数),所以需要的空间倍数小于4(2n+1-1)/( 2n-1+1)),当考虑数组长度是N时,对应的空间复杂度是4N

现在稍微考虑一下极限空间是多少(如果是代码的话,建树的代码记录叶节点的线段树下标最大值就可以得到),在划分各个节点时,左右子树的节点数量差最大是1,所以左右子树高度差最大是1,需要考虑的是对应树形结构最后一层中最大的下标编号。原数组元素数量与所需空间的部分对应关系如下所示,2n-1+1->2n+12n-1+2->2n+2n-1+12n-1+2n-1->2n+2n-1,根据数组建树,然后根据递推关系找到最大的线段树节点下标加1就是所需的空间。原数组大小从2n-1开始,增加1,空间是2n+1,增加2,空间是2n+2n-1+1,增加4,空间再加2n-2,所以增加2m时,空间在上一次增加的基础上增加2n-m。需要注意的地方是,原数组大小在增加的过程中,对线段树的最下层的影响是,左边加一个,右边再加一个,交替进行,只有当增加到一定程度,才会影响最后的消耗空间,还有线段树的节点是占两个位置,在考虑的时候要取第二个位置,并且在计算的下标最大值的基础上加1才是消耗的空间。大概就是[2n-1+2m, 2n-1+2m+1)对应2n+2n-1+…+2n-m+1,也就是2n+1-2n-m+1,在边界m=n-1时,对应2n+1-2+1,也是成立的

按照这个式子,可以得到一个O(loglogN)时间,O(logN)空间的计算方法,大小为10的数组,最多可以计算原数组大小为256的情况。

int z[10] = {1,2,4,8,16,32,64,128,256,512};
int get(int a){
         int n=0, m=-1;
         n = lower_bound(z,z+10,a)-z;
         if(n>0){
                   int b = a-z[n-1];
                   m = upper_bound(z,z+10,b)-z-1;
         }
         return z[n+1] - z[n-m] + 1;
}

线段树中叶节点的数量与原数组的大小相等,在单点更新时,从根节点到对应叶节点的路径上的所有区间都要更新。从线段树的根出发,根据要更新的点的索引和当前区间的中点的关系,探索相应的节点,递归栈中有从根节点到相应叶节点的所有节点(线段树数组中对应的位置),先修改叶节点,然后逐个修改递归栈中的节点。

在区间查询时,从线段树的根节点出发,根节点表示整个原数组,每次将当前节点分成两份,对应线段树中的左右节点。在划分时,如果当前节点与要查询的区间没有交集,则返回0,如果有交集,只有在当前节点完全被要查询的区间包围时,返回节点对应的区间和。递归的过程会尝试探索线段树所有的节点,在超出范围的地方剪枝返回0,在恰好包含的地方剪枝返回区间和。为了便于理解,可以将线段树区间[begin,end]划分点S与要查找的区间[L,R]的关系分为两种情况,如果S位于LR两端,新的两个线段树节点有一个超出范围被剪枝,线段树查询的范围缩小了一半,如果S位于LR中间,在下一轮操作时,相当于在两个更小的线段树区间[begin,S][S+1,end]内,查找两个更小的查询区间[L,S][S+1,R],这样就不是在包含的时候输出,而是在精确对应的时候输出。证明时间复杂度是O(logN)较为复杂,可以参考https://www.cnblogs.com/AC-King/p/7789013.html。线段树经过修改可以实现O(logN)区间修改,之后如果有机会理解再做介绍,另外,对于这类问题,其中一部分可以使用树状数组记录前缀和,代码相对好写。

337. House Robber III

题目:正整数二叉树型结构,不能取相邻节点,取若干点求和,求所能取到的最大值。

做法:树形动态规划的基础题,从根节点开始,遍历整棵树,根据状态转移方程,由节点的子节点的信息得到父节点的信息。假设每个节点只记录一个信息,这个信息可以是选取该节点的子树的最大和,也可以是不选取该节点的子树的最大和,也可以是前两者的最大和,可以发现这三种信息都不足以由子节点推出父节点的信息。假设记录两个信息,不选取该节点的子树的最大和Sno是必须有的,另一个信息考虑以该节点为根的子树最大和SmaxSmax[root] = Sno[left] + Sno[right] + Val[root]Sno[root] = Smax[left] + Smax[right]Smax[root] = max(Smax[root], Sno[root])。使用深度优先搜索可以遍历所有节点,得到根节点的Smax,就是所能取到的最大值。

215. Kth Lagest Element in an Array

题目:求整数数组中第K大的数。

做法:维护一个大小为K的最小堆,保证堆里面是遍历过程中最大的K个数,遍历整数数组,如果堆的元素数量小于K,则直接插入,否则对比当前数与堆顶的大小,如果当前数大于堆顶,说明堆顶是当前已知的第K+1大的数,需要弹出,然后插入当前数,最后堆顶就是第K大的数。

347. Top K Frequent Elements

题目:找到整数数组中出现频率最高的K个数。

做法:与求第K大的数类似,需要维护一个大小为K的最小堆。建立一个结构或者类,包含数值与出现次数,遍历记录各个数值的出现次数。自定义compare函数,默认priority_queue是最大堆,定义a.m_count > b.m_count可以得到最小堆。

378. Kth Smallest Element in a Sorted Matrix

题目:每行每列都是递增的整数矩阵,找到其中第K小的数。

做法:比较明显的方法是用一个数组记录当前每一行已经探索了几个数,每次探索一个最小值,直到找到第K小的数。可以找到一个更好的方法,回顾一下行列递增矩阵的查找,初始选取整个矩阵,考虑要找的数N与当前矩阵右上角的数M的大小关系,如果N>M则第一行被排除了,这意味着去掉的部分有当前矩阵列数的数比要找的数小,如果N<M则倒数第一列被排除了,去掉的部分有当前矩阵行数的数比要找的数大。根据这个方法,选定一个数,在N>=MN<M时都进行行列的删除,同时记录总共有多少个数小于选定的数。用二分每次在二维数组中找一个数,得到有几个数小于该数,对比与K的关系,修改要查找的数。

一些LeetCode中等题做法(一)

记录一些LeetCode中等题解法。

15. 3Sum

题目:给定一个整数数组,求在数组中取三个数相加和为0的所有非重复元组。

做法:遍历数组建立map,使得查找某个值是否在数组中的操作变为O(1),双重循环遍历数对,计算0减去这两个数得到M, 在map中查找M,如果找到则得到一个三元组,在插入结果数组之前,排序后用map去重。这个方法比较明显但是相对较慢,可以找到一个更好的解法。首先对数组排序,依次遍历各个数,相当于在剩下的数中选取两个指定和的数,用两个指针指向头和尾,根据两个指针指向的数之和与目标和的大小关系,移动指针寻找和为指定值的两个数,如果找到了就记录一下,然后把头尾指针移到和当前值不等的数上,在头尾重合的时候终止。遍历完第一个数后,要跳过和第一个数相等的数,这样最后就没有重复的三元组。

16. 3Sum Closest

题目:给定一个整数数组和目标整数,求在数组中取三个数相加离目标整数最近的整数。

做法:对数组排序,遍历数组中的除了后两个数以外的数,在剩下的数中记录头尾指针,如果对应的三个数的和比已经记录的最接近目标值的数还要接近目标值,则更新已经记录的值,根据和与目标值的大小关系移动头尾指针,大于目标值则尾指针减一,小于目标值则头指针加一。

18. 4Sum

题目:给定一个整数数组和目标整数,求所有和为目标整数的非重复四元组。

做法:对数组排序,双重循环遍历数对,根据情况移动保证前两个数组成的元组不重复,如果加上剩下的数中最大的两个依然小于目标值,或者加上最小的两个都大于目标值则跳过,接着就按照正常的两数和用头尾指针移动并去重,最后得到的是不重复的四元组。

454. 4Sum II

题目:4个等长整数数组,在4个数组中各取一个数求和为0,求取法数量。

做法:用map记录前两个数组中数对的和,key为和,value为数量,遍历后两个数组中的数对c和d,在前面记录的map中寻找0-c-d,如果找到了就增加对应数量的取法。

39. Combination Sum I

题目:给定一个整数集合和目标整数,集合中的数可以多次使用,求所有和为目标整数的非重复元组。

做法:对数组排序,递归调用,参数包含原整数数组,当前已经使用的数构成的数组,与目标整数的差值,遍历数组的起始位置。如果与目标整数的差值为0,将当前数组加入结果数组,否则遍历原整数数组,如果当前数小于等于当前目标值,将当前数加入已用数组,递归调用,然后把当前数弹出。

40. Combination Sum II

题目:给定一个整数数组和目标整数,数组中的数只能使用一次,求所有和为目标整数的非重复元组。

做法:与前一道题的差别在于当前遍历的数不能和上一个数相同,并且递归时遍历数组的起始位置要递增1。

74. Search a 2D Matrix

题目:每行递增排序的二维数组,并且每一行的开头大于前一行的末尾,查找是否包含目标值。

做法:相当于一个排序数组的二分查找,需要对一维下标和二维下标转化。

240. Search a 2D Matrix II

题目:每行每列都是递增排序的二维数组,查找是否包含目标值。

做法:如果取中点,划分出来的两部分不能形成子结构。初始为整个矩形区域,需要每次选取剩余矩形的右上角,如果右上角小于目标值,则去掉第一行,如果右上角大于目标值,则去掉最右边的列,是一个O(M+N)的解法。

137. Single Number II

题目:线性时间,O(1)空间,求每一个元素只可能出现一次或三次的整数数组中出现一次的数。

做法:这里的线性时间需要用到整数32位的限制,遍历一次数组,计算最末位的1的和,该和模3的结果是只出现一次的数的最末位的值,然后一次遍历32位,然后拼起来就得到结果。

260. Single Number III

题目:线性时间,O(1)空间,给定整数数组中有两个数只出现一次,其他的数都是出现两次,求只出现一次的两个数。

做法:这里需要用到异或的一个特性,如果一个数组只有一个数出现一次,其他数都是两次,对数组依次异或,最后的结果就是只出现一次的那个数。所以对原数组依次异或,得到的结果是那两个只出现一次的数的异或值,需要找到这个异或值的某位为1的位,根据这个位是否为1将原数组分成两组,对这两组数依次异或得到的两个值就是结果。

220. Contains Duplicate III

题目:给定长度为N的数组,整数K和T,如果可以在数组中找到两个数,下标差小于等于K,值的差的绝对值小于等于T,返回true,否则返回false。

做法:对每个数,需要考察前后下标在K以内的数,但是如果要遍历一次数组,实际需要考察每个数的前K个数里是否有满足条件的数。使用一个大小为K的滑动窗口,在遍历初期窗口大小可以小于K,该窗口内的数需要排序,并且需要经常插入和删除,所以使用map,map默认是排序的。对遍历的当前值A,在map中查找第一个大于等于A-T的数,如果该数与A的差的绝对值值小于等于T,则查找成功,否则维护窗口,继续遍历。

396. Rotate Function

题目:给定长度为N的数组,它有N个旋转数组,用0~N-1对应乘以旋转数组,求最大值。

做法:遍历原数组,记录0~N-1与对应元素的和Sum,以及所有元素的和S,记录初始最大值为Sum,遍历N-1次,每次进行一些操作,修改当前Sum,求与当前最大值的最大值,具体执行的操作示例如下。

0*4 + 1*3 + 2*2 + 3*6 初始值Sum

-1*4 + 0*3 + 1*2 + 2*6 (-= S)

0*4 + 0*3 + 1*2 + 2*6 (+= Ai)

3*4 + 0*3 + 1*2 + 2*6 (+= Ai * (N-1))

一开始i是0,操作完后从1开始,每次对应0*X的位置。

421. Maximum XOR of Two Numbers in an Array

题目:O(N)求非负整数数组取两个数进行异或运算的最大值。

做法:似乎涉及32位整数位运算的这个32的常数不算logN。把每个数按照二进制建二叉前缀树,对数组中的每个数,依次遍历二进制表示的各个位,如果为1,在树中尝试寻找0,如果没有0,就找1,为0则相反,找31次可以得到和该数异或的最大值。

424. Logest Repeating Character Replacement

题目:给一串大写字母和一个数字K,可用任意大写字母替换原串中某字母K次,求替换后最长相同字母子串长。

做法:滑动窗口初始化begin=end=0,同时维护窗口中26个大写字母各有几个。每次遍历先获取一个新的字母,修改对应字母的数量,递增end为后续移动窗口做准备。end-begin是当前窗口大小,end-begin-MAX(26个字母数量)是使得窗口内所有字母相同需要的替换次数,当这个次数大于K时,递增begin,修改对应字母数量。

窗口的初始长度为0,保证不需要替换也可以满足所有字母相同,窗口长度增加时如果使窗口内不满足条件,对于新增的字母,结果子串是否包含它有两种情况,如果不包含,当前的窗口就是从字符串头到这里的最大满足条件的窗口(如果此前有一个大于此窗口P的满足条件的窗口Q,此窗口P的左边缘会大于窗口Q的左边缘,遍历时一定会有当前左边缘与窗口Q左边缘重合的时候,不论当前右边缘是多少,在逐渐递增时一定会获得窗口Q),如果包含,把窗口的头去掉,包含进这个字母,得到的新窗口不一定满足条件(例如ABABACB,K=2,执行到C的时候,会把第一个A去掉,剩下的BABAC不满足条件), 接下来移动窗口是为了找到比现在的窗口大的满足条件的窗口,如果接下来再也找不到更大的窗口,就会输出之前得到的窗口大小,如果某个窗口扩充1格可以满足条件,窗口就会增大1。相当于优化了双重循环遍历所有begin和end,根据串内情况计算是否满足条件,每个begin不需要遍历所有的长度,以某个点开始,如果长度为20的串是满足条件的,则长度1-19的都满足条件,如果已经找到了长度为20的串,下一个begin+1就不需要考察end=begin+1了,直接看begin+20,写成双重循环加break计算量是差不多的。


LeetCode感想

面试考上机,不能用编译器,不能用long long,LeetCode里的第八题atoi,1小时没写完,真惨。我这里显示是5个月前做过,当时写了好久,用的方法也没有规划,速度较慢。当时上机的时候优化了一下,如果能写完速度应该还行。看来做过的题如果没有巩固,下次再碰到不一定能在规定时间内完成,不过要巩固的东西太多了,都掌握还是很困难的。

最近两天刷了一下LeetCode的中等难度题,直接从第二页开始,一天15题,一天17题,其中有一题没想出思路,看了答案。发现有些题目有“Follow up”,如果没有按照更高的要求完成,写起来相对比较容易,后续如果有时间再做一遍,应该要研究一下。现在注意到每次AC都会显示所写的代码在运行时间上打败了多少人,大部分都没办法做到时间最优,经常为了省事多遍历一次,如果靠自己想优化时间感觉很困难,过一遍别人的最优代码吸取一下经验,下一轮可能就能好一点。像那些树和图的题,如果调试的话写起来比较麻烦,其中刚好有很多简单题,直接交的话刷得快一点。好多题都是用递归写的,其中有些题如果不用笔来写,还是挺绕的,部分题目为了避免超时要加一个数组持久化,感觉如果转成动态规划会快一些。目前就一道明显的动态规划,后面应该会有更难的题。