Leetcode排序

本文介绍了多种排序算法,包括冒泡、选择、插入、归并、堆、快速、希尔、计数和基数排序,并提供了具体实现。同时,详细讲解了如何解决字母异位词分组和找到字符串中所有字母异位词的问题,以及颜色分类和任务调度器的解决方案。这些算法和问题在编程面试和实际开发中常见,对提升编程技能很有帮助。
摘要由CSDN通过智能技术生成


十大排序算法
冒泡排序:重复地走访要排序的数列,一次比较两个元素,如果顺序错误就交换这两个元素。一直到没有再需要交换的为止。元素经交换慢慢“浮”到数列顶端
步骤:

  1. 比较相邻元素,若第一个比第二个大,就交换位置
  2. 对每一对相邻元素作同样操作,这样最大的元素一定会在末尾
  3. 对所有元素(除去最后一个(最后一个一定最大,无需比较))重复上述操作
  4. 重复1-3,直到排序完成
  5. 注:两层for循环,时O(N^2)

选择排序
思想和冒泡排序类似,可看为冒泡排序的优化。不断在剩余元素中找最大(小)指
步骤:

  1. 找到数组中最大(小)的那个元素
  2. 和数组的第一个元素交换位置(若第一个元素就是最大(小)元素那么就和它自己交换)
  3. 在剩下的元素中找到最大(小)的元素,和数组的第二个元素交换位置。如此往复知道将整个数组排序
  4. 注:找最小——升序;找最大:降序
  5. 注:两层for循环,时O(N^2)

插入排序
对未排序的数,在已排序序列中从后往前扫描,找到相应位置并插入
注:要为待插入数据腾出空间,需要将插入位置之后的已排序元素先向后移动一位;插入排序所需时间取决于输入的序列的初始顺序,如果已排序或接近有序,则使用插入排序会很高效

④ 归并排序
对给定的一组数据,利用递归和分支将数组序列划分成越来越小的半子集,在对半子集排序后,再用递归的方法将排好序的半子表合并成更大的有序序列
解释:先划分为很小的子集,再对两两数组排序,然后两两有序数组合并

⑤ ⭐堆排序
⑥ ⭐快速排序(quicksort)
对冒泡的一种改进,是分治法的一个典型应用

  • 首先任选一个数据作为关键数据(基准数),将所有比它小的数放前面,所有比它大的数放后面,这个过程称为一趟快速排序,也成为分区(partition)操作
  • 对基准数左右两边分别进行快速排序
  • 整个排序过程递归进行,直到所有数组有序
  • 时O(NlogN) 空O(logN)

⑦ 希尔排序
基于插入排序的改进,也称为缩小增量排序。
主要思想:对一组元素使用增量进行分组(如,增量为3,指下标差为3的元素分为一组),对每组元素直接使用插入排序;慢慢缩小增量(每组内元素会增加)至1(此时整个数组为一组),再次使用插入排序,置止完成整个数组的排序
增量选取,一般为N/2, (N/2)/2, ……, 1

⑧ 计数排序
适合于数组中元素值分布较连续,跨度小的情况
如:0 1 2 1 2 2 0 0
主要思想:设置一个数组大小为待排序数组中的最大元素值+1,初始化为0;用于记录每个元素出现的次数,然后根据次数重写待排数组,得到的就是排序好的数组
时:O(N);空:O(K)_K为待排数组的元素跨度

⑨ 基数排序
⑩ 桶排序

49 字母异位词分组

在这里插入图片描述
题目解释:给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。(字母异位词,如“abc”和“bca”,“cba”)

方法一:排序
字母异位词排序后一定相同

# 法1:快排+哈希表
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
    hashmap = {}
    for s in strs:
        s_sorted = "".join(sorted(s))
        if s_sorted not in hashmap:
            hashmap[s_sorted] = [s]  # 若不在哈希表中,则添加  注意使用[]
        else:            
            hashmap[s_sorted].append(s)  # 若在哈希表,则向value中添加字符串s
    return list(hashmap.values())  # 转为列表输出

# 时:O(nklogk)——用到快排,n是字符串数量,k是最长字符串长度
# 空:O(nk)——哈希表存储所有字符串

方法二:计数
对每个字符串计数得到该字符串的计数数组,对于计数数组相同的字符串,就互为异位词。
把这个数组手动编码变成字符串作为哈希表的key。
比如将 [b,a,a,a,b,c] 编码成 a3b2c1。

# 法2:不使用快排而是记录每个字符串各元素的出现次数,若出现次数相同则为字母异位词
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
    hashmap = {}
    for s in strs:
        counter = [0]*26  # 使用数组记录每个元素出现的次数
        for i in s:
            num = ord(i) - ord('a')
            counter[num] += 1
        recode = ''  # 对数组再编码,如counter仅在0,1,2处分别有值1,2,3,则再编码后为a1b2c3,以此作为key
        for i in range(26):
            if counter[i] > 0:
                key = chr(i + ord('a'))  # 找到对应的字符
                recode += key
                recode += chr(counter[i])            
        if recode not in hashmap:  # 将再编码得到的字符串作为哈希表的key
            hashmap[recode] = [s]
        else:
            hashmap[recode].append(s)
    return list(hashmap.values())  # 注意是.value()
# 时:O(nk) n是字符串数量,k是最长字符串长度
# 空:O(nk) 哈希表存储

438 找到字符串中所有字母异位词(滑动窗口)

75 颜色分类(较难)

在这里插入图片描述

def sortColors(self, nums: List[int]) -> None:
    """
    Do not return anything, modify nums in-place instead.
    """
   
    def swap(index1, index2):
        nums[index1], nums[index2] = nums[index2], nums[index1]
    
    p1 = 0  # p1左全为0
    p2 = len(nums)-1  # p2右全为2
    i = 0  # 遍历每个元素
    while i <= p2:  # 若i大于p2,则所有元素都为2,不用遍历
        if nums[i] == 0:                
            swap(i, p1) 
            i += 1
            p1 += 1
        elif nums[i] == 2:
            swap(i, p2)
            p2 -= 1
        else:
            i += 1      

时O(N) 空O(1)

148 排序链表(较难)

在这里插入图片描述

法1 递归(不满足进阶要求,复杂度怎么分析)

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if not head or not head.next:  # 链表长度为1则不用排序
            return head
        
        mid = self.middleNode(head)  

        left = self.sortList(head)
        right = self.sortList(mid)

        return self.merge(left, right)  # 合并两个有序列表

    def middleNode(self, head):
        if not head or not head.next: 
            return head
        dummynode = ListNode(0, head)
        slow = dummynode
        fast = head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
        newhead = slow.next
        slow.next = None
        return newhead
        
    def merge(self, l1, l2):
        if not l1:
            return l2
        if not l2:
            return l1
        if l1.val < l2.val:
            l1.next = self.merge(l1.next, l2)
            return l1
        else:
            l2.next = self.merge(l1, l2.next)
            return l2


215 数组中的第K个最大元素

在这里插入图片描述

def findKthLargest(self, nums: List[int], k: int) -> int:
    return self.quickselect(nums, k)


def quickselect(self, nums, k):
    randomIndex = random.randint(0, len(nums)-1)
    pivot = nums[randomIndex]
    left = []
    right = []
    mid = []
    for i in nums:
        if i < pivot:
            left.append(i)
        elif i > pivot:
            right.append(i)
        else:
            mid.append(i)
    '''
    if len(right)>=k:
        return self.quickselect(right, k)
    elif len(right) + len(mid) == k:  # 去掉,可能pivot就是第k个,但等于pivot的数很多,此时就不满足这个判断
        return pivot
    else:
        return self.quickselect(left, k - len(right) - len(mid))
    '''  
    if len(right) >= k:
        return self.quickselect(right,k)

    elif len(right) + len(mid) < k:
        return self.quickselect(left,k-len(right)-len(mid))

    return pivot

在这里插入图片描述

注,上述代码是在快排基础上的排序,每次分区后只有一个区域需要递归,提高了时间效率——快速选择算法,时O(N)
由于使用了递归操作,空O(logn)

581 最短无序连续子数组

在这里插入图片描述

解释:
在这里插入图片描述

在这里插入图片描述
如果进入了右段,就没有比最大值小的数,所以最后一个比最大值小的数就是中段的右边界,同理,如果进入左段,就不会出现比最小值更大的情况,所以最后一个出现就视为中段左边界?

class Solution:
    def findUnsortedSubarray(self, nums: List[int]) -> int:
        n = len(nums)
        maxnum = nums[0]
        minnum = nums[n-1]
        end = -1 # 为了无需排序的情况 即end-start+1=0
        start = 0
        for i in range(n):
            if nums[i]<maxnum:  # 从左向右 若找到比当前最大值小的则更新右边界,否则更新最大值(因为进入右端就一定大于最大值)
                end = i
            else:
                maxnum = nums[i]
            
            if nums[n-1-i]>minnum:  # n-1-i是从右向左
                start = n-1-i
            else:
                minnum = nums[n-1-i]
        return end-start+1

621 任务调度器

题解
主要思想:找到出现次数最多的字符,结合冷却时间确定固有时间(m-1)×(n+1) + c,其中m是最大的出现次数,n是冷却时间,c是出现次数等于最大出现次数的字符数;当tasks中的字符数超过这个固有容量时,只需在每次的冷却时间后继续添加字符即可,此时需要的时间就是tasks的大小,所以最后的结果应该是max(len(tasks), (m-1)×(n+1) + c)

class Solution:
    def leastInterval(self, tasks: List[str], n: int) -> int:
        counter = collections.Counter(tasks)
        m = 0  # 最大任务数量
        c = 0  # 最大任务数量的个数
        for i in counter.values():
            if i > m:
                m = i
                c = 1
            elif i == m:
                c += 1
        return max(len(tasks), (m-1)*(n+1) + c)

时:O(N) 空:O(X) 其中N为tasks的大小, X为tasks中字符种类数(最大为26)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值