算法设计与分析 - 主元素

 

一、主元素问题

设T[0..n-1]是n个元素的数组。对任一元素x,设S(x)={i|T[i]=x}。当|S(x)|>n/2时,称x为T的主元素。

1) 如果T中元素存在序关系,按分治策略设计并实现一个线性时间算法,确定T[0..n-1]是否有一个主元素。

2) 若T中元素不存在序关系,只能测试任意两个元素是否相等,试设计并实现一个O(nlogn)有效算法,确定T是否有一个主元素。进一步,能找到一个线性时间算法吗?

注:实现的算法要求列出足够的实验结果。

 

1)  基于分治法的线性时间求主元素算法

<!--[if !supportLists]-->   <!--[endif]-->算法思想

中位数:数列排序后位于最中间的那个数。如果一个数列有主元素,那么必然是其中位数。求一个数列有没有主元素,只要看中位数是不是主元素。

找中位数的方法:选择一个元素作为划分起点,然后用快速排序的方法将小于它的移动到左边,大于它的移动到右边。这样就将元素划分为两个部分。此时,划分元素所在位置为k。如果k>n/2,那么继续用同样的方法在左边部分找;如果k<n/2就在右边部分找;k=n/2就找到了中位元素。

根据快速排序的思想,可以在平均时间复杂度为O(n)的时间内找出一个数列的中位数。然后再用O(n)的时间检查它是否是主元素。

<!--[if !supportLists]-->   <!--[endif]-->算法实现

对应的Java程序在MajorElement.java中

----------------------------------------------------------------------------------------

判断是否是主元素的伪代码:

master(A):

      len ← length[A]

      median ← randomizedSelect(A , 0 , n - 1 , n/2); 求中位数

      cnt ← 0

      计算中位数出现次数

      for i ← 0 to len – 1

        do if A[i] = median

               then cnt ← cnt + 1

      if cnt > n/2

        then print "主元素:" +median + "出现次数:" + cnt

        else print "无主元素"

----------------------------------------------------------------------------------------

找一个序列中第k大的数伪代码

randomizedSelect(A , p , q , k):

      r ← randomizedPartition (p , q)  找出划分元素r

      if r = k

        then return A[r]

        else if r > k

         then randomizedSelect(A , p , r – 1, k)

           else randomizedSelect(A , r + 1 , q , k)

----------------------------------------------------------------------------------------

实现随机划分的伪代码:

randomizedPartition(A , p , q ):
      rand
← random(p , q)

      exchange A[rand] A[q]

      return partition(p , q)

----------------------------------------------------------------------------------------

基于快速排序思想的划分伪代码:

partition(A , p , q ):

      pivot ← A[q]

      i ← p – 1

      for j ← p to q – 1

           do if A[j] <= pivot

                then i ← i + 1

                            exchange A[i] A[j]

      exchange A[i + 1] A[q]

      return i + 1

----------------------------------------------------------------------------------------

<!--[if !supportLists]-->   <!--[endif]-->时间复杂度分析

master()中求中位数可以在平均时间复杂度为O(n)的时间内完成,检查中位数是否是主元素耗时O(n),所以时间复杂度为O(n)。

 

2) 无序关系时求主元素的O(nlgn)的算法

<!--[if !supportLists]-->   <!--[endif]-->算法思想

    若T 中存在主元素,则将T 分为两部分后,T 的主元素也必为两部分中至少一部分的主元素,因此可用分治法。

将元素划分为两部分,递归地检查两部分有无主元素。算法如下:

a. 若T 只含一个元素,则此元素就是主元素,返回此数。

      b. 将T 分为两部分T1 和T2(二者元素个数相等或只差一个),分别递归调用此方法求其主元素m1 和m2。

      c. 若m1 和m2 都存在且相等,则这个数就是T 的主元素,返回此数。

d. 若m1 和m2 都存在且不等,则分别检查这两个数是否为T 的主元素,若有则返回此数,若无则返回空值。

e. 若m1 和m2 只有一个存在,则检查这个数是否为T 的主元素,若是则返回此数,若否就返回空值。

f. 若m1 和m2 都不存在,则T 无主元素,返回空值。

<!--[if !supportLists]-->   <!--[endif]-->算法实现

相应的Java程序在MasterElement.java中

-----------------------------------------------------------------------------------------

O(nlgn)的算法伪代码:

求T[p..q]中的主元素。返回主元素及其出现次数或空(表示无主元素)

CheckMaster(T , p , q):

      if p ← q

           then return T[p] and 1

      len ← q – p + 1

      r ← p + len / 2

      a and numa ← CheckMaster(T , p , r – 1)

      b and numb ← CheckMaster(T , r , q)

     

      if a = NIL and b = NIL

           then return NIL

      if a = NIL and b ≠ NIL

    then return CheckAnotherPart(T , len , p , r – 1 , b , numb)

      if a ≠ NIL and b = NIL

        then return CheckAnotherPart(T , len , r , q , a , numa)

      if a ≠ NIL and b ≠ NIL

then if a = b

         then numa ← numa + numb

         return a and numa

          else re ← CheckAnotherPart(T , len , p , r – 1 , b ,numb)

               if re ≠ NIL

             then return re

     else return CheckAnotherPart(T, len, r, q, a, numa)

-----------------------------------------------------------------------------------------

检查候选主元素是否是主元素

CheckAnotherPart(T , len , p , q , c , numc):

      numc ← CheckNum(T , p , q , c) + numc

      if num > len/2

           then return c and numc

           else return NIL

-----------------------------------------------------------------------------------------

----------------------------------------------------------------------------------------

计算T[p..q]中element出现的次数

CheckNum( T , p , q , element):

      cnt ← 0

      for i ← p to q

           do if T[i] = element

              then cnt ← cnt + 1

      return cnt

----------------------------------------------------------------------------------------

<!--[if !supportLists]-->   <!--[endif]-->时间复杂度分析

T(n)=2T(n/2)+n  

所以时间复杂度为O(nlgn)

 

3)无序关系时求主元素的O(n)算法

<!--[if !supportLists]-->   <!--[endif]-->算法思想

在一个集合中,删除两个不同的数,则集合的主元素保持不变。根据这个原理,可以很快求出主元素。

<!--[if !supportLists]-->   <!--[endif]-->算法实现

-------------------------------------------------------------------------------------

相应的Java程序在MainElement.java中

master(A):

      n ← length[A]

      count ← 1

      seed ← A[0]

      找候选主元素

      for i ← 1 to n – 1

           do if A[i] = seed

                   then count ← count + 1

                   else if count > 0

                         then count ← count – 1

                        else seed ← A[i]

      查找候选主元素是否是主元素

      count ← 0

      for i ← 0 to n – 1

        do if A[i] = seed

         then count ← count + 1

      if count > n/2

        then return seed and count

        else return NIL

-------------------------------------------------------------------------------------

<!--[if !supportLists]-->   <!--[endif]-->时间复杂度分析

时间复杂度为O(n)

 

 

4)算法测试

对前面三个求主元素算法,使用测试数据进行测试:

测试数组

结果

0,0,1,1,0,8,1,1,1

主元素:1出现次数:5

13,17,26,3,5,2,17,3

无主元素

1,2,3,4,5,6,7,8

无主元素

1,0,0,1,0,2,0

主元素:0 出现次数:4

1,3,4,1,2,1,1,4,0,1,1,1

主元素:1 出现次数:7

0,1,1

主元素:1 出现次数:2

13,17,26,3,5,2,17,3,3,3,3,3,3

主元素:3 出现次数:7

100,58

无主元素

597

主元素:597 出现次数:1

6,1,2,2,2,3,5

无主元素

7,7,7,7,7,7

主元素7  出现次数:6

5,9,45,96,77,28,13

无主元素

 原博客出处:

http://wintys.blog.51cto.com/425414/100688
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值