双向冒泡排序学习

  本博客主要考虑冒泡排序的扫描次数的问题。特别地,以升序排序为例,考虑冒泡排序中单向、双向的情况。启发来自洛谷的这道题

单向冒泡排序

  这是最简单的一种冒泡排序,其伪代码如下:

sorted = false
while (not sorted):
   sorted = true
   for i = 0 to N-2:
      if A[i+1] < A[i]:
         swap A[i], A[i+1]
         sorted = false

  单向冒泡排序每一轮扫描都能确定数组A的最后一个元素,动态演示如下:

图源这篇博客

  那么问题来了,如果采用上面的伪代码,已知一个序列A[1...N],如何提前得知冒泡排序需要扫描多少轮

直接模拟

  一个简单的想法是直接进行模拟。也就是设置一个计数器cnt,在while语句块的开头进行++cnt,就能够得知需要扫描多少次。然而它的时间复杂度是 O ( n 2 ) O(n^2) O(n2)

更快的方法

  我们仔细观察上面的动图,可以获得更一般性的结论:

  • 考虑序列中一个固定的数 x x x,如果它前面有比自己大的数,那么每轮扫描都将使 x x x 前面比 x x x 大的数的个数减少 1 1 1
  • 当每个数前面都没有比自己大的数时,排序完成。
  • 最好的情况给出的序列直接是升序的,至少需要扫描 1 1 1 次。

  所以,我们可以直接得到,扫描的次数 X = max ⁡ ( 1 , max ⁡ 1 ≤ i ≤ n ( ∑ j = 1 i − 1 [ a j > a i ] ) ) X=\max(1,\max_{1\leq i\leq n}(\sum_{j=1}^{i-1}[a_j>a_i])) X=max(1,max1in(j=1i1[aj>ai]))。其中 [ ] [] [] 表示取布尔表达式的值,为真值就是 1 1 1,为假值就是 0 0 0。更通俗一点就是: X = max ⁡ ( 1 , 每个数前面有几个比自己大的数 ) X=\max(1,每个数前面有几个比自己大的数) X=max(1,每个数前面有几个比自己大的数)
  这种方式可以在 O ( n ) O(n) O(n) 的复杂度内解决问题。

双向冒泡排序

  这种排序方式,每一轮扫描的方式是:先从前往后扫描一次,再从后往前扫描一次。伪代码如下:

sorted = false
while (not sorted):
   sorted = true
   for i = 0 to N-2:
      if A[i+1] < A[i]:
         swap A[i], A[i+1]
   for i = N-2 downto 0:
      if A[i+1] < A[i]:
         swap A[i], A[i+1]
   for i = 0 to N-2:
      if A[i+1] < A[i]:
         sorted = false

  固然也可以用 O ( n 2 ) O(n^2) O(n2) 的方法模拟出扫描次数,但是依然有 O ( n ) O(n) O(n) 的简单办法。

离散化

  首先要把给到的序列 { a i } ( i = 1 , 2 , ⋯   , n ) \{a_i\}(i=1,2,\cdots,n) {ai}(i=1,2,,n) 离散化。离散化可以这样理解,每个数 a i → 1 + ∑ j = 1 n [ a j < a i ] a_i\rightarrow 1+\sum_{j=1}^n[a_j<a_i] ai1+j=1n[aj<ai],中括号的含义同上。说白了就是把 a i a_i ai 改成它是序列中第几小的数。如: { 1 , 1 , 4 , 5 , 1 , 4 } → { 1 , 1 , 2 , 3 , 1 , 2 } \{1,1,4,5,1,4\}\rightarrow\{1,1,2,3,1,2\} {1,1,4,5,1,4}{1,1,2,3,1,2}

扫描序列

  注意到关于双向冒泡排序的以下结论:

  • 考虑固定的 x x x,如果离散化之后的序列中,前 x x x 个数中没有比 x x x 大的,那么这些数就被锁在了 [ 1 , x ] [1,x] [1,x] 中,不会与 [ x + 1 , n ] [x+1,n] [x+1,n] 产生直接或者间接的交换。
  • 考虑固定的 x x x,如果离散化之后的序列中,前 x x x 个数中有比 x x x 大的,那么最大的那个在正向扫描时将被移出 [ 1 , x ] [1,x] [1,x],同时反向扫描时引入 [ 1 , x ] [1,x] [1,x] 的新数必定小于等于 x x x
      记 { a i } \{a_i\} {ai} 离散化得 { b i } \{b_i\} {bi},那么扫描的次数就是 X = max ⁡ ( 1 , max ⁡ 1 ≤ i ≤ n ( ∑ j = 1 i [ b j > i ] ) ) X=\max(1,\max_{1\leq i\leq n}(\sum_{j=1}^i[b_j>i])) X=max(1,max1in(j=1i[bj>i]))。更通俗一点就是: X = max ⁡ ( 1 , 第 1 到第 i 个数中有几个比 i 大的数 ) X=\max(1,第 1 到第i个数中有几个比i大的数) X=max(1,1到第i个数中有几个比i大的数)
  • 13
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
双向链表的双向冒泡排序可以通过比较相邻节点的数据域来实现。具体步骤如下: 1. 首先,定义一个指向链表头节点的指针,命名为`current`。 2. 使用两个循环嵌套,外层循环控制比较的轮数,内层循环控制每一轮的比较次数。 3. 在内层循环中,比较`current`节点和`current.next`节点的数据域,如果前者大于后者,则交换它们的数据域。 4. 内层循环结束后,将`current`指针指向下一个节点,继续进行下一轮的比较。 5. 外层循环结束后,整个链表的数据域将按照从小到大的顺序排列。 下面是一个示例代码,演示了双向链表的双向冒泡排序: ```python class Node: def __init__(self, data): self.data = data self.prev = None self.next = None def bubble_sort(head): if head is None or head.next is None: return head end = None while end != head.next: current = head.next while current.next != end: if current.data > current.next.data: current.data, current.next.data = current.next.data, current.data current = current.next end = current return head # 创建双向链表 head = Node(4) node1 = Node(2) node2 = Node(1) node3 = Node(3) head.next = node1 node1.prev = head node1.next = node2 node2.prev = node1 node2.next = node3 node3.prev = node2 # 执行双向冒泡排序 sorted_head = bubble_sort(head) # 输出排序后的链表数据 current = sorted_head.next while current is not None: print(current.data) current = current.next ``` 输出结果为: ``` 1 2 3 4 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值