Dos métodos de clasificación de montón (Python) y preguntas relacionadas sobre clasificación de montón en la entrevista

Tabla de contenido

1. Dos métodos de clasificación de pilas

1. El primer método de clasificación de pilas

2. El segundo método de clasificación del montón: (Maestro)

2. Preguntas relacionadas con la entrevista

1. El menor número de k

2. El K-ésimo elemento más grande de la matriz

3. Los primeros K elementos de alta frecuencia 


1. Dos métodos de clasificación de pilas

Si no sabe nada sobre la clasificación de pilas, primero puede ver este video en la estación B.

https://www.bilibili.com/video/BV1Eb41147dK/

Al mismo tiempo, el primer método de clasificación del montón también se basa en este video.

1. El primer método de clasificación de pilas

def heapify(arr,n,i):
    large = i #父节点的下标为i
    left_child = 2*i+1 #左孩子下标
    right_child = 2*i+2
    if left_child<n and arr[left_child]>arr[large]:#如果左孩子的值大于父节点的值,将下标互换
        large = left_child
    if right_child<n and arr[right_child]>arr[large]:
        large = right_child
    if large != i:#如果下标换了,将最大子节点和父节点的值互换
        arr[large],arr[i] = arr[i],arr[large]
        heapify(arr,n,large)#将最大值的那个子树继续递归
 
def build_heap(arr,n):
    last_child = n-1
    last_parent = (last_child-1)//2 #从最后一个叶子节点的父节点开始以此向上调整
    for i in range(last_parent,-1,-1):
        heapify(arr,n,i)
 
def heap_sort(arr,n):
    build_heap(arr,n)
    for i in range(n-1,-1,-1):
        arr[0],arr[i] = arr[i],arr[0] #每次将最大的节点(根节点)与最后一个节点交换,然后去掉最后一个节点,继续调整为最大堆
        heapify(arr,i,0)
    return arr
arr = [0, 8, 6, 2, 4, 9, 1, 4, 6]
n = len(arr)
res = heap_sort(arr,n)
print(res)

2. El segundo método de clasificación del montón: (Maestro)

def heapify(arr):
    n = len(arr)
    for i in range(n//2,-1,-1):
        shiftDown(arr,n,i)

def shiftDown(arr, n, k):
    while 2 * k + 1 < n:
        j = 2 * k + 1
        if j + 1 < n and arr[j + 1] > arr[j]: #保证右孩子不超过数组长度,同时找出左右孩子中最大的那个孩子。这里大顶堆用>, 小顶堆用<
            j += 1  #如果右孩子大于左孩子,就使得使得j指向最大的那个元素,即指向右孩子
        if arr[k] >= arr[j]: #如果左孩子是最大的那个元素,但是父节点又大于左孩子,这样就满足大顶堆条件,就不需要换,直接退出即可。同样的,大顶堆用>=, 小顶堆用<=·
            break
        arr[k], arr[j] = arr[j], arr[k] #将最大的子节点和父节点互换
        k = j #互换后它的子树可能就不满足大顶堆,所以将子节点的下标赋值给父节点,继续循环,保证它的子树也是大顶堆  5


def heapSort(arr):
    n=len(arr)
    heapify(arr)
    print("堆化:",arr)
    for i in range(n-1):
        arr[n-i-1],arr[0] = arr[0],arr[n-i-1]
        # print("交换最小值后:",arr)
        shiftDown(arr,n-i-1,0)
        # print("调整后:",arr)


arr = [3,2,1,9,4,7,8]
heapSort(arr)
print("排序后:",arr)

2. Preguntas relacionadas con la entrevista

1. El menor número de k

Esta pregunta se puede ordenar por montones, pero si durante la entrevista, el entrevistador generalmente desea utilizar la clasificación rápida para completar, se escriben las siguientes dos formas de escribir:

Se implementa el primer método de clasificación del montón:

class Solution:
    def smallestK(self, arr: List[int], k: int) -> List[int]:     
        n = len(arr)
        self.build_heap(arr, n)
        pos = k
        for i in range(n - 1, pos-1, -1):
            arr[0], arr[i] = arr[i], arr[0]  # 每次将最大的节点(根节点)与最后一个节点交换,然后去掉最后一个节点,继续调整为最大堆
            self.heapify(arr, i, 0)
        return arr[:k]

    def heapify(self, arr, n, i):
    
        large = i  # 父节点的下标为i
        left_child = 2 * i + 1  # 左孩子下标
        right_child = 2 * i + 2
        if left_child < n and arr[left_child] > arr[large]:  # 如果左孩子的值大于父节点的值,将下标互换
            large = left_child
        if right_child < n and arr[right_child] > arr[large]:
            large = right_child
        if large != i:  # 如果下标换了,将最大子节点和父节点的值互换
            arr[large], arr[i] = arr[i], arr[large]
            self.heapify(arr, n, large)  # 将最大值的那个子树继续递归


    def build_heap(self,arr, n):
        last_child = n - 1
        last_parent = (last_child - 1) // 2  # 从最后一个叶子节点的父节点开始以此向上调整
        for i in range(last_parent, -1, -1):
            self.heapify(arr, n, i)

El segundo método de clasificación del montón:

La segunda forma de pensar se puede aplicar a algunos de los siguientes temas, por lo que debe dominarse

1. Toma los primeros K elementos de nums para crear un montón máximo de tamaño K.
2. Compara los k + 1 restantes con N elementos con la parte superior del montón. Si es más pequeño que la parte superior del montón, reemplaza la parte superior actual del montón y mantener el montón más grande

3. La primera k en la matriz final es el número más pequeño de k

class Solution:
    def smallestK(self, arr: List[int], k: int) -> List[int]:
        n = len(arr)
        self.build_heap(arr,k) 
        for i in range(k,n):
            if arr[0]>arr[i]:
                arr[0] = arr[i]
                self.heapify(arr,k,0)
        return arr[:k]

    def heapify(self,arr,n,i):
        while 2*i+1<n:
            j=2*i+1
            if j+1<n and arr[j+1]>arr[j]:
                j+=1
            if arr[i]>=arr[j]:
                break
            arr[i],arr[j] = arr[j],arr[i]
            i = j
    
    def build_heap(self,arr,n):
        for i in range(n//2,-1,-1):
            self.heapify(arr,n,i)

La realización de clasificación rápida: (enfoque en la masterización)

class Solution:
    def smallestK(self, arr: List[int], k: int) -> List[int]:
        if k>=len(arr):
            return arr
        low = 0
        high = len(arr)-1 
        while low<high:
            index = self.partition(arr,low,high)
            if index == k-1:
                break
            if index<k-1:
                low = index+1
            else:
                high = index-1
        return arr[:k]
        
                
    def partition(self,num,low,high):
        temp = num[low]
        while low<high:
            while low<high and temp<=num[high]:
                high-=1
            num[low] = num[high]
            while low<high and temp>=num[low]:
                low+=1
            num[high] = num[low]
        num[low]=temp
        return low

2. El K-ésimo elemento más grande de la matriz

Clasificación de montón, aquí está la idea de usar la segunda forma de escritura, pero esta vez debe reemplazarse con el montón más pequeño.

Nota: si la primera k es grande, use una pila de raíces pequeña, si la primera k es pequeña, use una pila de raíces grande

Ideas:

1. Toma los primeros K elementos de nums para crear un montón mínimo de tamaño K.
2. Compara los k + 1 restantes con N elementos con la parte superior del montón. Si es más grande que la parte superior del montón, reemplaza la parte superior del montón actual y mantener el montón mínimo
3. Finalmente, el elemento más pequeño en el montón más pequeño es el elemento con el mayor K, y la parte superior del montón es el elemento más pequeño entre los elementos con el mayor K, es decir, el elemento con el mayor Kth

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        n = len(nums)
        self.heapify(nums,k)
        for i in range(k,n):
            if nums[0]<nums[i]:
                nums[0] = nums[i]
                self.shift(nums,k,0)
        return nums[0]

    def heapify(self,arr,k):
        for i in range(k//2,-1,-1):
            self.shift(arr,k,i)
    def shift(self,arr,n,i):
        while 2*i+1<n:
            j = 2*i+1
            if j+1<n and arr[j+1]<arr[j]:
                j+=1
            if arr[i]<=arr[j]:
                break
            arr[i],arr[j] = arr[j],arr[i]
            i = j

Implementación de clasificación rápida:

El título es sobre el k-ésimo elemento más grande después de ordenar la matriz. Si se ordena en orden ascendente, entonces el k-ésimo elemento se cuenta de derecha a izquierda (comenzando desde 11), luego el número de izquierda a derecha es len (arr) -k

Entonces, la redacción general de clasificación rápida es la siguiente:

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        left = 0
        right = len(nums)-1
        target = len(nums)-k
        while True:
            index = self.partition(nums,left,right)
            if index==target:
                return nums[index]
            if index<target:
                left = index+1
            else:
                right = index-1
    def partition(self,nums,left,right):
        temp = nums[left]
        while left < right:
            while left<right and temp<=nums[right]:
                right-=1
            nums[left]=nums[right]
            while left<right and temp>=nums[left]:
                left+=1
            nums[right] = nums[left]
        nums[left] = temp
        return left

Esta operación puede pasar, pero el tiempo es muy lento.

Esto se debe a que los casos de prueba tendrán ejemplos extremos, como el orden y el orden inverso, de modo que la complejidad de la clasificación rápida se convierte en O (n²), por lo que la clasificación rápida se optimiza.

Es seleccionar aleatoriamente la temperatura y observar cuidadosamente la implementación de la partición.

import random
class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        left = 0
        right = len(nums)-1
        target = len(nums)-k
        while True:
            index = self.partition(nums,left,right)
            if index==target:
                return nums[index]
            if index<target:
                left = index+1
            else:
                right = index-1
    def partition(self,nums,left,right):
        random_index = random.randint(left, right)
        nums[random_index], nums[left] = nums[left], nums[random_index]

        pivot = nums[left]
        j = left
        for i in range(left + 1, right + 1):
            if nums[i] < pivot: #小于pivot的元素都被交换到前面
                j += 1
                nums[i], nums[j] = nums[j], nums[i]

        nums[left], nums[j] = nums[j], nums[left] #在之前遍历的过程中,满足 [left + 1, j] < pivot,并且 (j, i] >= pivot, 交换以后 [left, j - 1] < pivot, nums[j] = pivot, [j + 1, right] >= pivot
        return j

3. Los primeros K elementos de alta frecuencia

Esta pregunta se puede escribir con una solución general, el código es el siguiente, el código es muy simple, así que no lo explicaré

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        
        dict = {}
        for i in nums:
            if i not in dict:
                dict[i] = 1
            else:
                dict[i] += 1
        dict_sort = sorted(dict.items(), key = lambda x: x[1],reverse = True)
        res = []
        for i in range(k):
            res.append(dict_sort[i][0])
        return res

De esta manera, aún puede usar el segundo método de clasificación de montones y las ideas de las dos preguntas anteriores:

class Solution(object):
    def topKFrequent(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        def shift(i,k):#维护最小堆
            while True:
                t=2*i+1
                if t >= k :
                    return
                if t+1<k and hashlist[t][1]>hashlist[t+1][1]:
                    t=t+1
                if hashlist[t][1]<hashlist[i][1]:
                    hashlist[t],hashlist[i]=hashlist[i],hashlist[t]                   
                    i=t
                else:
                    return


        #建立哈希表
        hashmap={}
        for i in nums:
            hashmap[i]=hashmap.get(i,0)+1
        #print(hashmap)
        #将哈希表转为二维列表
        hashlist=[ [key,v] for key, v in hashmap.items() ]
        #print(hashlist)
        #建立K个元素的最小堆
        for i in range(k/2,-1,-1):
            shift(i,k)
        #剩余依次和堆顶比较
        for i in range(k,len(hashlist)):
            if hashlist[i][1]>=hashlist[0][1]:
                hashlist[0]=hashlist[i]
                shift(0,k)
        return [hashlist[i][0] for i in range(k)]

 

Parte de la referencia del código:

https://leetcode-cn.com/problems/top-k-frequent-elements/solution/quan-shou-xie-jian-li-kge-yuan-su-de-zui-xiao-dui-/

https://leetcode-cn.com/problems/kth-largest-element-in-an-array/solution/partitionfen-er-zhi-zhi-you-xian-dui-lie-java-dai-/

https://www.runoob.com/python3/python-heap-sort.html

 

Supongo que te gusta

Origin blog.csdn.net/Matrix_cc/article/details/106606612
Recomendado
Clasificación