排序
一、快速排序
输入整数数组 arr
,找出其中最小的 k
个数 。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
(最小)堆排序算法 :https://docs.python.org/zh-cn/3/library/heapq.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Solution : def getLeastNumbers (self, arr: List [int ], k: int ) -> List [int ]: if k == 0 : return list () hp = [ -x for x in arr[:k]] heapq.heapify(hp) for i in range (k, len (arr)): if -hp[0 ] > arr[i]: heapq.heappop(hp) heapq.heappush(hp, -arr[i]) arr = [-x for x in hp] return arr def getLeastNumbers (self, arr: List [int ], k: int ) -> List [int ]: if k == 0 : return [] def quickSelect (low, high, k ): pvoit = random.randint(low, high) arr[pvoit], arr[low] = arr[low], arr[pvoit] base = arr[low] i = low for j in range (low+1 , high+1 ): if arr[j] < base: arr[i+1 ], arr[j] = arr[j], arr[i+1 ] i += 1 arr[low], arr[i] = arr[i], arr[low] if i == k - 1 : return arr[:k] elif i < k - 1 : return quickSelect(i+1 , high, k) else : return quickSelect(low, i-1 , k) return quickSelect(0 , len (arr) - 1 , k)
https://leetcode.cn/problems/kth-largest-element-in-an-array/solution/cpython3java-1da-gen-dui-diao-ku-2shou-l-xveq/
给定整数数组 nums
和整数 k
,请返回数组中第
k
个最大的元素。请注意,你需要找的是数组排序后的第
k
个最大的元素,而不是第 k
个不同的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Solution : def findKthLargest (self, nums: List [int ], k: int ) -> int : heap = [] for num in nums: if len (heap) >= k: if num > heap[0 ]: heapq.heapreplace(heap, num) else : heapq.heappush(heap, num) return heap[0 ] def findKthLargest (self, nums: List [int ], k: int ) -> int : def findTopk (low, high ): pvoit = random.randint(low, high) nums[pvoit], nums[low] = nums[low], nums[pvoit] base = nums[low] i = low for j in range (low + 1 , high + 1 ): if nums[j] > base: nums[i + 1 ], nums[j] = nums[j], nums[i + 1 ] i += 1 nums[i], nums[low] = nums[low], nums[i] if i == k - 1 : return nums[i] elif i < k - 1 : return findTopk(i + 1 , high) else : return findTopk(low, i - 1 ) return findTopk(0 , len (nums) - 1 )
输入一个非负 整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution : def minNumber (self, nums: List [int ] ) -> str : n = len (nums) def _smallA (a, b ): return True if a + b < b + a else False def partition (l, r ): pivot = random.randint(l, r) nums[pivot], nums[l] = nums[l], nums[pivot] base = nums[l] i = l for j in range (l+1 , r+1 ): if _smallA(nums[j], base): nums[j], nums[i+1 ] = nums[i+1 ], nums[j] i += 1 nums[l], nums[i] = nums[i], nums[l] return i def quick_sort (l, r ): if r - l <= 0 : return mid = partition(l, r) quick_sort(l, mid - 1 ) quick_sort(mid + 1 , r) nums = [str (num) for num in nums] quick_sort(0 , len (nums) - 1 ) return "" .join(nums).lstrip()
内置函数:cmp_to_key,单个元素并没有一个绝对的大小的情况
1 2 3 4 5 class Solution : def largestNumber (self, nums: List [int ] ) -> str : nums.sort(key=cmp_to_key(lambda x,y: int (str (y)+str (x)) - int (str (x)+str (y)))) ans = '' .join([str (num) for num in nums]) return str (int (ans))
从若干副扑克牌中随机抽 5
张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为
0 ,可以看成任意数字。A 不能视为 14。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution : def isStraight (self, nums: List [int ] ) -> bool : pset = set () mi, ma = 14 , -1 for num in nums: if num == 0 : continue if num in pset: return False mi = min (mi, num) ma = max (ma, num) pset.add(num) return ma - mi < 5 def isStraight (self, nums: List [int ] ) -> bool : joker = 0 nums.sort() for i in range (4 ): if nums[i] == 0 : joker += 1 elif nums[i] == nums[i + 1 ]: return False return nums[4 ] - nums[joker] < 5
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 heappush(self.B, -heappushpop(self.A, num))class MedianFinder : def __init__ (self ): """ initialize your data structure here. """ self.minheap = [] self.maxheap = [] def addNum (self, num: int ) -> None : if len (self.minheap) != len (self.maxheap): heappush(self.minheap, num) heappush(self.maxheap, -heappop(self.minheap)) else : heappush(self.maxheap, -num) heappush(self.minheap, -heappop(self.maxheap)) def findMedian (self ) -> float : return self.minheap[0 ] if len (self.minheap) != len (self.maxheap) else (self.minheap[0 ] - self.maxheap[0 ] ) / 2.0
二、归并排序
「归并排序」是分治思想的典型应用。
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
那么求逆序对和归并排序又有什么关系呢?
两个已排序的序列等待合并 ,记录计算逆序对的数量;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Solution : def reversePairs (self, nums: List [int ] ) -> int : def mergeSort (nums, low, high ): if low >= high: return 0 ans = 0 '''递归排序''' mid = low+(high-low)//2 ans += mergeSort(nums, low, mid) ans += mergeSort(nums, mid+1 , high) '''nums[low, mid] 和 nums[mid+1, high] 已排序好''' tmp = [] left, right = low, mid+1 while left<=mid and right<=high: if nums[left] <= nums[right]: tmp.append(nums[left]) left += 1 else : tmp.append(nums[right]) right += 1 ans += mid+1 -left '''解释:若nums[left] > nums[right], 则nums[left, mid] > nums[right]均成立,共 mid+1-left 项''' '''左或右数组需遍历完(最多只有一个未遍历完)''' while left<=mid: tmp.append(nums[left]) left += 1 while right<=high: tmp.append(nums[right]) right += 1 nums[low:high+1 ] = tmp return ans '''主程序''' return mergeSort(nums, 0 , len (nums)-1 )
给定链表的头结点 head
,请将其按 升序
排列并返回 排序后的链表 。
输入:head = [4,2,1,3] 输出:[1,2,3,4]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution : def sortList (self, head: Optional [ListNode] ) -> Optional [ListNode]: if not head or not head.next : return head slow, fast = head, head.next while fast and fast.next : fast, slow = fast.next .next , slow.next mid, slow.next = slow.next , None left, right = self.sortList(head), self.sortList(mid) h = dummy = ListNode(0 ) while left and right: if left.val < right.val: h.next = left left = left.next else : h.next = right right = right.next h = h.next h.next = left if left else right return dummy.next
给定一个链表数组,每个链表都已经按升序排列 。请将所有链表合并到一个升序链表中,返回合并后的链表。
输入:lists = [[1,4,5], [1,3,4], [2,6]] 输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下: [ 1->4->5, 1->3->4, 2->6 ]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
【优先队列】[推荐]
维护多个链表对象 ;时间:O(nlog(k)),n
是所有链表中元素总和,k
是链表个数;
【归并排序 】链表两两合并,递归占用很多空间;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Solution : def mergeKLists (self, lists: List [ListNode] ) -> ListNode: h = dummy = ListNode(0 ) heap = [] for i in range (len (lists)): if lists[i]: heapq.heappush(heap, (lists[i].val, i)) lists[i] = lists[i].next while heap: val, idx = heapq.heappop(heap) h.next = ListNode(val) h = h.next if lists[idx]: heapq.heappush(heap, (lists[idx].val, idx)) lists[idx] = lists[idx].next return dummy.next def mergeKLists (self, lists: List [ListNode] ) -> ListNode: if not lists:return n = len (lists) return self.merge(lists, 0 , n-1 ) def merge (self,lists, left, right ): if left == right: return lists[left] mid = left + (right - left) // 2 l1 = self.merge(lists, left, mid) l2 = self.merge(lists, mid+1 , right) return self.mergeTwoLists(l1, l2) def mergeTwoLists (self,l1, l2 ): if not l1:return l2 if not l2:return l1 if l1.val < l2.val: l1.next = self.mergeTwoLists(l1.next , l2) return l1 else : l2.next = self.mergeTwoLists(l1, l2.next ) return l2
三、堆排序
Python
十大排序算法:https://leetcode.cn/problems/sort-an-array/solution/python-shi-xian-de-shi-da-jing-dian-pai-xu-suan-fa/
堆排序是利用堆这个数据结构设计的排序算法。
算法描述:
建堆,从底向上==调整堆== ,使得父亲节点比孩子节点值大,构成大顶堆;
交换堆顶和最后一个元素,重新调整堆。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 def adjust_heap (nums, startpos, endpos ): pos = startpos chilidpos = pos * 2 + 1 if chilidpos < endpos: rightpos = chilidpos + 1 if rightpos < endpos and nums[rightpos] > nums[chilidpos]: chilidpos = rightpos if nums[chilidpos] > nums[pos]: nums[pos], nums[chilidpos] = nums[chilidpos], nums[pos] adjust_heap(nums, pos, endpos) def adjust_heap (nums, startpos, endpos ): newitem = nums[startpos] pos = startpos childpos = pos * 2 + 1 while childpos < endpos: rightpos = childpos + 1 if rightpos < endpos and nums[rightpos] >= nums[childpos]: childpos = rightpos if newitem < nums[childpos]: nums[pos] = nums[childpos] pos = childpos childpos = pos * 2 + 1 else : break nums[pos] = newitem
设计一个找到数据流中第 k
大元素的类(class)。注意是排序后的第 k
大元素,不是第
k
个不同的元素。
请实现 KthLargest 类:
KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums
初始化对象。
int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k
大的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class KthLargest : def __init__ (self, k: int , nums: List [int ] ): self.heap = [] self.size = k for num in nums: self._push(num) def _push (self, num ): if self.size <= len (self.heap): if self.heap[0 ] < num: heapq.heappop(self.heap) heapq.heappush(self.heap, num) else : heapq.heappush(self.heap, num) def add (self, val: int ) -> int : self._push(val) return self.heap[0 ]
给定一个整数数组 nums
和一个整数 k
,请返回其中出现频率前 k
高的元素。可以按
任意顺序 返回答案。
API :
return [c[0] for c in Counter(nums).most_common(k)]
快速选择
堆排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Solution : def topKFrequent (self, nums: List [int ], k: int ) -> List [int ]: def findTopk (low, high ): pivot = random.randint(low, high) nums_cnt[pivot], nums_cnt[low] = nums_cnt[low], nums_cnt[pivot] base = nums_cnt[low][1 ] i = low for j in range (low + 1 , high + 1 ): if nums_cnt[j][1 ] > base: nums_cnt[i + 1 ], nums_cnt[j] = nums_cnt[j], nums_cnt[i + 1 ] i += 1 nums_cnt[i], nums_cnt[low] = nums_cnt[low], nums_cnt[i] if i == k - 1 : return nums_cnt[:k] elif i < k - 1 : return findTopk(i + 1 , high) else : return findTopk(low, i - 1 ) nums_cnt = list (Counter(nums).items()) res = findTopk(0 , len (nums_cnt) - 1 ) return [r[0 ] for r in res] def topKFrequent (self, nums: List [int ], k: int ) -> List [int ]: conter = Counter(nums) heap = [] for key, val in conter.items(): if len (heap) >= k: if val > heap[0 ][0 ]: heapq.heapreplace(heap,(val, key)) else : heapq.heappush(heap,(val, key)) return [item[1 ] for item in heap]
给定两个以升序排列 的整数数组 nums1 和 nums2 ,
以及一个整数 k 。定义一对值 (u,v),其中第一个元素来自
nums1,第二个元素来自 nums2 。请找到和最小的 k 个数对 (u1,v1), (u2,v2)
... (uk,vk) 。
输入 : nums1 = [1,7,11], nums2 = [2,4,6], k = 3
输出 : [1,2],[1,4],[1,6] 解释 :
返回序列中的前 3 对数:
[1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution : def kSmallestPairs (self, nums1: List [int ], nums2: List [int ], k: int ) -> List [List [int ]]: m, n = len (nums1), len (nums2) heap = [(nums1[0 ] + nums2[0 ], 0 , 0 )] res = [] visited = set () while heap: val, i, j = heapq.heappop(heap) res.append((nums1[i], nums2[j])) visited.add((i, j)) if len (res) == k: return res if i + 1 < m and (i + 1 , j) not in visited: heapq.heappush(heap, (nums1[i + 1 ] + nums2[j], i + 1 , j)) visited.add((i + 1 , j)) if j + 1 < n and (i, j + 1 ) not in visited: heapq.heappush(heap, (nums1[i] + nums2[j + 1 ], i, j + 1 )) visited.add((i, j + 1 )) return res
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] =
[starti, endi] 。请你合并所有重叠的区间,并返回
一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]] 解释:区间 [1,3] 和 [2,6] 重叠,
将它们合并为 [1,6].
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution : def merge (self, intervals: List [List [int ]] ) -> List [List [int ]]: n = len (intervals) intervals.sort(key = lambda x: x[0 ]) ans = [] ans.append(intervals[0 ]) for i in range (1 , n): if ans[-1 ][1 ] >= intervals[i][0 ]: if (t := intervals[i][1 ]) > ans[-1 ][1 ]: ans[-1 ][1 ] = t else : ans.append(intervals[i]) return ans
给定两个数组,arr1 和 arr2,
arr2 中的元素各不相同
arr2 中的每个元素都出现在 arr1 中
对 arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2
中的相对顺序相同。未在 arr2 中出现过的元素需要按照升序放在 arr1
的末尾。
1 2 3 4 5 6 class Solution : def relativeSortArray (self, arr1: List [int ], arr2: List [int ] ) -> List [int ]: def cmp (k ): return (0 , cmpDict[k]) if k in cmpDict else (1 , k) cmpDict = {val:i for i, val in enumerate (arr2)} return sorted (arr1, key = cmp)