https://blog.csdn.net/weixin_41571493/article/details/81875088
时间复杂度的下界是o(nlogn)证明:
对于n个待排序元素,在未比较时,可能的正确结果有n!种。在经过一次比较后,其中两个元素的顺序被确定,所以可能的正确结果剩余n!/2种。依次类推,直到经过m次比较,剩余可能性 n ! / ( 2 m ) n!/(2^m) n!/(2m)种。直到 n ! / ( 2 m ) < = 1 n!/(2^m)<=1 n!/(2m)<=1时,结果只剩余一种。此时的比较次数m为o(nlogn)次。所以基于排序的比较算法,最优情况下,复杂度是o(nlogn)的。
给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数。
示例 1:
输入: [10,2]
输出: 210
示例 2:
输入: [3,30,34,5,9]
输出: 9534330
说明: 输出结果可能非常大,所以你需要返回一个字符串而不是整数。
解法
其实就是写一个排序方法,判断两个字符串的大小。两个字符串数字a,b,
若a+b 记一下map,sorted,lambda的用法。
class Solution(object):
def largestNumber(self, nums):
"""
:type nums: List[int]
:rtype: str
"""
if not nums:
return ""
# nums = [str(x) for x in nums]
nums = sorted(list(map(str, nums)), cmp = lambda x, y: cmp(x+y, y+x), reverse = True)
if nums[0] == '0':
return '0'
else:
return ''.join(nums)
给定一个无序的数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]… 的顺序。
示例 1:
输入: nums = [1, 5, 1, 1, 6, 4]
输出: 一个可能的答案是 [1, 4, 1, 5, 1, 6]
示例 2:
输入: nums = [1, 3, 2, 2, 3, 1]
输出: 一个可能的答案是 [2, 3, 1, 3, 1, 2]
说明:
你可以假设所有输入都会得到有效的结果。
进阶:
你能用 O(n) 时间复杂度和 / 或原地 O(1) 额外空间来实现吗?
解法
想到对数组从小到大排序,然后从左到右间隔排序,就像示例1一样,奇数位置是前半排序,偶数位置是后半排序。但是由于排序会出现中间好几个数相同的情况,比如[4, 5, 5, 6],按照这个方法得到的结果还是[4, 5, 5, 6],所以可以在赋值的时候,倒着赋值,即按照从大到小的顺序。
看了网上的答案,发现思想相同,但是代码简洁的多,特别是求中间位置的时候直接用len(nums[::2]),以及赋值的时候不需要循环。
class Solution(object):
def wiggleSort(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
tmp = sorted(nums, reverse=False)
mid = len(tmp) / 2 if len(tmp) % 2 == 0 else len(tmp) / 2 + 1
#print(mid, tmp)
i = 0
j = mid - 1
k = len(nums) - 1
while i < len(nums):
nums[i] = tmp[j]
i += 1
if i < len(nums):
nums[i] = tmp[k]
i += 1
j -= 1
k -= 1
class Solution(object):
def wiggleSort(self, nums):
nums.sort()
half = len(nums[::2])
nums[::2], nums[1::2] = nums[:half][::-1], nums[half:][::-1]
峰值元素是指其值大于左右相邻值的元素。
给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。
数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞。
示例 1:
输入: nums = [1,2,3,1]
输出: 2
解释: 3 是峰值元素,你的函数应该返回其索引 2。
示例 2:
输入: nums = [1,2,1,3,5,6,4]
输出: 1 或 5
解释: 你的函数可以返回索引 1,其峰值元素为 2;
或者返回索引 5, 其峰值元素为 6。
说明:
你的解法应该是 O(logN) 时间复杂度的。
解法 给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。 示例 1: 输入: [1,3,4,2,2] 输入: [3,1,3,4,2] 说明: 解法 二分法代码: 快慢指针代码: 给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 注意: 示例: 输入: [2,0,2,1,1,0] 一个直观的解决方案是使用计数排序的两趟扫描算法。 解法 合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。 示例: 输入: 解法 思路二的解法: 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。 解法 注意:记得最后要判断一下最后得到的数字是否真的是众数。这几种方法的时间复杂度都是O(n)。 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。 解法 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。 解法 类似于题:把一个字符串的小写字母放到前面,大写放到后面,保持原有的顺序。 LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。 题解 所以根据如上两个条件,算法过程如下: 给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。 说明: 示例: 解法 给定一个未排序的整数数组,找出其中没有出现的最小的正整数。 示例 1: 输入: [1,2,0] 输入: [3,4,-1,1] 输入: [7,8,9,11,12] 你的算法的时间复杂度应为O(n),并且只能使用常数级别的空间。 解法 比如[3,4,-1,1],按照我说的操作后,它应该是[1,-1,3,4],1,3,4全都在自己的位置上,剩下一个-1,它就是一个坑,这个坑本来应该是2(与index+1对应),所以缺失的第一个正数是2。总之,经过我们这样的操作后,所有能归位的元素都已经到了对应的位置上,一旦数组里的数字大小不等于对应的index+1时,这个点就是第一个缺的点,于是我们返回这个点的index+1即可。 给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。 示例 1: 输入: nums = [1,2,3,1], k = 3 输入: nums = [1,0,1,1], k = 1 输入: nums = [1,2,3,1,2,3], k = 2 解法 解法一: 解法二: 在整数数组 nums 中,是否存在两个下标 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值小于等于 t ,且满足 i 和 j 的差的绝对值也小于等于 ķ 。 如果存在则返回 true,不存在返回 false。 示例 1: 输入: nums = [1,2,3,1], k = 3, t = 0 输入: nums = [1,0,1,1], k = 1, t = 2 输入: nums = [1,5,9,1,5,9], k = 2, t = 3 解法 辅导课堂在推进质量建设,需要分析每堂直播课的用户报障数量。
因为时间复杂度是O(logN)的,所以不能直接遍历,logN可以想到二分法。注意数组的0和len-1位置也是有可能的峰值。
此时我们应该考虑nums[k]和nums[k+1]的关系。如果我们nums[k]>nums[k+1],我们应该让j=k,因为k很可能就是峰值(因为右边小,而左边存在-inf)。而如果nums[k]class Solution(object):
def findPeakElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
l = 0
r = len(nums) -1
while l < r:
mid = (l + r) // 2
if nums[mid] <= nums[mid + 1]:
l = mid + 1
else:
r = mid
return l
寻找重复数
输出: 2
示例 2:
输出: 3
参考二分法解法
快慢指针的方法class Solution(object):
def findDuplicate(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
l, r = 0, len(nums) - 1
while l < r:
mid = (l + r) // 2
cnt = 0
for x in nums:
if x <= mid:
cnt += 1
if cnt <= mid:
l = mid + 1
else:
r = mid
return l
class Solution(object):
def findDuplicate(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
slow = nums[0]
fast = nums[nums[0]]
while slow != fast:
slow = nums[slow]
fast = nums[nums[fast]]
entry = 0
while entry != slow:
entry = nums[entry]
slow = nums[slow]
return entry
颜色分类
不能使用代码库中的排序函数来解决这道题。
输出: [0,0,1,1,2,2]
进阶:
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。
你能想出一个仅使用常数空间的一趟扫描算法吗?
同时使用3个指针,这样只需要进行一次遍历,如果是0,则往前移动,如果是2则往后。
注意,在循环过程中,如果遇到元素等于2的情况,由于数字从end位置调换过来,因此需要再对该位置的数字进行判断,因此这个时候的k不能也+1;而从前往后遍历的时候,当元素等于1的时候,前面的数字替换过来不会有错,因为前面的数不可能出现2。class Solution(object):
def sortColors(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
s, e = 0, len(nums) - 1
k = 0
while k <= e:
if nums[k] == 0:
nums[s], nums[k] = nums[k], nums[s]
s += 1
k += 1
elif nums[k] == 2:
nums[e], nums[k] = nums[k], nums[e]
e -= 1
else:
k += 1
return nums
合并K个排序链表
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# @param a list of ListNode
# @return a ListNode
def mergeKLists(self, lists):
heap = []
for node in lists:
if node != None:
heap.append((node.val, node))
heapq.heapify(heap)
head = ListNode(0)
curr = head
while heap:
pop = heapq.heappop(heap)
curr.next = ListNode(pop[0])
curr = curr.next
if pop[1].next:
heapq.heappush(heap, (pop[1].next.val, pop[1].next))
return head.next
数组中出现次数超过一半的数字
# -*- coding:utf-8 -*-
class Solution:
def MoreThanHalfNum_Solution(self, numbers):
# write code here
cnt, tmp = 1, numbers[0]
for x in numbers[1:]:
if x == tmp:
cnt += 1
else:
cnt -= 1
if cnt == 0:
cnt = 1
tmp = x
cnt = 0
for x in numbers:
if x == tmp:
cnt += 1
if cnt > len(numbers)/2:
return tmp
return 0
把数组排成最小的数
def PrintMinNumber(self, numbers):
# write code here
n = len(numbers)
for i in range(n):
for j in range(i+1, n):
if int(str(numbers[i]) + str(numbers[j]) > str(numbers[j]) + str(numbers[i])):
numbers[j], numbers[i] = numbers[i], numbers[j]
return ''.join([str(i) for i in numbers])
# -*- coding:utf-8 -*-
class Solution:
def PrintMinNumber(self, numbers):
# write code here
lmb = lambda a, b: int(str(a) + str(b)) - int(str(b) + str(a))
numbers = sorted(numbers, cmp=lmb)
res = "".join(str(x) for x in numbers)
return res
调整数组顺序使奇数位于偶数前面
参考https://www.nowcoder.com/questionTerminal/beb5aa231adc45b2a5dcc5b62c93f593?answerType=1&f=discussion
# -*- coding:utf-8 -*-
class Solution:
def reOrderArray(self, array):
# write code here
i = 0
for j in range(len(array)):
if array[j] % 2 == 1:
tmp = array[j]
for k in range(j - 1, i - 1, -1):
array[k + 1] = array[k]
array[i] = tmp
i += 1
return array
扑克牌顺子
参考: https://www.nowcoder.com/questionTerminal/762836f4d43d43ca9deb273b3de8e1f4?answerType=1&f=discussion
这个题也不难,本来想的是用一堆判断来做,但是发现有更简单的思路。
分两种情况考虑,
# -*- coding:utf-8 -*-
class Solution:
def IsContinuous(self, numbers):
# write code here
if not numbers:
return False
mi, ma = 14, 0
res = set()
for x in numbers:
if x in res:
return False
if x != 0 and x not in res:
res.add(x)
if x < mi:
mi = x
if x > ma:
ma = x
if ma - mi < 5:
return True
return False
两数之和 II - 输入有序数组
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
返回的下标值(index1 和 index2)不是从零开始的。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
class Solution(object):
def twoSum(self, numbers, target):
"""
:type numbers: List[int]
:type target: int
:rtype: List[int]
"""
for i in range(len(numbers)):
if numbers[i] > target:
break
x = target - numbers[i]
l, r = i + 1, len(numbers) - 1
while l <= r:
mid = (l + r) // 2
if numbers[mid] == x:
return [i + 1, mid + 1]
if numbers[mid] > x:
r = mid - 1
else:
l = mid + 1
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
idx1, idx2 = 0, len(numbers) - 1
while idx1 < idx2:
if numbers[idx1] + numbers[idx2] == target:
return [idx1 + 1, idx2 + 1]
elif numbers[idx1] + numbers[idx2] > target:
idx2 -= 1
else:
idx1 += 1
缺失的第一个正数
输出: 3
示例 2:
输出: 2
示例 3:
输出: 1
说明:
参考:https://blog.csdn.net/weixin_41958153/article/details/80950245
这道题的要求线性的复杂度和常数的空间,所以基本定下了一个总基调:1.一遍循环。2.原地修改
思路:非常tricky的想法。给我们的这个数组里,有负数,有正数,正数里还分大于(数组长度-1)的数和小于数组长度的数这两类。如果我们能把小于数组长度的数全部填入应该填入的位置(如果我们有1,它应该在0号位,如果我们有2,它应该在1号位,以此类推),那剩下的位置就留给那些负数和大于(数组长度-1)的数随机分配。这样一来,我么就会发现一些奇妙的东西。
如果数组里没有坑,那就是我们数组的长度是m,而元素是1…m时才有可能。这种时候第一个缺失的正数就是m+1。class Solution:
def firstMissingPositive(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
#第一个循环去遍历Nums里的每一个元素
for i in range(len(nums)):
#如果当前这个数字在我们能够进行转移的范围内
if 0 < nums[i] and nums[i]< len(nums):
# if nums[i] != nums[nums[i]-1]:
#如果当前这个位置上的数字与它的index对应不上的话,我们会把它放到他应该在的正确的位置上去
#同时为了保证数组元素信息不丢失,那个正确位置上的信息我们也要拿过来。
while nums[i] != i + 1:
nums[nums[i]-1], nums[i] = nums[i], nums[nums[i] - 1]
#一旦换完后当前位置i上的元素出现以下任意一种情况都说明我们不用再换了
if nums[i] <= 0 or nums[i] >= len(nums) or nums[i]==nums[nums[i]-1]:
break
#上一段代码在干的事情就是我们让大于0小于len(nums)的元素全部到对应的位置上去
#比如nums为[1,3,-1,5,2],那么经过上面之后就变成[1,2,3,-1,5],能够对应上的数字全部已经正确排序,缺陷的(-1)的index+1就是缺失的第一个正数。
jet = 1
#再次遍历数组
#jet是一个标识符,用来标志我们找没找到不符合的项
for i in range(len(nums)):
#找到的第一个大小与index不符的项,我们的答案就已经产生,就是当前index+1
if nums[i] != i + 1:
jet = 0
ans = i + 1
break
#如果数组是[1,2,3]这种,每一项都符合,则缺失的是4,也就是len(nums) + 1
if jet == 1:
ans = len(nums)+1
return ans
存在重复元素 II
输出: true
示例 2:
输出: true
示例 3:
输出: false
class Solution(object):
def containsNearbyDuplicate(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: bool
"""
dic = {}
for i in range(len(nums)):
if nums[i] in dic and i - dic[nums[i]] <= k:
return True
dic[nums[i]] = i
return False
class Solution:
def containsNearbyDuplicate(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: bool
"""
n = len(nums)
if (n <= 1):
return False
recode = set()
for i in range(n):
if nums[i] in recode:
return True
recode.add(nums[i])
if len(recode) > k:
recode.remove(nums[i - k])
return False
存在重复元素 III
输出: true
示例 2:
输出: true
示例 3:
输出: false
也是查找表与滑动窗口的思路:维持滑动窗的大小最大为 k,遍历每一个元素 nums[i],在活动窗口中寻找 |one-nums[i]| < t,即窗口中的元素范围为:[one-t … one+t] 之间class Solution:
def containsNearbyAlmostDuplicate(self, nums, k, t):
"""
:type nums: List[int]
:type k: int
:type t: int
:rtype: bool
"""
n = len(nums)
if n <= 1:
return False
recode = set()
for i in range(n):
if t == 0:
if nums[i] in recode:
return True
else:
for one in recode:
if abs(nums[i] - one) <= t:
return True
recode.add(nums[i])
if (len(recode) > k):
recode.remove(nums[i - k])
return False
最长连续和小于s的长度
当连续多个课程的报障数量之和大于一个数s的时候,系统会发出报警。小猿想知道最长连续的没有触发报警的课程数量。n,s = list(map(int,input().strip().split()))
l = list(map(int, input().strip().split()))
left, right = 0, 1
course_sum = l[0]
res = 0
while left <= right and right < n:
if course_sum <= s:
res = max(res, right - left)
else:
course_sum -= l[left]
left += 1
course_sum += l[right]
right += 1
print(res)