NO. 912
给你一个整数数组 nums,请你将该数组升序排列。
示例 1:
输入:nums = [5,2,3,1]
输出:[1,2,3,5]
示例 2:
输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]
提示:
1 <= nums.length <= 50000
-50000 <= nums[i] <= 50000
排序算法 | 时间复杂度 | 空间复杂度 | 稳定性 | 原地排序 |
---|---|---|---|---|
冒泡排序 | O(n2) | O(1) | 稳定 | 是 |
插入排序 | O(n2) | O(1) | 稳定 | 是 |
选择排序 | O(n2) | O(1) | 不稳定 | 是 |
快速排序 | O(n logn) | O(1) | 不稳定 | 是 |
归并排序 | O(n logn) | O(n) | 稳定 | 不是 |
堆排序 | O(n logn) | O(1) | 不稳定 | 是 |
希尔排序 | O(n logn) | O(1) | 不稳定 | 是 |
桶排序 | O(n) | O(n) | 稳定 | 不是 |
计数排序 | O(n) | O(n+k) | 稳定 | 不是 |
基数排序 | O(n) | O(n) | 稳定 | 不是 |
思路:以冒泡为代表的一类排序算法,通过两层循环进行数据对比和排序,这类排序算法的时间复杂度为O(n2),常见的排序算法包括:冒泡排序、选择排序、插入排序,3个的核心思想近似。
解法二、三全部算法通过了全部测试用例,解法一时间复杂度过高,数据量大的时候会超时,有个超时的测试用例有5万个正负整数,具体看链接:https://www.zhenxiangsimple.com/files/tech/testCase20200331.txt
# author:[email protected]
class Solution:#冒泡排序
def sortArray(self, nums: List[int]) -> List[int]:
numLen = len(nums)
for i in range(0,numLen):
flag = True #提前退出冒泡循环的标志位
for j in range(0,numLen-1-i):
if nums[j] > nums[j+1]: #比较相邻元素,顺序不满足时,交换位置
nums[j],nums[j+1] = nums[j+1],nums[j]
flag = False #存在数据交换
if flag:
break
return nums
# author:[email protected]
class Solution:#插入排序
def sortArray(self, nums: List[int]) -> List[int]:
numLen = len(nums)
for i in range(1,numLen):# 从前往后表示有序数据放前面
j, tmpVal = i-1, nums[i]
while j >= 0 and nums[j] > tmpVal:#查找插入位置
nums[j+1] = nums[j] # 数据向后移动
j -= 1
nums[j+1] = tmpVal
return nums
# author:[email protected]
class Solution: #选择排序
def sortArray(self, nums: List[int]) -> List[int]:
numLen = len(nums)
for i in range(0,numLen):
minIdx, tmpVal = i, nums[i]
for j in range(i+1, numLen): #查找最小值
if nums[j] < tmpVal:
minIdx, tmpVal = j, nums[j] # 找到更小值
if i != minIdx: #交换最小值和无序的第一个(有序的末尾)
nums[i], nums[minIdx] = nums[minIdx], nums[i]
return nums
思路:以快速排序为代表的一类排序算法,通过分治的思想实现排序,这类排序算法的时间复杂度为O(nlogn),适合大规模的数据排序,比上面的那三种排序算法更常用,常见的排序算法包括:快速排序、归并排序、堆排序、希尔排序,4个的核心思想基本近似。
# author:[email protected]
class Solution:#快速排序
# 将数组按分为大小两部分,并递归进行快排
def splitArray(self, nums, start, end):
if start >= end: #起止中间没有数据
return
splitIdx = self.getQuickIdx(nums,start, end)
self.splitArray(nums, start, splitIdx-1) # 左边
self.splitArray(nums, splitIdx+1, end) # 右边
# 获取分割数值对应的索引
def getQuickIdx(self, nums, start, end):
tmpVal = nums[end] #以最后一个作为分割数(也可以选第一个或其它)
i = start # i 标记分割数要插入的位置(即i始终指向比tmpVal大的第一个数)
#也可以从左到右找大值,从右到左找小值,然后交换
for j in range(start,end+1):
if nums[j] < tmpVal:
nums[i], nums[j] = nums[j], nums[i]
i += 1
nums[i], nums[end] = nums[end], nums[i]
return i
def sortArray(self, nums: List[int]) -> List[int]:
numLen = len(nums)
self.splitArray(nums,0,numLen-1)
return nums
# author:[email protected]
class Solution:#归并排序
# 将数组按下标递归分开为两部分
def splitArray(self, nums, start, end):
if start >= end: #起止中间没有数据
return
mid = int((start + end) / 2)
self.splitArray(nums, start, mid) # 左边
self.splitArray(nums, mid+1, end) # 右边
self.joinArray(nums, start, mid, mid+1, end)
# 将两个有序数组,合并为一个有序数组
def joinArray(self, nums, leftStart, leftEnd, rightStart, rightEnd):
leftIdx, rightIdx , idx, cnt= leftStart, rightStart, 0, rightEnd - leftStart + 1
tmpArray = [0] * cnt
# 使用两个指针分别指向两部分的最小值
while leftIdx <= leftEnd and rightIdx <= rightEnd:
if nums[leftIdx] < nums[rightIdx]:
tmpArray[idx] = nums[leftIdx]
leftIdx += 1
else:
tmpArray[idx] = nums[rightIdx]
rightIdx += 1
idx += 1
#判断哪部分仍有剩余数组
tmpStart, tmpEnd = leftIdx, leftEnd
if rightIdx <= rightEnd:
tmpStart, tmpEnd = rightIdx, rightEnd
while tmpStart <= tmpEnd:
tmpArray[idx] = nums[tmpStart]
idx , tmpStart = idx+1, tmpStart+1
# 将临时有序数据 放回原始数组
for i in range(0,cnt):
nums[leftStart+i] = tmpArray[i]
def sortArray(self, nums: List[int]) -> List[int]:
numLen = len(nums)
self.splitArray(nums,0,numLen-1)
return nums
# author:[email protected]
class Solution:#堆排序
#将原始数组 调整为堆结构
def initalHeap(self, nums,cnt):
mid = int(cnt/2) # n/2以上的都在叶子节点,不需要调整
for i in range(mid,-1,-1): #从下往上建堆
self.buildHeap(nums,cnt, i)
# 设置索引idx的堆值(最大/小值)
def buildHeap(self,nums, cnt, idx): #构建大顶堆
maxIdx = idx
if idx * 2 < cnt and nums[idx] < nums[idx * 2]:#左子树
maxIdx = idx * 2
if idx*2 + 1<cnt and nums[maxIdx] < nums[idx*2 + 1]:#右子树
maxIdx = idx*2 + 1
if maxIdx != idx: #递归 保证子树原来的堆结构
nums[maxIdx], nums[idx] = nums[idx], nums[maxIdx]
self.buildHeap(nums, cnt, maxIdx)
def sortArray(self, nums: List[int]) -> List[int]:
numLen = len(nums)
self.initalHeap(nums,numLen)
for i in range(numLen-1,0,-1): #直到剩下一个元素
#大顶堆的堆顶是最大值,放在数组末尾
nums[0], nums[i] = nums[i], nums[0]
self.buildHeap(nums,i, 0)
return nums
# author:[email protected]
class Solution:#希尔排序
def sortArray(self, nums: List[int]) -> List[int]:
numLen, gapLen = len(nums), int(len(nums)/2)
while gapLen >= 1:
for i in range(0,gapLen): #共gapLen个分组
for k in range(i,numLen,gapLen):#组内插入排序
j, tmpVal = k - gapLen, nums[k]
while j >= 0 and nums[j] > tmpVal:#插入排序:寻找插入点
nums[j+gapLen] = nums[j]
j -= gapLen
nums[j + gapLen] = tmpVal
gapLen = int(gapLen/2) #间隔减半
return nums
思路:前面两类排序算法都是基于比较实现排序,当前以桶排序为代表的一类排序算法,都不涉及元素之间的比较,这类排序算法的时间复杂度为O(n),适合用在有特殊属性的数据的排序,常见的排序算法包括:桶排序、计数排序、基数排序,3个的核心思想近似。
计数排序可以当作桶排序的一种特例,对桶内的数据进行排序即为桶排序,桶内数据的排序可以使用快排或归并排序,甚至可以继续使用桶排序进行递归,本算法使用了python内置的排序。
当前桶数量选择了可以覆盖所有数值的数量,也可以选择其它数量,只要能够将数据按顺序映射到各个桶内即可。
# author:[email protected]
class Solution:#桶排序
def sortArray(self, nums: List[int]) -> List[int]:
numLen, numMin, numMax = len(nums), min(nums), max(nums)
bucketCnt = numMax - numMin +1 #桶的数量可以覆盖所有数值
buckets = [[] for _ in range(0, bucketCnt)] #构建桶
for num in nums:
buckets[num - numMin].append(num)
for i in range(0, bucketCnt):# 桶内排序
buckets[i].sort()
return [valArray[i] for valArray in buckets for i in range(0,len(valArray))]
# author:[email protected]
class Solution:#计数排序
def sortArray(self, nums: List[int]) -> List[int]:
numLen, numMin, numMax = len(nums), min(nums), max(nums)
tmpBucket = [0] * (numMax-numMin + 1) # 申请k个桶
# 对元素进行计数(放入桶中)
for num in nums:
tmpBucket[num-numMin] += 1
for i in range(1,numMax-numMin + 1):#把计数进行累加
tmpBucket[i] += tmpBucket[i-1]
tmpArray = [0] * numLen
for i in range(0,numLen):#直接将数据放入累计数量对应的索引
tmpArray[tmpBucket[nums[i]-numMin] - 1] = nums[i]
tmpBucket[nums[i]-numMin] -= 1
nums = tmpArray
return nums
# author:[email protected]
class Solution:#基数排序
def sortArray(self, nums: List[int]) -> List[int]:
numLen, numMin = len(nums), min(nums)
nums = [num - numMin for num in nums] #兼容负值
numMax = max(nums)
i, j = 0, len(str(numMax)) #记录最高位数
while i < j: #从低到高,对所有位数进行排序
tmpBitArray = [[] for _ in range(10)]
for num in nums:
tmpBitArray[int(num / (10**i)) % 10].append(num)
nums.clear()# 清除原数组
for bitArray in tmpBitArray:
for val in bitArray:
nums.append(val)#使用有序子序列恢复原数据
i += 1
return [num + numMin for num in nums]