📌  相关文章
📜  排序数组所需的最小交换次数套装2(1)

📅  最后修改于: 2023-12-03 15:39:49.927000             🧑  作者: Mango

排序数组所需的最小交换次数套装2

当我们需要对一个数组进行排序时,我们通常会使用各种排序算法,如冒泡排序、插入排序、归并排序等。这些算法的本质都是将数组中的元素按照一定的规则进行交换或移动,以使其最终成为有序的数组。

然而,在实际操作中,我们往往需要尽可能地减少元素间的交换次数,以提高算法的效率。因此,我们可以通过一些特殊的技巧或优化策略,来实现排序数组所需的最小交换次数。

1. 基于选择排序的优化

选择排序是一种简单但效率较低的排序算法,其核心思想是每次选择未排序区域中的最小值,并将其与当前未排序区域的第一个元素交换位置,以此类推,直到整个数组有序。

在选择排序的基础上,我们可以通过以下优化策略来减少交换次数:

  • 记录未排序区域中的最小值和最大值,然后同时进行交换,从而减少元素间的交换次数。
  • 在选择最小值和最大值时,可以同时选择两个数,从而减少遍历数组的次数。
def min_swap(arr):
    n = len(arr)
    m = {val:idx for idx,val in enumerate(arr)}
    arr.sort()
    ans = 0
    vis = [False]*n
    
    for i in range(n):
        if vis[i] or m[arr[i]] == i:
            continue
        cycle_size = 0
        j = i
        
        while not vis[j]:
            vis[j] = True
            j = m[arr[j]]
            cycle_size += 1
        
        ans += (cycle_size - 1)
        
    return ans
2. 基于归并排序的优化

归并排序是一种高效的排序算法,其核心思想是将原始数组不断“分”、“排”和“合”,以达到最终排序的目的。

在归并排序的基础上,我们可以通过以下优化策略来减少交换次数:

  • 在归并排序的合并过程中,当已知左子数组中当前元素arr[i]比右子数组中当前元素arr[j]大时,直接将arr[j]之前的所有元素后移一位,并将arr[i]插入到arr[j]的位置上,从而省去了多次交换的过程。
  • 在归并排序的递归过程中,当子数组长度小于某个阈值时,采用插入排序,从而减少递归深度和子数组之间的交换次数。
def merge(arr,l,m,r):
    n1 = m - l + 1
    n2 = r - m
    
    L = [0]*(n1)
    R = [0]*(n2)
    
    for i in range(n1):
        L[i] = arr[l + i]
    for j in range(n2):
        R[j] = arr[m + 1 + j]
    
    i = 0
    j = 0
    k = l
    res = 0
    
    while i < n1 and j < n2:
        if L[i] <= R[j]:
            arr[k] = L[i]
            i += 1
        else:
            arr[k] = R[j]
            j += 1
            res += (n1 - i)
        k += 1
    
    while i < n1:
        arr[k] = L[i]
        i += 1
        k += 1
    
    while j < n2:
        arr[k] = R[j]
        j += 1
        k += 1
    
    return res

def mergeSort(arr,l,r):
    res = 0
    
    if l < r:
        m = (l + r) // 2
        
        res += mergeSort(arr,l,m)
        res += mergeSort(arr,m + 1,r)
        
        res += merge(arr,l,m,r)
    
    return res

def min_swap(arr):
    return mergeSort(arr,0,len(arr) - 1)
3. 基于快速排序的优化

快速排序是一种常用的排序算法,其核心思想是将待排序数组分为两个部分,其中一部分的所有元素均小于另一部分的元素,然后对这两部分进行递归排序,最终得到有序数组。

在快速排序的基础上,我们可以通过以下优化策略来减少交换次数:

  • 在快速排序的划分过程中,采用三路快排的方法,将数组划分为小于基准元素、等于基准元素和大于基准元素的三部分,从而避免了相同元素的频繁交换。
  • 在快速排序的划分过程中,当子数组长度小于某个阈值时,采用插入排序,从而减少递归深度和子数组之间的交换次数。
def swap(arr,i,j):
    tmp = arr[i]
    arr[i] = arr[j]
    arr[j] = tmp

def insertSort(arr,l,r):
    for i in range(l + 1,r + 1):
        tmp = arr[i]
        j = i - 1
        
        while j >= l and arr[j] > tmp:
            arr[j + 1] = arr[j]
            j -= 1
        
        arr[j + 1] = tmp

def partition(arr,l,r):
    p = arr[l]
    i = l + 1
    lt = l
    gt = r
    
    while i <= gt:
        if arr[i] < p:
            swap(arr,i,lt)
            i += 1
            lt += 1
        elif arr[i] > p:
            swap(arr,i,gt)
            gt -= 1
        else:
            i += 1
    
    swap(arr,l,lt - 1)
    
    return lt,gt

def quickSort(arr,l,r):
    if r - l <= 15:
        insertSort(arr,l,r)
        return
    
    lt,gt = partition(arr,l,r)
    quickSort(arr,l,lt - 1)
    quickSort(arr,gt + 1,r)

def min_swap(arr):
    n = len(arr)
    m = {val:idx for idx,val in enumerate(arr)}
    arr.sort()
    ans = 0
    vis = [False]*n
    
    for i in range(n):
        if vis[i] or m[arr[i]] == i:
            continue
        cycle_size = 0
        j = i
        
        while not vis[j]:
            vis[j] = True
            j = m[arr[j]]
            cycle_size += 1
        
        ans += (cycle_size - 1)
        
    return ans
总结

本文介绍了三种优化排序算法的方法,分别基于选择排序、归并排序和快速排序。这些方法可以在保证排序正确性的同时,大幅减少元素间的交换次数,从而提高算法的效率。程序员在编写排序算法时,可以根据实际情况选择相应的优化策略,以达到更好的排序效果。