银行排队问题 有n个人去银行排队办事(n<=1000000)。银行只有一个窗口,每人有不同的优先级。每办完一个人,从等待的人中选优先级最高的人办理,同级选最先到的。 输入n以及n行数据Ai,Pi,Ti,表示第i个人的到达时间、优先级、办理用时。所有输入都是正整数,输入顺序为Ai递增顺序,输出实际办理的人的顺序。 样例输入: 5
0 1 10 2 1 10 5 3 10 7 2 5 11 3 5 样例输出: 1 3 5 4 2
性能一般的方法 建立一个优先队列,优先队列的优先原则是优先级大的在前,如果优先级相同,则达到时间小的在前,如果到达时间相同,则用时短的在前。设一个变量currentTime
用来记录当前的时间,根据当前时间来提取队列中的元素,如果队顶元素不满足要求,先出队,按出队顺序保存在数组中,直到遇到符合条件的元素,就让符合条件的元素去办理业务,完后把数组中保存的元素再入队,循环。。。直到队列为空,即所有人都去办理了业务为止。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 #include <iostream> #include <vector> #include <queue> using namespace std ;struct Member { int arrTime; int priority; int conTime; int id; }; void consructMember (Member &m, int arrTime, int priority, int conTime, int id) { m.arrTime = arrTime; m.priority = priority; m.conTime = conTime; m.id = id; } bool operator < (const Member &m1, const Member &m2) { if (m1.priority != m2.priority) { return m1.priority < m2.priority; } else if (m1.arrTime != m2.arrTime) { return m1.arrTime > m2.arrTime; } else { return m1.conTime > m2.conTime; } } vector <int > bankQueue(priority_queue<Member> &pq_m, int currentTime) { vector <int > result; vector <Member> vec_m; while (!pq_m.empty()) { Member m = pq_m.top(); while (m.arrTime > currentTime) { vec_m.push_back(m); pq_m.pop(); m = pq_m.top(); } if (m.arrTime <= currentTime) { pq_m.pop(); result.push_back(m.id); currentTime += m.conTime; } for (int i = 0 ; i < vec_m.size(); i++) { pq_m.push(vec_m[i]); } vec_m.clear(); } return result; } int main () { int n; cin >> n; Member member[n]; int a, p, c; priority_queue<Member> pq_m; vector <int > result; int currentTime; for (int i = 0 ; i < n; i++) { cin >> a >> p >> c; consructMember(member[i], a, p, c, i + 1 ); pq_m.push(member[i]); currentTime = i == 0 ? a : currentTime; } result = bankQueue(pq_m, currentTime); for (int i : result) cout << i << " " ; cout << endl ; }
辅导班老师指出这个代码存在的问题: 这个算法逻辑上还有一些小bug。 首先bankQueue函数里少了一些判断,导致有些输入的时候会死循环 另外你这个算法对某些人做了反复出堆和入堆,在某些极端情况下复杂度会到达O(n^2*logn) 下面这组数据就会同时触发上面这两件事情 10 0 1 1 2 2 1 4 3 1 6 4 1 8 5 1 10 6 1 12 7 1 14 8 1 16 9 1 18 10 1
修复bug后的代码: bankqueue2.cpp
性能更好的方法 思路: 维护两个数据结构,一个是vector
,按先来后到的顺序存放每个member
,一个是优先队列priority_queue
,优先队列的优先原则是优先级大的在前,如果优先级相同,则达到时间小的在前,如果到达时间相同,则用时短的在前。设一个变量currentTime
用来记录当前的时间。 首先,去访问队列中的成员,如果该成员已经服务过,直接删除,如果没有,则判断该成员是否已经到来,如果到来则访问,并记录他的编号,然后删除。如果队列头元素没有到来,就去vector
数组中访问当前游标指向的成员,访问后游标加1,并置访问过的成员的标志为true
。
要注意的是:
两个数据结构里面存放的都是指针;
相比方法一,Member
中添加了一个成员变量isVisited
,用来标记当前成员是否已经服务过了。
循环的退出条件是队列为空或者数组已遍历一遍。
该算法的时间复杂度相比方法一提高了不少,为O(nlogn)
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 #include <iostream> #include <vector> #include <queue> using namespace std ;struct Member { int arrTime; int priority; int conTime; int id; bool isVisited; Member(int arrT, int p, int conT, int i, bool f) : arrTime(arrT), priority(p), conTime(conT), id(i), isVisited(f) {} }; struct mycmp { bool operator () (const Member* m1, const Member* m2) { if (m1->priority != m2->priority) { return m1->priority < m2->priority; } else if (m1->arrTime != m2->arrTime) { return m1->arrTime > m2->arrTime; } else { return m1->conTime > m2->conTime; } } }; vector <int > bankQueue(priority_queue<Member*, vector <Member*>, mycmp> &pq_m, Member *pmem[], int n) { vector <int > result; if (n == 0 ) return result; int currentTime = pmem[0 ]->arrTime; int i = 0 ; while (i < n && pq_m.size() > 0 ) { Member *m = pq_m.top(); if (m->isVisited) { pq_m.pop(); continue ; } if (m->arrTime <= currentTime) { m->isVisited = true ; result.push_back(m->id); currentTime += m->conTime; pq_m.pop(); } else { if (pmem[i]->isVisited == false ) { result.push_back(pmem[i]->id); pmem[i]->isVisited = true ; currentTime += pmem[i]->conTime; } i++; } } return result; } int main () { int n; cin >> n; Member *pmember[n]; int a, p, c; priority_queue<Member*, vector <Member*>, mycmp> pq_m; vector <int > result; for (int i = 0 ; i < n; i++) { cin >> a >> p >> c; pmember[i] = new Member(a, p, c, i + 1 , false ); pq_m.push(pmember[i]); } result = bankQueue(pq_m, pmember, n); for (int i : result) cout << i << " " ; cout << endl ; }
堆排序的原理与特性 这部分我之前总结过。详见典型排序算法 - 堆排序 堆排序动态示例:
堆排序实现 方法一
在原数组上建立最小堆;
交换数组的第一个元素和最后一个元素;
维护最小堆;
重复2-3步骤直到完成数组中所有元素的排序。
此方法的时间复杂度为O(nlogn),空间复杂度为O(1)。提示: 建堆的方法和过程参考上面给出的堆排序的链接。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <iostream> #include <algorithm> using std ::cout ;using std ::endl ;class HeapSort {public : void heapSort (int A[], int n) { if (n < 2 ) return ; buildMinHeap(A, n); for (int i = n - 1 ; i > 0 ; i--) { std ::swap(A[0 ], A[i]); heap_size--; minHeapify(A, 0 ); } } private : void minHeapify (int A[],int index) { int left = 2 * index + 1 ; int right = 2 * index + 2 ; int lowest = 0 ; if (left < heap_size && A[left] < A[index]) { lowest = left; } else { lowest = index; } if (right < heap_size && A[right] < A[lowest]) lowest = right; if (lowest != index) { std ::swap(A[index], A[lowest]); minHeapify(A, lowest); } } void buildMinHeap (int A[], int n) { heap_size = n; for (int i = (n - 2 ) / 2 ; i >= 0 ; i--) { minHeapify(A, i); } } private : int heap_size = 0 ; }; int main () { int A[] = {9 , 2 , 7 , 5 , 3 , 19 , 8 , 0 }; int n = sizeof (A) / sizeof (int ); HeapSort h; h.heapSort(A, n); for (int e : A) { cout << e << " " ; } cout << endl ; return 0 ; }
方法二
在原数组上建立最大堆;
把数组的第一个元素放入新的数组中,把数组最后一个元素赋值给第一个元素;
维护最大堆;
重复2-3步骤直到完成数组中所有元素的排序。
此方法的时间复杂度为O(nlogn),空间复杂度为O(n)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <iostream> #include <algorithm> #include <vector> using std ::cout ;using std ::endl ;using std ::vector ;class HeapSort {public : void heapSort (int A[], vector <int > &B, int n) { if (n < 2 ) return ; buildMaxHeap(A, n); for (int i = n - 1 ; i > 0 ; i--) { B.push_back(A[0 ]); A[0 ] = A[i]; heap_size--; maxHeapify(A, 0 ); } } private : void maxHeapify (int A[],int index) { int left = 2 * index + 1 ; int right = 2 * index + 2 ; int largest = 0 ; if (left < heap_size && A[left] > A[index]) { largest = left; } else { largest = index; } if (right < heap_size && A[right] > A[largest]) largest = right; if (largest != index) { std ::swap(A[index], A[largest]); maxHeapify(A, largest); } } void buildMaxHeap (int A[], int n) { heap_size = n; for (int i = (n - 2 ) / 2 ; i >= 0 ; i--) { maxHeapify(A, i); } } private : int heap_size = 0 ; }; int main () { int A[] = {7 , 9 , 2 , 5 , 3 , 19 , 8 , 0 }; int n = sizeof (A) / sizeof (int ); vector <int > B; HeapSort h; h.heapSort(A, B, n); for (int e : B) { cout << e << " " ; } cout << endl ; return 0 ; }
方法三 按照课堂上老师的代码进行修改,先建立一个最大堆,然后依次打印出堆顶元素。
提示: 这里的代码不一定是最好的,也不一定非要建立一个最大堆。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 #include <iostream> #include <vector> using namespace std ;class MaxHeap {private : vector <int > data; int capacity,size; void swap (int &a,int &b) { a=a+b; b=a-b; a=a-b; } void recover (int i) { if (i>1 && data[i/2 ]<data[i]){ swap(data[i/2 ],data[i]); recover(i/2 ); } if (i*2 +1 <=size && data[i*2 +1 ]>data[i] && data[i*2 +1 ]>=data[i*2 ]){ swap(data[i*2 +1 ],data[i]); recover(i*2 +1 ); } if (i*2 <=size && data[i*2 ]>data[i] && (i*2 +1 >size || data[i*2 ]>data[i*2 +1 ])){ swap(data[i*2 ],data[i]); recover(i*2 ); } } public : MaxHeap(const int & n){ data.empty(); capacity=1 <<n; size=0 ; data.reserve(capacity); } int removeRoot () { int root= data[1 ]; data[1 ]=data[size--]; recover(1 ); return root; } void insert (int value) { data[++size]=value; recover(size); } void print () { for (int i=1 ;i<=size;i++){ int left=i*2 <=size?data[i*2 ]:-1 ; int right=i*2 +1 <=size?data[i*2 +1 ]:-1 ; cout <<data[i]<<"(" <<left<<"," <<right<<")" <<endl ; } cout <<endl ; } }; void heapSort (int data[],int n) { int level=0 ; for (int i=n; i>0 ; i>>=1 ,level++); MaxHeap heap (level) ; for (int i=0 ;i<n;i++){ heap.insert(data[i]); } for (int i=1 ; i<=n; i++){ int max = heap.removeRoot(); cout << max << " " ; } } int main () { int n; scanf ("%d" , &n); int data[n]; for (int i=0 ;i<n;i++){ scanf ("%d" , &data[i]); } heapSort(data, n); cout << endl ; }