排序

冒泡排序:依次比较相邻元素,进行len-1次冒泡,第K次冒泡将倒数第k个元素排好序

for i in range(len(s)):for j in range(len(s)-i):if s[j]>s[j+1]:s[j],s[j+1]=s[j+1],s[j]

快速排序: 递归加分治 215
不稳定排序,中间交换过程会打乱顺序
时间复杂度:最差O(N^ 2),平均O(N^logN)
取中间值作为基准,数列从第一个值开始比较,小于基准值放入左边分区,大于基准值放入右边分区
然后依次对左右两个分区进行再分区,直到最后只有一个元素
分解完成再一层一层返回,返回规则是:左边分区+基准值+右边分区

def quick_sort(nums):if len(nums)<2:return numsleft,right=[],[]mid=nums[len(nums)//2]# 从原始数组中移除基准值nums.remove(mid)for num in nums:if num<mid:left.append(num)else:right.append(num)return quick_sort(left)+[mid]+quick_sort(right)

算法导论中的快排
快排实现原地排序,期望时间复杂度只有O(nlogn)。其中n是数组的长度。在最不差的情况下,快排的时间复杂度会退化到O(n2). 但是这种情况出现的概率很小
1.选取关键字,2.将关键字放在正确的位置上:关键字左边的数都比它小,右边的数都比它大。这样,这个关键字在接下来的排序中都不会改变位置。
i是一个关键的位置:i是比值关键字A[r]小的序列的末尾位置。i+1则是比A[r]大的数的区间的最左边的位置。i从整个区间最左端p-1开始不断增加。在整个过程结束之后,A[r]会和A[i+1]交换,就是和比A[r]大的序列的最左边的数交换,来达到A[r]的正确位置。(h步中的8和4交换)
A[j]小于等于x时,要讲A[j]交换到前面,因为【i+1,j】是比关键字大的区间,所以i的值增加,同时交换此时i和j的值。但是当A[j]的值大于x时,这个位置上的数不应该在x的左边区间,因此i不动,j继续右移。[i+1,j]就是比关键字大的数的区间。[p,i]是比关键字x小的区间
因为选取的关键字A[r]在区间的最右边,这个位置应该放比关键字大的数。因此,最后一步是将区间[i+1,j]的最左边的位置上的数值和关键字交换。
快排保证了每次排序关键字的位置一定正确,关键字左侧数据小于关键字,但顺序未知,关键字右侧数据大于关键字,顺序未知
下面代码是是升序有序,就是右边的数大于等于左边的数,这里由array[j] <= x决定。如果想要非增序排序,则i向右移动的条件是array[j] >= x
快排可以跳过一些元素的比较,在对一个区间进行划分之后,形成了两个区间。前一个区间里的数是没有必要和后面区间里面的数比较的。划分完成之后,不仅给关键字找到了最终的位置,而且还隔离了比较域。
https://blog.csdn.net/uestc_my/article/details/45600499?utm_source=copy

def quick_sort(array, l, r):if l < r:#一趟排序后,q左边的值都比q小,q右边的值都比q大q = partition(array, l, r)quick_sort(array, l, q - 1)quick_sort(array, q + 1, r)#原地排序,不需要返回值
def partition(array, l, r):
#选择区间尾作为基准x = array[r]i = l - 1for j in range(l, r):if array[j] <= x:#当J位置值比基准值小时,i,j一起移动i += 1array[i], array[j] = array[j], array[i]array[i + 1], array[r] = array[r], array[i+1]return i + 1

归并排序
O(N^logN)
首先归并排序使用了二分法,归根到底的思想还是分而治之。拿到一个长数组,将其不停的分为左边和右边两份,然后以此递归分下去。然后再将她们按照两个有序数组的样子合并起来
将其分割成只剩单一元素,在进行比较合并

#合并def merge(l,r):i,j=0,0c=[]while i<len(l) and j<len(r):if l[i]<r[j]:c.append(l[i])i+=1else:c.append(r[j])j+=1if i<len(l):c.extend(l[i:])if j<len(r):c.extend(r[j:])return c
#分割def merge_sort(nums):if len(nums)<2:return numsmid=len(nums)//2left=merge_sort(nums[:mid])right=merge_sort(nums[mid:])return merge(left,right)

桶排序
分桶,合并
1.基数排序
2.次数排序
统计每一个数字出现的次数,输出次数次即可
桶排序也叫计数排序,简单来说,就是将数据集里面所有元素按顺序列举出来,然后统计元素出现的次数。最后按顺序输出数据集里面的元素。

比较占用内存,如果数组范围较大,那么需要申请一个很大的空间,但是速度很快

def bucketSort(nums):# 选择一个最大的数max_num = max(nums)# 创建一个元素全是0的列表, 当做桶bucket = [0]*(max_num+1)# 把所有元素放入桶中, 即把对应元素个数加一for i in nums:bucket[i] += 1# 存储排序好的元素sort_nums = []# 取出桶中的元素for j in range(len(bucket)):if bucket[j] != 0:for y in range(bucket[j]):sort_nums.append(j)return sort_nums


912. 排序数组

快速排序:

class Solution:def sortArray(self, nums: List[int]) -> List[int]:if len(nums)<2:return numsleft,right=[],[]mid=nums[len(nums)//2]nums.remove(mid)for num in nums:if num<=mid:left.append(num)else:right.append(num)return self.sortArray(left)+[mid]+self.sortArray(right)

归并排序
时间复杂度:O(N^logN)
首先归并排序使用了二分法,归根到底的思想还是分而治之。拿到一个长数组,将其不停的分为左边和右边两份,然后以此递归分下去。然后再将她们按照两个有序数组的样子合并起来

class Solution:def sortArray(self, nums: List[int]) -> List[int]:def merge(l,r):i,j=0,0c=[]while i<len(l) and j<len(r):if l[i]<r[j]:c.append(l[i])i+=1else:c.append(r[j])j+=1if i<len(l):c.extend(l[i:])if j<len(r):c.extend(r[j:])return cdef merge_sort(nums):if len(nums)<2:return numsmid=len(nums)//2left=merge_sort(nums[:mid])right=merge_sort(nums[mid:])return merge(left,right)return merge_sort(nums)

桶排序
164. 最大间距

本题不需要真正将所有元素严格排序,只需要求出最大的间隔即可,考虑桶排序。将元素分配到不同的桶中,桶的大小一样,桶内的元素无序,且桶有序
n个元素的数组有n-1个间隔,所有元素间隔一致时的间隔是最小的最大间隔(因为缩小任意一个间隔,一定有另一个间隔增大),所以按平均分配间隔来建立桶,平均间隔为margin=max(1, (max-min)//(n-1))
桶的个数b=ceil((max-min)/margin) ceil:向上取整
桶间间隔由后面桶的最小值减去前面桶的最大值得到,跳过没有元素的桶。

如果区间里面的数字都是等间隔排列的,那最大的间隔值一定是最小的,这种情况下,任何一个数值不论增大还是减小,都只可能会造成最大间隔变大,不可能让情况变更好,把这个等间隔作为桶的大小(margin=max(1, (max-min)//(n-1))),最大间隔一定比这个桶大小要大,所以分在一个桶里面的数值之间差值不可能是最大间隔,最大间隔只可能产生在相邻的桶之间,一定值前一个桶的最大值和后一个桶的最小值的差值,所以只需要维护每一个桶里面的最大值和最小值,最后把相邻的桶遍历一遍即可,时间复杂度是线性的
通过( num-minnum)//margin判断属于哪个桶
所有元素遍历完后,比较k-1个相邻桶间距的最大值即可


对于n个元素的数组,有n+1个桶,那么至少会有一个空桶,那么最大间距一定是最大桶间间距(跳过空桶)

class Solution:def maximumGap(self, nums: List[int]) -> int:if len(nums)<2:return 0maxm,minm=max(nums),min(nums)n=len(nums)#平均间隔,用于计算每个数据属于哪个桶margin=max(1,(maxm-minm)//n-1)#桶的个数b=(maxm-minm)//margin+1# 保存桶内最小值和最大值bucke=[[float('inf'),float('-inf')]for i in range(b)]for num in nums:loc=(num-minm)//margin#维护桶内最大最小值bucke[loc][0]=min(num,bucke[loc][0])bucke[loc][1]=max(num,bucke[loc][1])res=float('-inf')premin=minmfor x,y in bucke:#跨过空桶if x==float('inf'):continue#后一个桶的最小值减去前一个桶的最大值res=max(res,x-premin)premin=yres=max(res,maxm-premin)return res

220 存在重复元素 III


将元素按顺序分配到桶内,分配规则是按照 nums[i] // (t + 1),桶的大小是t,同一个桶内元素最大差值是t,桶间元素最大差值是2*t+1,
0-t t+1-2t+1 2t+2-3t+2 …
例如 t=0,说明一个元素一个桶
如果命中同一个桶,则直接返回True
如果命中相邻桶,判断差值是否<=t
否则返回False

由于题目有索引相差k的要求,因此要维护一个大小为k的窗口,定期清除桶中过期的数字
哈希表的key作为桶的编号,Value是元素

class Solution:def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:if t<0:return False#将元素分到桶内,桶的大小为t,则桶内数据最大差值是tbucket=defaultdict(list)for i in range(len(nums)):loc=nums[i]//(t+1)#说明桶内已经有数据,符合条件索引差小于等于k,元素差小于等于t,返回Trueif loc in bucket:return True #桶内没有元素,则比较相邻桶之间数据差是否满足要求if loc-1 in bucket and abs(bucket[loc-1]-nums[i])<=t:return Trueif loc+1 in bucket and abs(bucket[loc+1]-nums[i])<=t:return Truebucket[loc]=nums[i]#定期删除索引范围差超过K的元素,为了下一次循环准备桶,保证bucket中最多只有k个桶(key)if i>=k:bucket.pop(nums[i-k]//(t+1))return False

451. 根据字符出现频率排序

按照元素出现次数分桶,出现次数相同的放到一个桶里,从大编号(出现次数多的)桶到小编号桶输出

class Solution:def frequencySort(self, s: str) -> str:n=len(s)res=[]bucket=[[] for i in range(n+1)]map=defaultdict(int)for i in s:map[i]+=1# 按照字符出现次数分桶,桶内元素是字符for i in map:bucket[map[i]].extend(i*map[i])#按照桶由大到小输出for m in bucket[::-1]:if m:res.extend(m)return ''.join(res)

归并排序
能够看到非常明显效果的阶段性排序结果
面试题51. 数组中的逆序对

在归并算法中,左右子序列都是有序序列,因此,当归并时,出现左子序列的某一元素
大于右子序列的某一元素时,说明左子序列剩下的所有元素都比右子序列的该元素大,因此
在归并中就可得到所有跨界逆序对数,即 len(left)-i ,只有当右子序列指针需要移动时才计算(说明次数右子序列j的元素小于左子序列i)
右边元素归并回去的时候,将左边元素还没归并回去的数目加到计数器变量上
计算逆序对个数发生在合并时候

class Solution:def reversePairs(self, nums: List[int]) -> int:if len(nums)<=1:return 0def merge(left,right):i,j=0,0m,n=len(left),len(right)tmp=[]while i<len(left) and j<len(right):if left[i]<=right[j]:tmp.append(left[i])i+=1else:#当右边小于左边元素时,更新逆序对数量self.count+=(m-i)tmp.append(right[j])j+=1if i<m:tmp.extend(left[i:])if j<n:tmp.extend(right[j:])return tmpdef merge_sort(nums):if len(nums)<2:return numsmid=len(nums)//2left=merge_sort(nums[:mid])right=merge_sort(nums[mid:])return merge(left,right)self.count=0merge_sort(nums)return self.count

方法同理,只是每次修改Nums,虽然速度并没有起来,

class Solution:def reversePairs(self, nums: List[int]) -> int:self.cnt = 0def merge(nums, start, mid, end, temp):i, j = start, mid + 1while i <= mid and j <= end:if nums[i] <= nums[j]:temp.append(nums[i])i += 1else:self.cnt += mid - i + 1temp.append(nums[j])j += 1while i <= mid:temp.append(nums[i])i += 1while j <= end:temp.append(nums[j])j += 1for i in range(len(temp)):nums[start + i] = temp[i]temp.clear()def mergeSort(nums, start, end, temp):if start >= end: returnmid = (start + end) >> 1mergeSort(nums, start, mid, temp)mergeSort(nums, mid + 1, end, temp)merge(nums, start, mid,  end, temp)mergeSort(nums, 0, len(nums) - 1, [])return self.cnt

315. 计算右侧小于当前元素的个数 类似上题

降序排序,当左边元素i大于右边元素j时,说明i大于剩下的len(right)-j个元素
为了保证排序过程中索引变换导致数据相加错误,用索引数组排序,不用原始数组

class Solution:def countSmaller(self, nums: List[int]) -> List[int]:self.count=[0 for i in range(len(nums))]def merge(left,right):i,j=0,0m,n=len(left),len(right)tmp=[]while i<m and j<n:if nums[left[i]]>nums[right[j]]:self.count[left[i]]+=n-jtmp.append(left[i])i+=1else:tmp.append(right[j])j+=1if i<m:tmp.extend(left[i:])if j<n:tmp.extend(right[j:])return tmpdef merge_sort(nums):if len(nums)<2: return numsmid=(len(nums))//2left=merge_sort(nums[:mid])right=merge_sort(nums[mid:])return merge(left,right)list=[i for i in range(len(nums))]merge_sort(list)return self.count

327. 区间和的个数

前缀和+归并排序
在排序过程中,每次将left和right列表合并前,为left中的每一个元素确定right中符合条件的值的范围。也就是说在right中设两个指针lo和up,其中lo指向right中第一个使right[lo] - left[i] >= lower的元素,而up指向right中最后一个使right[up] - left[i] <= upper的元素,将up-lo加到最后的结果上即可
由于可能存在重叠的数据,所以对于下界我们找左插入点,上界我们找右插入点。
归并排序的前提是合并时候,两边都是有序的,所以可以根据上下界求区间
代码根据这个写的
https://leetcode-cn.com/problems/count-of-range-sum/solution/327qu-jian-he-de-ge-shu-ti-jie-zong-he-by-xu-yuan-/



class Solution:def countRangeSum(self, nums: List[int], lower: int, upper: int) -> int:def merge_sort(nums):if len(nums)<2:return numsmid=len(nums)//2left=merge_sort(nums[:mid])right=merge_sort(nums[mid:])return merge(left,right)def merge(left,right):i,j,lo,up=0,0,0,0m,n=len(left),len(right)#固定左边i,求右边jfor le in range(m):while lo<n and right[lo]-left[le]<lower:lo+=1while up<n and right[up]-left[le]<=upper:up+=1self.count+=up-lotmp=[]while i<m and j<n:if left[i]<=right[j]:tmp.append(left[i])i+=1else:tmp.append(right[j])j+=1if i<m:tmp.extend(left[i:])if j<n:tmp.extend(right[j:])return tmpself.count=0#求前缀和s=[0]*(len(nums)+1)for i in range(1,len(nums)+1):s[i]=s[i-1]+nums[i-1]merge_sort(s)return self.count

493. 翻转对

同找逆序对,用归并排序,在合并的时候进行翻转对的判断和数量相加
时间复杂度:O(Nlog(n))
空间复杂度:O(N)

class Solution:def reversePairs(self, nums: List[int]) -> int:self.count=0def merge(nums):if len(nums)<2:return numsmid=len(nums)//2left=merge(nums[:mid])right=merge(nums[mid:])return merge_sort(left,right)def merge_sort(left,right):i,j=0,0tmp=[]m,n=len(left),len(right)r=0for l in range(m):#固定i,右移符合条件的j,即找到最右边的r,符合left[l]>2*right[r]while r<n and left[l]>2*right[r]:r+=1self.count+=rwhile i<m and j<n:if left[i]<right[j]:tmp.append(left[i])i+=1else:tmp.append(right[j])j+=1if i<m:tmp.extend(left[i:])if j<n:tmp.extend(right[j:])return tmpmerge(nums)return self.count

148 排序链表

nlog(n):要求归并排序

分割:找到链表中点处断开,使用快慢指针,奇数个节点找到中点,偶数个节点找到中点左边的节点,找到中点后,设置右端头结点,同时断开左右两段链表,
递归分割时,输入当前链表左端点 head 和中心节点 slow 的下一个节点 tmp(因为链表是从 slow 切断的)。
cut 递归终止条件: 当head.next == None时,说明只有一个节点了,直接返回此节点
合并:谁小接谁
返回:头结点

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = Noneclass Solution:def sortList(self, head: ListNode) -> ListNode:if not head or not head.next:return head#找中点fast,slow=head.next,headwhile fast and fast.next:fast,slow=fast.next.next,slow.nextmid=slow.nextslow.next=Noneleft=self.sortList(head)right=self.sortList(mid)tmp=dummy=ListNode(0)while left and right:if left.val<right.val:tmp.next=leftleft=left.nextelse:tmp.next=rightright=right.nexttmp=tmp.nexttmp.next=left if left else rightreturn dummy.next

215 数组中第K个最大元素

O(nlogn) 利用快速排序,每次判断基准值位置是不是第k个,利用非递增顺序排列,不是的话改变左右区间 边界,每次都缩小第k大数所在区间的边界

class Solution:def findKthLargest(self, nums: List[int], k: int) -> int:#快速排序 倒着排def partition(nums,l,r):pivort=nums[r]i=l-1for j in range(l,r):# 非递增排序,将大的值换到前面俩if nums[j]>=pivort:i+=1nums[i],nums[j]=nums[j],nums[i]#选取的关键字在区间的最右边,这个位置应该放比关键字小的数字,因此最后交换i+1和r位置的值nums[i+1],nums[r]=nums[r],nums[i+1]return i+1l,r=0,len(nums)-1while True:p=partition(nums,l,r)#如果关键字位置就是第k个元素,因为是降序排序,所以是第K大元素if p+1==k:return nums[p]# 说明p前面大的数不足k个,因为第k大元素去P的右侧区间找elif p+1<k:l=p+1else:r=p-1

219. 存在重复元素 II

class Solution:def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:if k==0:return False#滑动窗口大小为Khash=defaultdict(int)for r in range(len(nums)):if nums[r] in hash:return Trueif r>=k:hash.pop(nums[r-k])hash[nums[r]]=rreturn False

217. 存在重复元素

class Solution:def containsDuplicate(self, nums: List[int]) -> bool:hash=set()for i in range(len(nums)):if nums[i] in hash:return Truehash.add(nums[i])return False

5.25 力扣 排序(冒泡、快速、桶) 二分相关推荐

  1. 排序及查找----[(冒泡,快速)(拉格朗日,二分)]

    代码展示: 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; ...

  2. 导图整理数组1: 总结了二分查找的通用模板写法, 彻底解决几个易混淆问题, 力扣35:搜索插入位置

    此专栏文章是对力扣上算法题目各种方法的总结和归纳, 整理出最重要的思路和知识重点并以思维导图形式呈现, 当然也会加上我对导图的详解. 目的是为了更方便快捷的记忆和回忆算法重点(不用每次都重复看题解), ...

  3. 《LeetCode力扣练习》剑指 Offer 25. 合并两个排序的链表 Java

    <LeetCode力扣练习>剑指 Offer 25. 合并两个排序的链表 Java 一.资源 题目: 输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的. 示例1: ...

  4. python旋转排序数组_LeetCode(力扣)——Search in Rotated Sorted Array 搜索旋转排序数组 python实现...

    题目描述: python实现Search in Rotated Sorted Array 搜索旋转排序数组 中文:假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2, ...

  5. 第 254 场力扣周赛(KMP、贪心、快速幂、二分+多源bfs、并查集 + 时光倒流)

    第 254 场力扣周赛 稀里糊涂双眼双眼惺忪的做了三道,错了4次...还是600来名 5843. 作为子字符串出现在单词中的字符串数目 题目描述 给你一个字符串数组 patterns 和一个字符串 w ...

  6. 《LeetCode力扣练习》第33题 搜索旋转排序数组 Java

    <LeetCode力扣练习>第33题 搜索旋转排序数组 Java 一.资源 题目: 整数数组 nums 按升序排列,数组中的值 互不相同 . 在传递给函数之前,nums 在预先未知的某个下 ...

  7. 力扣 数组的相对排序

    力扣 数组的相对排序 题目描述 给你两个数组,arr1 和 arr2, arr2 中的元素各不相同 arr2 中的每个元素都出现在 arr1 中 对 arr1 中的元素进行排序,使 arr1 中项的相 ...

  8. 力扣 根据数字二进制下1的数目排序

    力扣 根据数字二进制下1的数目排序 题目描述 给你一个整数数组 arr .请你将数组中的元素按照其二进制表示中数字 1 的数目升序排序. 如果存在多个数字二进制中 1 的数目相同,则必须将它们按照数值 ...

  9. 20200805:Java拓扑排序实现力扣207课程表

    力扣207:课程表 题目 思路与算法 代码实现 题目 题目链接:课程表 你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 . 在选修某些课程之前需要一些先修课程. ...

最新文章

  1. 备战 ‘金三银四’ 必备超多软件测试面试题全在这里
  2. Java中的访问权限
  3. hive mysql 远程_Hive配置 远程连接MySQL
  4. Containerd迎来1.0通用版本
  5. [转]微信小程序安全浅析
  6. 获取计算机主机mac地址的命令有,怎么获取计算机的MAC地址和IP地址?
  7. springBoot(5)---单元测试,全局异常
  8. 【Computer Organization笔记07】实验课:可编程逻辑器件介绍,硬件编程方法与原则,硬件编程流程
  9. WindowsServers2019上手体验
  10. MySQL数据库设置主从同步
  11. 《Visual C++ 开发从入门到精通》——2.9 技术解惑
  12. 使用两个队列,改进耗时线程引起的性能问题的思路及代码
  13. 常用电子元件识别图解大全
  14. 微信对话生成器V4.4绿色版,自定义生成微信聊天截图软件(资源供学习参考)
  15. 汉字转拼音,并返回第一个字母
  16. RuntimeError: CUDA error: device-side assert triggered的解决
  17. 有意思的网站 - 收集
  18. [corefx注释说]-System.Collections.Generic.StackT
  19. 【P4】解决本地文件修改与库文件间的冲突问题
  20. PHP7新特性-简述

热门文章

  1. android语音识别方法二:应用程序自己调用语音识别库
  2. 实现一个全链路监控平台很难吗?
  3. 电子产品开发-模块开发计划
  4. 零信任下的应用安全网关该如何建设?
  5. Springboot智能物流拼单组合系统设计与实现
  6. OpenCV实现图像卡通化
  7. 触摸液晶屏 如何进行日常维护?
  8. IRS综述(一)Intelligent Reflflecting Surface-Aided Wireless Communications: A Tutorial
  9. javascript笔记6之函数
  10. 微信小程序自定义组件省、市区联动