java 希尔排序 leetcode_leetcode刷题-- 2. 排序(待更新)

排序

复杂度比较

990497f8c640148e96b7d0f14f288dfa.png

时间复杂度

O(n2) 各种简单的排序:直接插入、直接选择、冒泡

O(nlog2n) 快速排序、堆排序、归并排序

O(n1+\(\lambda\)),希尔排序

线性阶O(n)排序,基排序、桶、箱排序

稳定性

稳定排序:冒泡、插入、归并、基数排序

不稳定:选择、快速、希尔、堆排序

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。(相等元素相对位置不变)

选择排序

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置

再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。

重复第二步,直到所有元素均排序完毕。

例题

面试题45. 把数组排成最小的数

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

示例 1:

输入: [10,2]

输出: "102"

示例 2:

输入: [3,30,34,5,9]

输出: "3033459"

思路

将数组里数字当作字符串拼接起来,nm > mn那么就交换n与m,将m移动到n的前面。选择排序:

class Solution:

def minNumber(self, nums: List[int]) -> str:

nums = [str(i) for i in nums]

for i in range(len(nums)-1):

for j in range(i+1, len(nums)):

if nums[i] + nums[j] > nums[j] + nums[i]:

nums[i],nums[j] = nums[j],nums[i]

return "".join(nums)

注意第一层循环到len(nums)-1因为受到第二层循环i+1到 len(nums)的限制。

快速排序

双向

两个指针i,j分别从左边和右边往中间扫,当nums[i]>基准,nums[j]

例如:求前k个元素(由小到大)

def quick_select_without_optimizer(arr, k):

n = len(arr)

# 如果k大于n,没啥好说的,直接返回

if k >= n:

return arr

# 缓存

buffer = []

while arr:

# 选择最后一个元素作为标杆

mark = arr.pop()

less, greater = [], []

# 遍历数组,将元素分为less和greater

for x in arr:

if x <= mark:

less.append(x)

else:

greater.append(x)

# 判断三种情况,如果相等直接返回

if len(less) == k:

return less

# 如果小于,将less存入buffer,因为它一定是答案的一部分,可以简化计算

elif len(less) < k:

buffer += less

# k要减去less的长度

k -= len(less)

arr = [mark] + greater

else:

# 如果大于,直接舍弃右边

arr = less

单向

76436b80b3b52535c480f2883645dd80.png

单向扫描,只有j从左到右扫描一遍,i指向小于基准区间的最右边,j指向大于基准区间的最左边,如上图。

public void qSort(int[] a,int begin,int end){

if(begin >= end) return;

int mid = partition(a, begin, end);

qSort(a, begin, mid - 1);

qSort(a, mid + 1, end);

}

public int partition(int[] a,int p ,int r){

int x = a[r];

int i = p - 1;

int j = p;

for(j = p; j < r; j++){

if(a[j] < x){

i++;

swap(a, i, j);

}

}

swap(a, i + 1, j);

return i + 1;

}

优化BFPRT算法

快排的问题在于,最坏情况(逆序)下时间复杂度为O(n^2)。发生这种情况问题在于初始值的选择,也就是每次基准的选择。为了解决这种情况,引入BFPRT算法(Blum、Floyd、Pratt、Rivest、Tarjan五位大牛首字母)。

步骤

判断数组元素是否大于 5 ,如果小于 5 ,对它进行排序,并返回数组的中位数

如果元素大于 5 个,对数组进行分组,每 5 个元素分成一组,允许最后一个分组元素不足 5 个。

对于每个分组,对它进行插入排序

选择出每个分组排序之后的中位数,组成新的数组

重复以上操作

因此也叫中位数的中位数算法。具体复杂度分析参考,这个算法复杂度只有O(n),当然只是寻找基准,不是排序,算是快排的准备步骤。

def bfprt(arr, l=None, r=None):

if l is None or r is None:

l, r = 0, len(arr)

length = r - l

# 如果长度小于5,直接返回中位数

if length <= 5:

arr[l: r] = insert_sort(arr[l: r])

return l + length // 2

medium_num = l

start = l

# 否则每5个数分组

while start + 5 < r:

# 对每5个数进行插入排序

arr[start: start + 5] = insert_sort(arr[start: start + 5])

arr[medium_num], arr[start + 2] = arr[start + 2], arr[medium_num] #将所有的中位数都放在数组前面

medium_num += 1

start += 5

# 特殊处理最后不足5个的情况

if start < r:

arr[start:r] = insert_sort(arr[start:r])

_l = r - start

arr[medium_num], arr[start + _l // 2] = arr[start + _l // 2], arr[medium_num]

medium_num += 1

# 递归调用,对中位数继续求中位数

return bfprt(arr, l, medium_num)

之后再调用这个函数获取基准值即可,这只是避免快排最坏情况发生,选择基准的一种做法。接着差不多:

def quick_select(arr, k):

n = len(arr)

if k >= n:

return arr

mark = bfprt(arr) # 基准获取

arr[mark], arr[-1] = arr[-1], arr[mark]

buffer = []

while arr:

mark = arr.pop()

less, greater = [], []

for x in arr:

if x <= mark:

less.append(x)

else:

greater.append(x)

if len(less) == k:

return buffer + less

elif len(less) < k:

k -= len(less)

buffer += less

arr = [mark] + greater

else:

arr = less

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值