def pop(self): # 弹出最大元素,同时交换最后一个元素和最大元素在维护最大堆 tool.swap(len(self), 1, self.heap) result = self.heap.pop() self._shift_down() return result
def __init__(self, arr: []): self.heap = arr i = len(self.heap) - 1 >> 1 while i >= 0: self._shift_down(i, len(self.heap)) i -= 1 for j in range(len(self.heap) - 1, 0, -1): tool.swap(0, j, arr) self._shift_down(0, j)
def _shift_up(self, k): """ 比较大的数据上浮 查看父节点是否大于当前节点,如果大交换位置,同时再次检查 :param k: :return: """ parent = self._get_parent(k) while k > 1 and self.heap[parent] < self.heap[k]: tool.swap(parent, k, self.heap) k = parent parent = self._get_parent(parent)
def _shift_down(self, k=1): """ 比较小的数据下沉 从子节点找到最大的数据,使其和父节点交换位置,重复这个操作到,最后元素 :param k: :return: """ while self._get_left_child(k) <= len(self): j = self._get_left_child(k) # 默认使用左节点交换位置 if j + 1 <= len(self) and self.heap[j + 1] > self.heap[j]: # 如果有右节点并且右节点的值大于左节点的值,使用右节点交换 j += 1 if self.heap[k] >= self.heap[j]: break tool.swap(k, j, self.heap) k = j
def bubble_sort(cls, arr: []) -> []: """ 冒泡排序: 找到最大的,排到最后,n次循环这样的过程,最终排好 :param arr: :return: """ length = len(arr) for i in range(length): max_index = 0 for j in range(0, length - i): if arr[j] > arr[max_index]: max_index = j # arr[j], arr[j + 1] = arr[j + 1], arr[j] tool.swap(length - i - 1, max_index, arr) return arr
def shell_sort(cls, arr: []) -> []: """ 希尔排序: 使用偏移量,每次比较几个子序列,用插入排序把数据排好 :param arr: :return: """ offset = len(arr) while offset > 1: offset = offset // 2 for i in range(offset, len(arr)): # 插入排序 j = i - offset while j >= 0 and arr[j] > arr[j + offset]: tool.swap(j, j + offset, arr) j -= offset return arr
def select_sort(cls, arr: []) -> []: """ 选择排序: 遍历列表,找到列表当前位置到最后位置的最小数,把这个最小数和当前值互换 :return: [] """ assert arr, "not arr" length = len(arr) for i in range(length): min_index = i # 最小下标 for j in range(i, length): # [i:length] 找到最小数的下标 if arr[min_index] > arr[j]: min_index = j # 位置互换 tool.swap(i, min_index, arr) return arr
def __partition(cls, arr, l, r): """ 使当前[l:r]区间的数组,以第一个数v分割,左边比v小,右边比v大 :param arr: :param l: :param r: :return: 分割数组的中间下标 """ # p = l # 分割数组的下标 # tool.swap(random.randint(l, r), l, arr) # 避免数组基本上是有序的 # v = arr[l] # # for i in range(l + 1, r + 1): # if arr[i] < v: # # 如果p下标之后的元素比分割数组的元素v小,则把当前元素和p下标所在位置交换 # p += 1 # tool.swap(i, p, arr) # # tool.swap(l, p, arr) # return p # ============= 处理数组里有大量重复元素 p = r # 分割数组的下标 tool.swap(random.randint(l, r), l, arr) # 避免数组基本上是有序的 v = arr[l] i = l + 1 while 1: while i <= r and arr[i] < v: i += 1 # 从左边开始,如果当前元素小于中间元素,位置不变,左值加一 while p > l and arr[p] > v: p -= 1 # 从右边开始,如果当前元素大于中间元素,位置不变,右值减一 if i > p: break # 左边和右边已经分开 tool.swap(i, p, arr) # 大值放在右边,小值放在左边,无序的 i += 1 # 完成一次,左值加一,右值减一 p -= 1 tool.swap(l, p, arr) # return p
def partition(arr, l, r): """ 以v分割数组, 左边的比v小, 右边的比v大 :param arr: :param l: :param r: :return: """ p = r n = random.randint(l, r) tool.swap(l, n, arr) v = arr[l] i = l + 1 while 1: while i <= r and arr[i] < v: i += 1 while p > l and arr[p] > v: p -= 1 if i > p: break tool.swap(i, p, arr) i += 1 p -= 1 tool.swap(l, p, arr) return p
def __partition3_ways(cls, arr, l, r): tool.swap(random.randint(l, r), l, arr) # 避免数组基本上是有序的 v = arr[l] lt = l # [l:lt] < v gt = r + 1 # [gt: r] > v i = l + 1 # 处理过的下标 while i < gt: if arr[i] < v: # 如果当前位小于v,把当前位和第二个元素交换 tool.swap(i, lt + 1, arr) i += 1 lt += 1 elif arr[i] > v: # 如果当前位大于v,把当前位和最后一个元素交换 tool.swap(i, gt - 1, arr) gt -= 1 else: i += 1 tool.swap(l, lt, arr) # 把第一个位置的元素与最后一个小于v的元素交换 return lt, gt