使用Hoare's Partition和Lomuto Partition将偶数移到数组前面的方法

273 阅读4分钟

在这篇文章中,我们解释了使用Hoare's Partition和Lomuto Partition将偶数移到数组前面的两种有效方法。

内容表

  1. 问题陈述
  2. 方法1:使用Hoare分割法
  3. 方法2:使用Lomuto分割法

让我们来了解并解决这个问题。

问题陈述

给定一个正数的数组,以这样的方式划分数组,使所有的偶数都在数组的前面。

例子

Input : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
Output: {2, 4, 6, 8, 10, 1, 3, 5, 7, 9}

注意:偶数和奇数的相对顺序并不重要

Another Solution:
{2, 6, 4, 10, 8, 1, 3, 7, 9, 5}
Following Outputs are wrong.
{1, 3, 5, 7, 9, 2, 4, 6, 8, 10}
{2, 1, 4, 3, 6, 5, 8, 7, 10, 9}

这个问题类似于隔离0和1,可以用荷兰国旗算法的变化来解决。

各种分区方案

这个问题可以用一个分区方案来解决,比如:

  1. 霍尔分割法
  2. Lomuto分区

方法1:使用Hoare分区法

它是基于双指针法,指针start和end被初始化为数组的两端,并相互移动,直到发生倒置,奇数在左边,偶数在右边。在反转时进行交换操作,这个过程重复进行,直到起点小于终点。

算法/步骤

  1. 初始化i = low,j = high。
  2. 将i递增1,直到i处出现的元素是偶数。
  3. j减1,直到i处的元素是奇数。
  4. 将i处的元素与j处的元素交换。
  5. 重复步骤2-4,直到i小于j。

代码

def segregateEvenOdd(arr):
    j = 0
    i = len(arr) - 1
    
    while i < j:
        while i<len(arr) and arr[i]%2 == 0:
            i = i + 1
        
        while j>=0 and arr[j]%2 != 0:
            j =  j - 1
        
        if i < j :
            swap(arr[i],arr[j])  

例子

输入:{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

进行的交换:0

1 2 3 4 5 6 7 8 9 10
|                  |
i                  j

已执行的互换:0 + 1 = 1

10 2 3 4 5 6 7 8 9 1
|                  |
i                  j

已执行的互换:1

10 2 3 4 5 6 7 8 9 1
     |             |
     i             j

已执行的互换:1

10 2 3 4 5 6 7 8 9 1
     |         |
     i         j

已执行的互换:1 + 1 = 2

10 2 8 4 5 6 7 3 9 1
     |         |
     i         j

已执行的互换:2

10 2 8 4 5 6 7 3 9 1
         |     |
         i     j

已执行的互换:2

10 2 8 4 5 6 7 3 9 1
         | |
         i j

执行的互换操作:2 + 1 = 3

10 2 8 4 6 5 7 3 9 1
         | |
         i j
END

比较操作的数量较多,但交换操作较少。因此,它是更有效的方法,但与Lomuto分区相比,很难理解。

时间复杂度:O(N),其中N是数组的长度

空间复杂度::O(1)

方法2:使用Lomuto分区

我们不从两端移动指针,而是从数组的低位置移动到高位置。当我们在数组中迭代时,我们用滑动目标索引交换偶数元素。滑动目标索引是一个分隔偶数和奇数元素的分区索引,最初它指向-1。

算法/步骤

  1. 初始化sliding_index = low - 1,因为最初没有偶数元素存在于左边的子阵列中,i = low。
  2. 检查i处存在的元素是偶数还是奇数。
  3. 如果是偶数,则将sliding_index增加1,并将sliding_index和i处的元素交换。
  4. 否则,不做任何操作。
  5. 将i增加1。
  6. 重复步骤2-5,直到i小于或等于高。

代码

def segregateEvenOdd(arr):
    sliding_indx = -1
    
    for i in range(0,len(arr)):
 
        if arr[i]%2 == 0:
            sliding_indx = sliding_indx + 1
            swap(arr[i],arr[sliding_indx])
            

例子

输入:{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
让滑动的indx为i,迭代器为j

进行交换:0

  1 2 3 4 5 6 7 8 9 10
| |
i j

已执行的互换:0

  1 2 3 4 5 6 7 8 9 10
|   |
i   j

已执行的互换:0 + 1 = 1

  2 1 3 4 5 6 7 8 9 10
  | |
  i j

已执行的互换:1

  2 1 3 4 5 6 7 8 9 10
  |   |
  i   j

已执行的互换:1

  2 1 3 4 5 6 7 8 9 10
  |     |
  i     j

已执行的互换:1 + 1 = 2

  2 4 3 1 5 6 7 8 9 10
    |   |
    i   j

已执行的互换:2

  2 4 3 1 5 6 7 8 9 10
    |     |
    i     j

已执行的互换:2

  2 4 3 1 5 6 7 8 9 10
    |       |
    i       j

已执行的互换:2 + 1 = 3

  2 4 6 1 5 3 7 8 9 10
      |     |
      i     j

已执行的互换:3

  2 4 6 1 5 3 7 8 9 10
      |       |
      i       j

已执行的互换:3

  2 4 6 1 5 3 7 8 9 10
      |         |
      i         j

已执行的互换:3 + 1 = 4

  2 4 6 8 5 3 7 1 9 10
        |       |
        i       j

已执行的交换:4

  2 4 6 8 5 3 7 1 9 10
        |         |
        i         j

已执行的交换:4

  2 4 6 8 5 3 7 1 9 10
        |           |
        i           j

已执行的互换:4 + 1 = 5

  2 4 6 8 10 3 7 1 9 5
          |          |
          i          j
END

Lomuto分区很容易实现和理解。但与Hoare分区相比,它的速度较慢,因为要进行更多的互换操作。

时间复杂度O(N),其中N是阵列的长度

空间复杂度::O(1)

通过OpenGenus的这篇文章,你一定对如何将偶数移到数组的前面有了完整的认识。