编程之美3.7——队列中取最大值操作问题

来源:互联网 发布:男加厚棉服淘宝 编辑:程序博客网 时间:2024/06/05 07:28
 

编程之美3.7——队列中取最大值操作问题

标签: 编程数据结构classstruct算法优化
 3386人阅读 评论(5) 收藏 举报
 分类:

问题:

假设有这样一个拥有3个操作的队列:

1. EnQueue(v): 将v加入队列中

2. DeQueue(): 使队列中的队首元素删除并返回此元素

3. MaxElement: 返回队列中的最大元素

设计一种数据结构和算法,让MaxElement操作的时间复杂度尽可能地低。


解法1:

用最大堆来维护队列中的节点,队列用单链表表示,每个节点包含数据,而最大堆用数组表示,数组元素为节点的指针。入队的时间复杂度为O(logn),出队的时间复杂度为O(n),达不到书上的O(logn),取最大值的时间复杂度为O(1)。

[cpp] view plain copy
  1. <span style="font-size:18px;">#include <iostream>  
  2. #include <algorithm>  
  3. #include <vector>  
  4. using namespace std;  
  5.   
  6. // 用最大堆来维护队列中的节点,队列用单链表表示,每个节点包含数据,  
  7. // 而最大堆用数组表示,数组元素为节点的指针  
  8. class Queue  
  9. {  
  10. public:  
  11.     // 模拟队列的链表节点  
  12.     struct Node  
  13.     {  
  14.         Node(int d):data(d),next(0){}  
  15.         int data;  
  16.         Node *next;  
  17.     };  
  18.   
  19.     Queue() {begin=end=0;vec.push_back(NULL);}  
  20.     // O(logn)时间内将节点加入队列  
  21.     void EnQueue(int data)  
  22.     {  
  23.         Node *nd = new Node(data);  
  24.         // 若队列中没有节点  
  25.         if (vec.size() == 1)  
  26.         {  
  27.             begin = end = 1;  
  28.             vec.push_back(nd);  
  29.             return;  
  30.         }  
  31.         // 若队列中已有节点,将其连上单链表  
  32.         vec.push_back(nd);  
  33.         vec[end]->next = nd;  
  34.         // 用shift_up在大顶堆中确定其位置  
  35.         end = vec.size()-1;  
  36.         while (end>>1 >= 1)  
  37.         {  
  38.             if (nd->data <= vec[end>>1]->data)  
  39.                 break;  
  40.             vec[end] = vec[end>>1];  
  41.             // 元素移动可能会使队列中第一个节点的位置发生改变  
  42.             if (end>>1 == begin)  
  43.                 begin = end;  
  44.             end >>= 1;  
  45.         }  
  46.         vec[end] = nd;  
  47.     }  
  48.     // O(n)时间内从队列中删去节点  
  49.     int DeQueue()  
  50.     {  
  51.         // 若队列中没有节点  
  52.         if (vec.size() == 1)   
  53.         {  
  54.             begin = end = 0;  
  55.             return 0;  
  56.         }  
  57.         // 将第一个节点删去  
  58.         int data = vec[begin]->data;  
  59.         Node *nextnd = vec[begin]->next;  
  60.         delete vec[begin];  
  61.         // 若删除的节点的位置不是vec的最后一个元素  
  62.         if (begin < vec.size()-1)  
  63.         {  
  64.             Node *nd = vec[vec.size()-1];  
  65.             vec.pop_back();  
  66.             // 执行shift_down确定vec最后一个元素在begin子树中的位置  
  67.             int nextbegin = begin<<1;  
  68.             while (nextbegin <= vec.size()-1)    // 边界约束  
  69.             {  
  70.                 // 找到两个子元素中较大的元素  
  71.                 if (nextbegin+1 <= vec.size()-1 &&   
  72.                     vec[nextbegin+1]->data > vec[nextbegin]->data)  
  73.                     nextbegin++;  
  74.                 // 若vec最后一个元素比较大元素,则它的位置是begin  
  75.                 if (nd ->data >= vec[nextbegin]->data)  
  76.                     break;  
  77.                 vec[begin] = vec[nextbegin];  
  78.                 // 元素的移动可能会使队列中最后一个节点的位置发生改变  
  79.                 if (nextbegin == end)  
  80.                     end = begin;  
  81.                 begin = nextbegin;  
  82.                 nextbegin <<= 1;  
  83.             }  
  84.             vec[begin] = nd;  
  85.             // 如果vec最后一个元素是最后一个节点的位置,则将其设为begin  
  86.             if (end >= vec.size())  
  87.                 end = begin;  
  88.         }  
  89.         else  
  90.             // 若删除的节点的位置是vec的最后一个元素  
  91.             vec.pop_back();  
  92.         // 在大顶堆中找到队列中的第二个元素耗时O(n),还需优化  
  93.         int i;  
  94.         for (i=1; i<vec.size(); ++i)  
  95.             if (vec[i] == nextnd)  
  96.                 break;  
  97.         begin = i;  
  98.         return data;  
  99.     }  
  100.   
  101.     // O(1)的时间内得到最大值元素  
  102.     int maxElement()  
  103.     {  
  104.         return vec[1]->data;  
  105.     }  
  106.   
  107. private:  
  108.     vector<Node*> vec;    // 模拟大顶堆的数组(从1开始)  
  109.     int begin, end;     // 队列的第一个节点和最后一个节点在数组中的位置  
  110. };  
  111.   
  112.   
  113. int main()  
  114. {  
  115.     const int n = 11;  
  116.     int data[] = {7, 4, 15, 9, 5, 10, 13, 3, 20, 17, 19};  
  117.     int i;  
  118.     Queue q;  
  119.     for (i=0; i<n/2; i++)  
  120.         q.EnQueue(data[i]);  
  121.     int d = q.DeQueue();  
  122.     d = q.DeQueue();  
  123.     d = q.DeQueue();  
  124.     d = q.DeQueue();  
  125.     d = q.DeQueue();  
  126.     d = q.DeQueue();  
  127.     d = q.DeQueue();  
  128.     for (; i<n; i++)  
  129.         q.EnQueue(data[i]);  
  130. }</span>  

解法2:

将队列用两个栈来表示。栈在加入数据时,判断最大数是否发生改变,若改变,要记录新的最大数的位置。栈在删除数据时,要判断被删除的数是否是最大数,若是,则要弹出当前的最大数的位置。队列的入队操作的时间复杂度为O(1)。对于出队操作,虽然如果栈sa为空时,栈sb会将所有数据弹出并压入到栈sa中,这个操作不是O(1),但经过平摊分析的记账方法(具体见算法导论)可知其平均时间时间复杂度为O(1)。取最大值的时间复杂度为O(1)。

[cpp] view plain copy
  1. <span style="font-size:18px;">#include <iostream>  
  2. #include <algorithm>  
  3. #include <vector>  
  4. using namespace std;  
  5.   
  6.   
  7. class Stack  
  8. {  
  9. public:  
  10.     void Push(int d)  
  11.     {  
  12.         vec.push_back(d);  
  13.         // 判断最大数是否发生改变  
  14.         if (maxvec.size()==0 || d>vec[maxvec[maxvec.size()-1]])  
  15.         {  
  16.             maxvec.push_back(vec.size()-1);  
  17.         }  
  18.     }  
  19.   
  20.     int Pop()  
  21.     {  
  22.         if (vec.size()==0)  
  23.             return 0;  
  24.         // 若删除的是当前的最大数,则pop到前一个最大数  
  25.         if (vec.size()-1 == maxvec[maxvec.size()-1])  
  26.             maxvec.pop_back();  
  27.         int d = vec[vec.size()-1];  
  28.         vec.pop_back();  
  29.         return d;  
  30.     }  
  31.   
  32.     bool empty()  
  33.     {  
  34.         return maxvec.size()==0;  
  35.     }  
  36.   
  37.     int maxElement()  
  38.     {  
  39.         int maxpos = maxvec[maxvec.size()-1];  
  40.         return vec[maxpos];  
  41.     }  
  42.    
  43. private:  
  44.     vector<int> vec;      // 存入压进来的数据  
  45.     vector<int> maxvec;       // 每当最大数发生改变时,存入新的最大数的位置  
  46. };  
  47.   
  48.   
  49. class Queue  
  50. {  
  51. public:  
  52.     void EnQueue(int d)  
  53.     {  
  54.         sb.Push(d);  
  55.     }  
  56.   
  57.     int DeQueue()  
  58.     {  
  59.         if (sa.empty())  
  60.         {  
  61.             while (!sb.empty())  
  62.             {  
  63.                 sa.Push(sb.Pop());  
  64.             }  
  65.         }  
  66.         return sa.Pop();  
  67.     }  
  68.     int maxElement()  
  69.     {  
  70.         return max(sa.maxElement(), sb.maxElement());  
  71.     }  
  72.   
  73. private:  
  74.     Stack sa, sb;   // 两个后进先出的stack相当于先进先出的queue  
  75. };  
  76.   
  77.   
  78. int main()  
  79. {  
  80.     const int n = 11;  
  81.     int data[] = {7, 4, 15, 9, 5, 10, 13, 3, 20, 17, 19};  
  82.     int i;  
  83.     Queue q;  
  84.     for (i=0; i<n/2; i++)  
  85.         q.EnQueue(data[i]);  
  86.     int d = q.DeQueue();  
  87.     d = q.DeQueue();  
  88.     d = q.DeQueue();  
  89.     d = q.DeQueue();  
  90.     d = q.DeQueue();  
  91.     d = q.DeQueue();  
  92.     d = q.DeQueue();  
  93.     for (; i<n; i++)  
  94.         q.EnQueue(data[i]);  
  95. }</span>  

0 0
原创粉丝点击