1 算法思想
队列
1.1含义
是操作受限的线性表,只允许在表的一端进行插入,另一端进行删除。
有顺序结构和链式结构两种实现形式。
队列的顺序存储:
分配连续存储单元存放队列中元素,设置两个指针front,read分别表示队头元素和队尾元素的位置。队尾指针指向队尾元素的下一个位置
类型描述如下:
# define Maxsize 50
typedef struct{
ElemType data[Maxsize];
Int front, rear;
}SqQueue;
队列的链式存储:
同时带有队头指针和队尾指针的单链表。头指针指向队头节点,尾指针指向队尾节点,即单链表的最后一个节点
类型描述如下:
typedef struct{
ElemType data;
Struct LinkNode* next;
}LinkNode;
typedef struct{
LinkeNode *front, *rear;
}LinkQueue;
1.2特点
先进先出。
1.3适用
消息队列,生产者和消费者公用的数据队列等。
1.4通用解法
队列算法: 1 确保符合先进先出的性质 2 注意记录队头和队尾 3 优先级队列本质上是堆 |
1.5经典例题讲解
队列经典例题:
用两个队列实现一个栈
分析:
分析:设两个栈分别为栈1和栈2.栈1负责压入,栈2负责弹出。
一个例子:假设压入1,2,3,然后弹出1,压入4,。模拟过程:
栈1:
3
2
1
栈2:空
然后需要弹出1,此时需要将栈1所有元素压入栈2,
此时栈1为空,栈2为
1
2
3
然后弹出1,
栈2变为:
2
3
当需要压入4时,肯定不能直接压入到栈2,因为此时顺序不对,因此只能将栈2元素都压入栈1,然后将
4也压入栈1,变成
4
3
2
总结规律:
栈1用于压入,当需要压入元素时,判断此时如果栈1为空,需要将栈2的所有元素先压入栈1,然后再压入当前元素
栈2用于弹出,当需要弹出元素时,判断此时如果栈2为空,需要将栈1的所有元素压入栈2,然后弹出栈2的栈顶元素
代码如下:
class MyQueue { public: void push(int value) { //如果弹出栈不空,将该栈所有元素压入压入栈中,然后再执行压入 if( !popStack.empty() ) { while( !popStack.empty() ) { int num = popStack.top(); popStack.pop(); pushStack.push(num); } pushStack.push(value); } //如果弹出栈为空,直接压入元素 else { pushStack.push(value); } }
void pop() { //如果压入栈不空,将压入栈中所有元素转移至弹出栈中,然后弹出 if(!pushStack.empty()) { while(!pushStack.empty()) { int num = pushStack.top(); pushStack.pop(); popStack.push(num); } popStack.pop(); } else { popStack.pop(); } }
//打印元素时,此时需要判断所有元素在哪个栈中。全部转移至弹出栈中,然后输出,就是正确顺序 void print() { if(!pushStack.empty()) { while(!pushStack.empty()) { int num = pushStack.top(); pushStack.pop(); popStack.push(num); } } //弹出元素 stack<int> tempStack( popStack ); while(!tempStack.empty()) { int value = tempStack.top(); tempStack.pop(); cout << value << " "; } cout << endl; } private: stack<int> pushStack; stack<int> popStack; }; |
2 队列系列
类别-编号 | 题目 | 遁去的一 |
1 | 用两个栈实现队列 | 剑指offer https://blog.csdn.net/qingyuanluofeng/article/details/102616547 参见经典例题解析 |
2 | 队列中取最大值操作问题: 假设有这样一个拥有3个操作的队列 1EnQueue(v):将v加入到队列中 2DeQueue():使队列中的队首元素删除并返回此元素 3MaxElement:返回队列中的最大元素 请设计一种数据结构和算法,让MaxElement操作的时间复杂度尽可能地低。 队列底层的数据结构不一定要用数组来实现,还可以使用其他特殊的数据结构来实现。 输入: 4 2 1 3 4 输出: 4 | 编程之美 https://blog.csdn.net/qingyuanluofeng/article/details/47187661 这道题目: 参见剑指。可以用两个栈来实现。(剑指上的题目是包含min功能的栈,这道题目是包含max功能的队列) 一个用作数据栈,一个用作存储最大值的栈。存储最大值的栈中,我们这样设计,如果最大栈为空,那么将当前元素直接压入最大栈中;如果已经有了元素, 那么我们将当前值与栈顶比较,如果栈顶>=当前值,再向栈中压入栈顶;如果栈顶<当前值,将当前值压入; 弹出的时候,我们就将最大栈里面的值弹出即可。 而数据栈则是存放所有数据。 解法三: 由于栈是和队列相似的数据结构,先看看栈。 这里,维护一个最大值的序列(link2NextMaxItem)来保证Max操作的时间复杂度为O(1),相当于用空间复杂度换取时间复杂度。
关键: 1 这里,维护一个最大值的序列(link2NextMaxItem)来保证Max操作的时间复杂度为O(1),相当于用空间复杂度换取时间复杂度。 2 stack():_iStackTop(-1),_iMaxIndex(-1){}//设置栈顶为-1,最大值下标元素为-1 3 if(_iMaxIndex >= 0)//如果最大值下标>=0,表示可以返回栈数组中对应最大值下标的元素,否则返回负无穷大 { return _itemArr[_iMaxIndex]; 4 void push(T x)//压入算法:首先使栈顶累加,然后判断栈顶是否超过最大值;放入元素,将放入的元素与栈中的最大值进行比较,如果大于最大值,那么 //使最大值序列中的栈顶存放最大值下标,同时使最大值下标为栈; 小于 ,那么 //使最大值序列栈顶元素对应的值为-1 { _iStackTop++; 5 if(_iStackTop == _iMaxIndex)//记住,返回栈顶最大值元素之后,要重新修改链表,更新最大值下标为链表中下一个元素,并使栈顶减减 { _iMaxIndex = _iLinkNextMaxItem[_iStackTop]; } _iStackTop--; 6 if(x > max()) { _iLinkNextMaxItem[_iStackTop] = _iMaxIndex;//采用类似头插法的方式来保存最大元素,使_iLinkNextMaxItem[_iStackTop]中始终保存的是最大值下标 _iMaxIndex = _iStackTop; } else { _iLinkNextMaxItem[_iStackTop] = -1;//如果不是最大值,就始终维持最大值下标为-1 } 7 Stack<T> stackA;//注意标准是stack,queue,小写 Stack<T> stackB;//注意,这里调用的是我们自己写的栈 8 void EnQueue(T x) { stackB.push(x); } T DeQueue() { if(stackA.empty())//如果队列A空了,就将B中的元素全部放入A中 { while(!stackB.empty())//每个元素被移动的次数最多3次:从B堆栈进入,当A堆栈为空时,从B堆栈弹出并压入A堆栈,从A堆栈弹出 { stackA.push(stackB.pop()); } } return stackA.pop();//返回A中弹出的栈顶元素 9 T max() { return maxValue(stackA.max(),stackB.max());//牛逼,用递归来做
代码:
const int INT_MAX1 = 0x7fffffff;//靠名字冲突了 const int INT_MIN1 = -INT_MAX1; const int MAXSIZE = 10000;
using namespace std;
template<typename T> class Stack { public: Stack():_iStackTop(-1),_iMaxIndex(-1){}//设置栈顶为-1,最大值下标元素为-1 bool empty() { if(_iStackTop < 0) { return true; } else { return false; } } T max() { if(_iMaxIndex >= 0)//如果最大值下标>=0,表示可以返回栈数组中对应最大值下标的元素,否则返回负无穷大 { return _itemArr[_iMaxIndex]; } else { return INT_MIN1; } } void push(T x)//压入算法:首先使栈顶累加,然后判断栈顶是否超过最大值;放入元素,将放入的元素与栈中的最大值进行比较,如果大于最大值,那么 //使最大值序列中的栈顶存放最大值下标,同时使最大值下标为栈; 小于 ,那么 //使最大值序列栈顶元素对应的值为-1 { _iStackTop++; if(_iStackTop >= MAXSIZE) { return; } else { _itemArr[_iStackTop] = x; } if(x > max()) { _iLinkNextMaxItem[_iStackTop] = _iMaxIndex;//采用类似头插法的方式来保存最大元素,使_iLinkNextMaxItem[_iStackTop]中始终保存的是最大值下标 _iMaxIndex = _iStackTop; } else { _iLinkNextMaxItem[_iStackTop] = -1;//如果不是最大值,就始终维持最大值下标为-1 } } T pop() { T ret; if(_iStackTop < 0) { printf("Stack is Empty!"); return NULL; //throw new exception("Stack is Empty!"); } else { ret = _itemArr[_iStackTop]; if(_iStackTop == _iMaxIndex)//记住,返回栈顶最大值元素之后,要重新修改链表,更新最大值下标为链表中下一个元素,并使栈顶减减 { _iMaxIndex = _iLinkNextMaxItem[_iStackTop]; } _iStackTop--; } return ret; } private: int _iStackTop; int _iMaxIndex; int _iLinkNextMaxItem[MAXSIZE];//维护最大值的序列 T _itemArr[MAXSIZE];//存放数据 };
//用两个栈来实现一个队列 template<typename T> class Queue { public: T maxValue(T a,T b) { return a > b ? a : b; } bool empty() { if(stackA.empty() && stackB.empty()) { return true; } else { return false; } } T max() { return maxValue(stackA.max(),stackB.max());//牛逼,用递归来做 } void EnQueue(T x) { stackB.push(x); } T DeQueue() { if(stackA.empty())//如果队列A空了,就将B中的元素全部放入A中 { while(!stackB.empty())//每个元素被移动的次数最多3次:从B堆栈进入,当A堆栈为空时,从B堆栈弹出并压入A堆栈,从A堆栈弹出 { stackA.push(stackB.pop()); } } return stackA.pop();//返回A中弹出的栈顶元素 } private: Stack<T> stackA;//注意标准是stack,queue,小写 Stack<T> stackB;//注意,这里调用的是我们自己写的栈 };
void queueMax_queue(int* pArr,int iLen) { Queue<int> que; for(int i = 0 ; i < iLen ; i++) { que.EnQueue(pArr[i]); } while(!que.empty()) { printf("%d\n",que.max());//关键这里只能返回最大值,对于2 1 3 4这种,只会返回4 3 2, 但是1不会返回是因为已经到达最后的 que.DeQueue(); } }
void process() { int n; while(EOF != scanf("%d",&n)) { int iArr[MAXSIZE]; for(int i = 0 ; i < n ; i++) { scanf("%d",&iArr[i]); } queueMax_queue(iArr,n); } } |
3 | 实现一个MyQueue类,该类用两个栈来实现一个队列 | 程序员面试金典 https://blog.csdn.net/qingyuanluofeng/article/details/53839880 参见经典例题解析 |
4 | 猫狗动物队列 有家动物收容所只收容狗与猫,并且严格遵守“先进先出”原则。在收养该收容所的动物时,收养人只能收养所有动物中“最老”(根据进入收容所的时间长短)的动物,或者,可以挑选猫或狗(同时必须收养此类动物中“最老”的)。换言之,收养人不能自由挑选想收养的对象。请创建适用于这个系统的数据结构,实现各种操作方法,比如enqueue,dequeueAny,dequeueDog和dequeueCat等。允许使用Java内置的LinkedList数据结构。 输入: 5(猫狗个数之和) d(表示狗) c(表示猫) c d c 3(指令个数) dequeueAny dequeueCat dequeueDog 输出: d(该操作返回的动物名称) 0(动物序号) c 1 d 3 | 程序员面试金典 https://blog.csdn.net/qingyuanluofeng/article/details/53858060 分析:所谓的挑选猫或狗,就是直接提取出任何动物,但是必须收养最老的动物。一种暴力破解方法就是直接遍历,从头到尾遍历,直接去获取最老的 动物,或者是挑选某个指定位置的猫,并要找出最老的猫一起返回;或者挑选某个狗以及最老的狗。 如果采用队列,那么就是先进先出。 一种实现队列的方法就是:采用链表来实现,并且采用尾插法,遍历的时候从头部开始。 挑选其实就是所谓的删除。 书上解法:只维护一个队列,找猫或狗需要遍历整个队列,增加复杂度。 简单方法:猫和狗各创建一个队列,将两个队列放入包裹类AnimalQueue,存储时间戳作为入队时间。 当调用dequeueAny时,查看狗队列和猫队列的时间戳即可。 我之所以没想到,是因为我想用一个队列来做。像类似的猫狗不同对象队列,小于x大于x进行划分,最好都用两个队列或两个链表来做。 另外想错了一个地方,dequeueDog就只能获取最老的狗而不是某个狗+最老的狗
关键: 1 猫和狗各创建一个队列,将两个队列放入包裹类AnimalQueue,存储时间戳作为入队时间。 当调用dequeueAny时,查看狗队列和猫队列的时间戳即可。 我之所以没想到,是因为我想用一个队列来做。像类似的猫狗不同对象队列,小于x大于x进行划分,最好都用两个队列或两个链表来做。 另外想错了一个地方,dequeueDog就只能获取最老的狗而不是某个狗+最老的狗
class Animal { public: Animal(string& name) : _name(name){} void setName(string& name) { _name = name; } virtual string getName() { return _name; } void setOrder(int order) { _order = order; } bool isOlderThan (Animal& other) { return _order < other._order; } int getOrder() { return _order; } protected: string _name; int _order;//用于记录顺序,数值越小,比较的时候越应该弹出 };
class Dog : public Animal { public: //构造函数需要直接对父类构造函数进行赋值 Dog(string& name) : Animal(name){} string getName() { return _name; } };
class Cat : public Animal { public: //构造函数需要直接对父类构造函数进行赋值 Cat(string& name) : Animal(name){} string getName() { return _name; } };
class AnimalQueue : public queue<Animal> { public: AnimalQueue() { order = 0; } void enqueue(Animal& animal) { string name = animal.getName(); if(name == "c") { //使用引用转换dynamic_cast<Cat&> (animal),注意转换必须加括号 Cat cat = dynamic_cast<Cat&> (animal); cat.setOrder(order++); queueCat.push(cat); } else if(name == "d") { Dog dog = dynamic_cast<Dog&> (animal); dog.setOrder(order++); queueDog.push(dog); } }
//弹出队列中最老的元素,注意每次弹入元素时,需要设置顺序 Animal dequeueAny() { if(queueDog.empty() && queueCat.empty()) { Animal animal(string("animal")); return animal; } else if(queueCat.empty()) { Dog dog = queueDog.front(); queueDog.pop(); return dog; } else if(queueDog.empty()) { Cat cat = queueCat.front(); queueCat.pop(); return cat; } else { //根据顺序来找到最老的元素 Dog dog = queueDog.front(); Cat cat = queueCat.front(); bool isOlder = dog.isOlderThan(cat); if(isOlder) { queueDog.pop(); return dog; } else { queueCat.pop(); return cat; } } }
Animal dequeDog() { if(queueDog.empty()) { Dog dog(string("dog")); return dog; } else { Dog dog = queueDog.front(); queueDog.pop(); return dog; } }
Animal dequeCat() { if(queueCat.empty()) { Cat cat(string("cat")); return cat; } else { Cat cat = queueCat.front(); queueCat.pop(); return cat; } } private: queue<Dog> queueDog; queue<Cat> queueCat; int order; }; |
5 | Implement Queue using Stacks | Leecode https://blog.csdn.net/qingyuanluofeng/article/details/56685862 参见经典例题解析 |
6 | 如何实现队列 实现一个队列的数据结构,使其具有入队列、出队列、查看队列首尾元素、查看队列大小等功能 | Python程序员面试算法宝典 https://blog.csdn.net/qingyuanluofeng/article/details/90115956 分析: 队列的特点就是先进先出。 这里最关键的一点就是查看队列首尾元素。 栈和队列实际都是链表的变体。 这里可以考虑用双向链表。 采用尾插法,设置一个头结点,头结点存储队列长度信息。 记录当前节点的前一个节点,即为prev, 每次待插入的节点记为curr, 初始令: prev = head tail = head 进队操作: prev.nextNode = curr curr.prevNode = prev prev = curr tail = prev 那么查看队列的首元素就是获取: head.nextNode 查看队列的尾元素就是获取: tail 出队操作,则需要将链表的第一个节点删除: head.nextNode = head.nextNode.nextNode 关键: 1 可以用单向链表实现 只是要查看队尾元素,可以记录一个尾指针 2 可以用数组实现 设置front指向第一个元素,设置rear初始为front后面的一个元素, 进队: list[rear] = newData, read += 1 出队: front += 1 后面可以用循环数组实现 两种方法更推荐用单向链表
class Node(object): def __init__(self, data=None, nextNode=None, prevNode=None): self.data = data self.nextNode = nextNode self.prevNode = prevNode
class MyQueue(object): def __init__(self): self.head = Node() self.prev = self.head self.tail = None self.len = 0
def getLen(self): return self.len
def isEmpty(self): if self.len <= 0: return True else: return False
def push(self, data): curr = Node(data) self.prev.nextNode = curr curr.prevNode = self.prev self.prev = curr self.tail = self.prev self.len += 1
def pop(self): if self.isEmpty(): print "queue is empty, it can not pop" return temp = self.head.nextNode self.len -= 1 self.head.nextNode = self.head.nextNode.nextNode if self.head.nextNode else None return temp.data
def getHead(self): if self.isEmpty(): print "queue is empty, it can not get head" return None return self.head.nextNode.data
def getTail(self): if self.isEmpty(): print "queue is empty, it can not get head" return None else: return self.tail.data |
7 | 如何用两个栈模拟队列操作 | Python程序员面试算法宝典 https://blog.csdn.net/qingyuanluofeng/article/details/91127045 关键: 1 书上解法 栈A入队,栈B用于出队, 如果B为空,则将A中元素压入B中,最后弹出B的top元素; 否则直接弹出B中的top元素
class Stack(object): def __init__(self): self.data = list()
def empty(self): result = False if self.data else True return result
def push(self, data): self.data.append(data)
def pop(self): if not self.empty(): self.data.pop() else: print "stack is empty, it can not pop"
def top(self): if not self.empty(): data = self.data[-1] return data else: print "stack is empty, it can not get top"
class SimulatedQueue(object): def __init__(self): self.stack1 = Stack() self.stack2 = Stack()
def push(self, data): self.stack1.push(data)
def pop(self): if self.stack2.empty(): while not self.stack1.empty(): value = self.stack1.top() if value is not None: self.stack2.push(value) self.stack1.pop() if not self.stack2.empty(): front = self.stack2.top() self.stack2.pop() return front else: return None
def empty(self): result = True if self.stack2.empty() and self.stack1.empty() else False return result |
8 | 如何设计一个排序系统 随时有人加入和退出;当有人退出影响到用户的位置的排名时需要及时反馈到用户。 | Python程序员面试算法宝典 https://blog.csdn.net/qingyuanluofeng/article/details/91350337 关键: 1 书上解法: 1.1 使用使用python中collections.deque deque是双端队列,在list基础上增加了移动,旋转,增删。 1.2 设定一个User类,并设置seq这个属性, 实现equals方法,比较两个对象的id是否相同(应该是队列 删除元素时需要) 当将User对象放入队列时,设置seq的序号; 当出队的时候,都需要更新seq 2 deque方法 append(value): 在最右边添加元素 appendleft(value): 最左边添加元素 extend(aList): 在最右边添加所有元素 extendleft(aList): 在最左边添加所有元素 pop():将最右边元素取出 popleft():将最左边元素取出 rotate(intValue): 旋转位置,负数为向左旋转 count(value):获取队列中值为value的元素个数 remove(value): 将队列中的元素value删除 reverse():将队列逆置
from collections import deque
class User(object): def __init__(self, id, name, seq=None): self.id = id self.name = name self.seq = seq
def equals(self, other): return other.id == self.id
def __str__(self): info = "id: {id}, name: {name}, sequence: {seq}".format( id=self.id, name=self.name, seq=self.seq ) return info
class UserQueue(object): def __init__(self): self.queue = deque()
def enQueue(self, user): user.seq = len(self.queue) + 1 self.queue.append(user)
def deQueue(self): self.queue.popleft() self.updateSeq()
# 有用户随机离开 def deQueueRemove(self, user): self.queue.remove(user) self.updateSeq()
def updateSeq(self): for i, user in enumerate(self.queue): user.seq = i + 1
def printQueue(self): for user in self.queue: print user |
9 | 如何实现LRU缓存方案 | Python程序员面试算法宝典 https://blog.csdn.net/qingyuanluofeng/article/details/91350970 分析: 最近最少使用实际就是在缓存不够的时候将最先进入的删除掉,那么这就可以使用队列完成。 将需要删除的最前面的n个元素删除即可。 使用双向链表来实现队列,队列最大容量为缓存大小。把最近使用的页面移动到队列头, 最近没有使用的放在队列尾部。 使用哈希表,建立: <页号,队列中该页面对应节点地址>的节点字典。 当引用页面在队列中,则将页面对应节点移动到队列头部, 否则,将一个新节点添加到队列前面,并更新节点字典。 若队列已经满了,那么就从队列尾部移除一个节点,并将新节点添加到队列前面 关键: 1 缓存有大小 最近访问的页面需要放置到队列头部; 队列满,需要移除队尾元素; 需要建立字典保存<页号,队列中页面对应节点地址>
from collections import deque
class LRU(object): def __init__(self, cacheSize): self.cacheSize = cacheSize self.queue = deque() self.hashSet = set()
def full(self): return len(self.queue) >= self.cacheSize
def enQueue(self, data): # 如果待入队元素在队列中,则移动到队头 if data in self.hashSet: self.queue.remove(data) self.queue.appendleft(data) else: self.hashSet.add(data) # 如果队列已经满了,则先移除队尾的元素,然后将待入队元素放入队头 if self.full(): self.queue.pop() self.queue.appendleft(data)
def deQueue(self): self.queue.pop()
def deQueueRemove(self, data): self.queue.remove(data)
def printLRU(self): info = "" for value in self.queue: if info: info += " " + str(value) else: info += str(value) print info |
10 | 如何从给定的车票中找出旅程 给定一趟旅途旅程中所有的车票信息,根据这个车票信息找出这趟旅程的路线。例如: 给定下面的车票: 西安到成都,北京到上海,大连到西安,上海到大连。 那么可以得到旅程的路线为: 北京->上海,上海->大连,大连->西安,西安->成都。 假定给定的车票不会有环,也就是说有一个城市之作为终点而不会作为起点。 | Python程序员面试算法宝典 https://blog.csdn.net/qingyuanluofeng/article/details/91351720 关键: 1 我的方法是可行的 书上解法: 同时设置input和reverseInput,遍历input中的每个key,如果某个key不在reverseInput中, 则该key就是起点。
from collections import deque
class OptimizedRoute(object): def __init__(self): self.queue = deque() self.dict = dict() self.reverseDict = dict()
def addTicket(self, key, value): self.dict[key] = value self.reverseDict[value] = key
def findRoute(self): # 寻找起点 start = None for key, value in self.dict.iteritems(): if key not in self.reverseDict: start = key break if start is None: print "start is None, it it not correct" return result = start while start in self.dict: start = self.dict[start] result += '->' + start return result |
11 | 卡片游戏 桌上有一叠拍,从第一张牌(位于顶面的牌)开始从上往下依次编号为1~n。当至少还剩两张牌时进行以下操作:把第一张牌扔掉,然后把新的第一张放到整叠牌 的最后。输入n,输出每次扔掉的牌,以及最后剩下的牌 | 算法竞赛入门经典 https://blog.csdn.net/qingyuanluofeng/article/details/47730663 思路: 设置剪枝数组,凡是扔掉的牌,置剪枝标记为真,循环结束条件为还剩一张牌,有两个循环:报数循环,每次报到的删除,大循环,每次超过7进入下一次循环 关键:如何解决放在末尾的问题?用循环链表,每次放在后面。凡是放在后面,再为它开辟一个数组元素 用链表,找到末尾位置之后,从前向后遍历,如果是新1,则在末尾后加上新1,一直到只剩一个元素位置,采用尾插法 首先建立单链表n 用队列也可以做,前面弹出元素,后面末尾追加元素
void cardGame(int n,int i) { int m_queue[MAXSIZE]; int front = 0,rear = n;//关键,模拟队列,设置队首和队尾 for(int i = 0; i < n;i++) { m_queue[i] = i+1; } while(front < rear) { printf("%d ",m_queue[front++]); m_queue[rear++] = m_queue[front++];//这个经典,将队尾累加队守元素,不需要设置剪枝数组 } } |
12 | 实现一个优先级队列,支持插入元素和返回最小元素操作 输入: 12(数组元素个数) 19 51 26 40 35 22 17 23 29 15 20 12 输出: 12(最小元素) | 编程珠玑 https://blog.csdn.net/qingyuanluofeng/article/details/54670021 分析:优先级队列保证插入和返回最小值元素的时间复杂度都是O(logN),假设有n个元素,每个元素插入在堆中的时间复杂度为O(logN) 所以堆排序的总时间复杂度为O(NlogN) 优先级队列:说白了就是小顶堆的变体。
关键: 1 优先级队列注意使用模板 //优先级队列:包含最大元素个数,支持插入和提起最小元素 template<class T> class MyPriorityQueue { public: MyPriorityQueue(int maxSize) { //需要new出maxSize+1个元素 _maxSize = maxSize; x = new T[_maxSize + 1]; n = 0; } 2 优先级队列保证插入和返回最小值元素的时间复杂度都是O(logN),假设有n个元素,每个元素插入在堆中的时间复杂度为O(logN) 所以堆排序的总时间复杂度为O(NlogN) 优先级队列:说白了就是小顶堆的变体。
//优先级队列:包含最大元素个数,支持插入和提起最小元素 template<class T> class MyPriorityQueue { public: MyPriorityQueue(int maxSize) { //需要new出maxSize+1个元素 _maxSize = maxSize; x = new T[_maxSize + 1]; n = 0; }
//析构必须删除指针 ~MyPriorityQueue() { delete[] x; } //插入:先判断元素个数是否已经 void insert(T t) { if( n + 1 > _maxSize ) { cout << "element size greater than " << _maxSize << endl; return; } x[++n] = t;//留了x[0]不做处理 int i = n ; int p = i / 2;; //注意插入元素,是向上调整,如果父节点 > 子节点 for( ; i > 1 && x[p] > x[i] ; ) { swap(p ,i); i = p; p = i/2; } }
//提取最小元素,只需要获取堆顶元素,然后交换堆顶和最末端元素,然后重新调整除最末端的元素,使其再次成为堆 T extractMin() { //如果当前队列为空 if(isEmpty()) { throw("PrioorityQueue is empty!"); } T min = x[1]; x[1] = x[n--]; int i = 1; int c; for(i = 1 ; (c = 2 * i) <= n ; i = c) { if(c + 1 <= n && x[c+1] < x[c]) { c++; } if(x[i] > x[c]) { swap(i , c); } else { break; } } return min; }
bool isEmpty() { return n <= 0 ? true : false; }
void swap(int i , int j) { T temp = x[i]; x[i] = x[j]; x[j] = temp; }
private: int _maxSize; int n; T* x; }; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
参考:
[1]计算机考研--机试指南,王道论坛 组编
[2]剑指offer
[3]算法设计与分析
[4]编程之美
[5]程序员面试金典
[6]leecode
[7]Python程序员面试算法宝典
[8]刘汝佳算法竞赛入门经典
[9]算法导论
[10]编程珠玑