leetcode刷题-- 2. 排序(待更新)

排序

参考五分钟学算法

复杂度比较

时间复杂度

  • O(n2) 各种简单的排序:直接插入、直接选择、冒泡
  • O(nlog2n) 快速排序、堆排序、归并排序
  • O(n1+(lambda)),希尔排序
  • 线性阶O(n)排序,基排序、桶、箱排序

稳定性

  • 稳定排序:冒泡、插入、归并、基数排序
  • 不稳定:选择、快速、希尔、堆排序

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

选择排序

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

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

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

例题

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

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]<基准交换他们,最终可确定基准所在位置。这道题每次都舍弃一部分数组,将符合条件的放入buffer中,减少了运算,也是类似的思想。

例如:求前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

单向


单向扫描,只有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
原文地址:https://www.cnblogs.com/ivan-blog/p/12344819.html