Deux méthodes de tri de tas (Python) et questions connexes sur le tri de tas dans l'interview

table des matières

1. Deux méthodes de tri en tas

1. La première méthode de tri par tas

2. La deuxième méthode de tri de tas: (Master)

2. Questions liées aux entrevues

1. Le plus petit nombre de k

2. Le Kième élément le plus grand du tableau

3. Les K premiers éléments haute fréquence 


1. Deux méthodes de tri en tas

Si vous ne savez rien sur le tri en tas, vous pouvez d'abord regarder cette vidéo sur la station B.

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

Dans le même temps, la première méthode de tri de tas est également basée sur cette vidéo.

1. La première méthode de tri par tas

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. La deuxième méthode de tri de tas: (Master)

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. Questions liées aux entrevues

1. Le plus petit nombre de k

Cette question peut être triée par tas, mais si lors de l'entretien, l'intervieweur souhaite généralement utiliser un tri rapide pour terminer, les deux manières d'écrire suivantes sont écrites:

La première méthode de tri de tas est implémentée:

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)

La deuxième méthode de tri de tas:

La deuxième façon de penser peut être appliquée à certains des sujets suivants, elle doit donc être maîtrisée

1. Prenez les K premiers éléments de nums pour créer un tas maximum de taille K.
2. Comparez les k + 1 à N éléments restants avec le haut du tas à tour de rôle. S'il est plus petit que le haut du tas, remplacez le haut actuel du tas et conserver le plus grand tas

3. Le premier k du tableau final est le plus petit nombre 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 réalisation du tri rapide: (focus sur la maîtrise)

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. Le Kème élément le plus grand du tableau

Tri de tas, voici l'idée d'utiliser la deuxième façon d'écrire, mais cette fois il faut le remplacer par le plus petit tas.

Remarque: si le premier k est grand, utilisez un petit pieu de racines, si le premier k est petit, utilisez un gros pieu de racines

Idées:

1. Prenez les K premiers éléments de nums pour créer un tas minimum de taille K.
2. Comparez les k + 1 à N éléments restants avec le haut du tas à tour de rôle. S'il est plus grand que le haut du tas, remplacez le haut du tas actuel et maintenir le tas minimum
3. Enfin, le plus petit élément du plus petit tas est l'élément avec le plus grand K, et le haut du tas est le plus petit élément parmi les éléments avec le plus grand K, c'est-à-dire le élément avec le plus grand 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

Implémentation du tri rapide:

Le titre correspond au k-ième élément le plus grand après le tri du tableau. S'il est trié par ordre croissant, le k-ième élément est compté de la droite vers la gauche (à partir de 11), puis le nombre de gauche à droite est len (arr) -k

Le libellé général du tri rapide est donc le suivant:

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

Cette opération peut passer, mais le temps est très lent

En effet, les cas de test auront des exemples extrêmes, tels que l'ordre et l'ordre inverse, de sorte que la complexité du tri rapide devient O (n²), de sorte que le tri rapide est optimisé.

Il s'agit de sélectionner au hasard la température et d'examiner attentivement la mise en œuvre de la partition.

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. Les K premiers éléments haute fréquence

Cette question peut être écrite avec une solution générale, le code est le suivant, le code est très simple, donc je ne l'expliquerai pas

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 cette façon, vous pouvez toujours utiliser la deuxième méthode de tri de tas et les idées des deux questions précédentes:

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)]

 

Partie de la référence de code:

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

 

Je suppose que tu aimes

Origine blog.csdn.net/Matrix_cc/article/details/106606612
conseillé
Classement