栈和队列
1. 用数组实现栈和队列
题目:用数组结构实现固定大小的栈和队列
实现栈
public class ArrayStack {
public Integer[] arr;
public Integer index;
public ArrayStack(Integer initSize) {
if(initSize < 0){
throw new IllegalArgumentException("The init size is less than 0.");
}
arr = new Integer[initSize];
index = 0;
}
public Integer peek(){
if(index == 0){
return null;
}
return arr[index - 1];
}
public void push(Integer num){
if(index == arr.length){
throw new ArrayIndexOutOfBoundsException("Full!");
}
arr[index++] = num;
}
public Integer pop(){
if(index == 0){
return null;
}
return arr[--index];
}
public static void main(String[] args){
ArrayStack stack = new ArrayStack(5);
stack.push(1);
stack.push(3);
System.out.println(stack.peek());
stack.push(0);
System.out.println(stack.peek());
System.out.println(stack.pop());
stack.pop();
System.out.println(stack.peek());
}
}
实现队列
public class ArrayQueue {
//也可以不用queueSize变量,使用arr.length代替也可以
public int queueSize;
public int tempSize;
public int start;
public int end;
public int[] arr;
public ArrayQueue(int queueSize){
if(queueSize < 0){
throw new IllegalArgumentException("size < 0");
}
start = 0;
end = 0;
tempSize = 0;
this.queueSize = queueSize;
arr = new int[queueSize];
}
public void push(int num){
if(tempSize == queueSize){
throw new ArrayIndexOutOfBoundsException("too much");
}
arr[end++] = num;
if(end == queueSize){
end = 0;
}
tempSize++;
}
public int poll(){
if(tempSize == 0){
throw null;
}
int result = arr[start++];
if(start == queueSize){
start = 0;
}
tempSize--;
return result;
}
public static void main(String[] args){
ArrayQueue queue = new ArrayQueue(3);
queue.push(1);
queue.push(2);
queue.push(3);
System.out.printf(String.valueOf(queue.poll()) + " ");
queue.push(4);
System.out.printf(String.valueOf(queue.poll()) + " ");
queue.push(5);
queue.push(6);
}
}
2. 实现一个特殊的栈,返回最小元素
题目:实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。
要求:
- pop、push、getMin操作的时间复杂度都为O(1)。
- 设计的栈类型可以使用现成的栈结构。
解决思路:可以用两个栈来实现:stackData和stackMin
- 当向栈中压入数据num时,将num正常存入stackData,比较num和stackMin的栈顶元素,将较小的压入栈顶。
- 弹出数据时,两个栈都弹出自己的栈顶元素。
- 返回栈中最小元素,即为stackMin的栈顶元素。
import java.util.Stack;
public class GetMinStack {
public static class MyStack{
private Stack<Integer> stackData;
private Stack<Integer> stackMin;
public MyStack(){
this.stackData = new Stack<Integer>();
this.stackMin = new Stack<Integer>();
}
public void myPush(Integer num){
stackData.push(num);
if(this.stackMin.empty()){
stackMin.push(num);
}else{
int minPeek = this.stackMin.peek();
int temp = num < minPeek ? num : minPeek;
stackMin.push(temp);
}
}
public int pop(){
if(this.stackData.empty()){
throw new IllegalArgumentException("MyStack is empty.");
}else{
int temp = stackData.pop();
stackMin.pop();
System.out.println("pop the number of " + temp);
return temp;
}
}
public void getMin(){
if(this.stackMin.empty()){
throw new IllegalArgumentException("Mystack is empty.");
}else{
int temp = stackMin.peek();
System.out.println("The min is " + temp);
}
}
}
public static void main(String[] args){
MyStack myStack = new MyStack();
myStack.myPush(2);
myStack.getMin();
myStack.myPush(3);
myStack.getMin();
myStack.myPush(1);
myStack.getMin();
myStack.pop();
myStack.getMin();
myStack.pop();
myStack.getMin();
}
}
3. 仅用队列结构实现栈结构
注:在java中,队列是用链表实现的。Queue queue = new LinkedList<Integer>();
思路:可以用两个队列实现栈结构。分别为data、help。
步骤:
- 入栈时将数据加入到队列data中,help队列不变。
- 出栈时,将队列data中的数据(除了最后一个元素)弹出到队列help中,弹出data队列的最后一个元素,然后help和data队列交换引用
- 求栈顶元素时,将队列data中的数据(除了最后一个元素)弹出到队列help中,然后先记录data队列的最后一个元素,再将其弹出到help队列中,交换help和data队列交换引用
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
/**
* 仅用队列结构实现栈结构
*/
public class Queue2Stack {
public static class MyStack{
public Queue data;
public Queue help;
public MyStack(){
this.data = new LinkedList<Integer>();
this.help = new LinkedList<Integer>();
}
public void push(int num){
data.add(num);
}
public int pop(){
if(data.isEmpty()){
throw new IllegalArgumentException("stack is empty.");
}
while(data.size() > 1){
help.add(data.poll());
}
int num = (int) data.poll();
swap();
return num;
}
public int peek(){
if(data.isEmpty()){
throw new IllegalArgumentException("stack is empty.");
}
while(data.size() > 1){
help.add(data.poll());
}
int num = (int) data.peek();
help.add(num);
swap();
return num;
}
public void swap(){
Queue<Integer> temp = data;
data = help;
help = temp;
}
}
public static void main(String[] args){
MyStack stack = new MyStack();
stack.push(1);
stack.push(2);
stack.peek();
System.out.println("peek:" + stack.peek());
stack.pop();
System.out.println("peek:" + stack.peek());
}
}
4. 仅用栈结构实现队列结构
思路:可以用两个栈来实现,这两个栈分别为pushStack和popStack,一个用来压入用户新增的数据,另一个用来弹出数据。
步骤:
- 用户添加数据,将数据依次添加到栈pushStack中,popStack不动
- 弹出数据,将pushStack中的全部数据依次弹出到popStack中,然后popStack栈弹出一个元素
- 第三步可以将popStack中的全部数据依次弹出并压入pushStack中,也可以等待下一个push操作发生之前,再把popStack中的元素依次弹出压入pushStack中
总之保证两个原则:
- 如果要弹出数据,首先要保证pushStack栈为空:将pushStack所有数据弹出到popStack中
- 如果要添加数据,首先要保证popStack栈为空:将pushpopStack所有数据弹出到pushStack中
import java.util.Stack;
/**
* 仅用栈结构实现队列结构
*/
public class Stack2Queue {
public static class MyQueue{
public Stack<Integer> pushStack;
public Stack<Integer> popStack;
public MyQueue(){
this.pushStack = new Stack<Integer>();
this.popStack = new Stack<Integer>();
}
public void add(int num){
while(popStack.size() > 0){
pushStack.push(popStack.pop());
}
pushStack.push(num);
}
public int poll(){
if(popStack.isEmpty()&&pushStack.isEmpty()){
throw new IllegalArgumentException("queue is empty.");
}
while(pushStack.size() > 0){
popStack.push(pushStack.pop());
}
return popStack.pop();
}
public int peek(){
if(popStack.isEmpty()&&pushStack.isEmpty()){
throw new IllegalArgumentException("queue is empty.");
}
while(pushStack.size() > 0){
popStack.push(pushStack.pop());
}
return popStack.peek();
}
}
public static void main(String[] args) {
MyQueue queue = new MyQueue();
queue.add(1);
queue.add(2);
queue.add(3);
System.out.println(queue.peek());
queue.poll();
System.out.println(queue.peek());
}
}
5. 猫狗队列
宠物、猫、狗的实现如下:
public class Pet{
private String type;
public Pet(String type){
this.type = type;
}
public String getPetType(){
return this.type;
}
}
public class Dog extends Pet{
public Dog(){
super("Dog");
}
}
public class Cat extends Pet{
public Cat(){
super("Cat");
}
}
实现一种猫狗队列的结构,要求如下:
用户可以调用add方法将cat类或者dog类的实例放入队列中;
用户可以调用pollAll方法,将队列中所有的实例按照队列的先后顺序依次弹出;
用户可以调用pollDog方法,将队列中dog类的实例按照队列的先后顺序依次弹出;
用户可以调用pollCat方法,将队列中cat类的实例按照队列的先后顺序依次弹出;
用户可以调用isEmpty方法,检查队列中是否还有dog和cat的实例;
用户可以调用isDogEmpty方法,检查队列中是否还有do的实例;
用户可以调用isCatEmpty方法,检查队列中是否还有cat的实例。
思路:
- 用两个队列来实现,分别用来装猫对象和狗对象。为了实现pollAll()方法,让所有实例按入队顺序弹出,就需要给每个对象定义一个时间戳。出队时,时间戳较小的先出。
- 但是又不能更改题目给出的Pet类,所以新定义一个类PetEnter,用来封装Pet类和时间戳。
- 使用add()方法入队时,压入的是PetEnter对象,而不是Pet对象。并记录时间戳的值赋值给要压入的每一个PetEnter对象。
import java.util.LinkedList;
import java.util.Queue;
public class CatDogQueue {
public static class Pet{
private String type;
public Pet(String type){
this.type = type;
}
public String getPetType(){
return this.type;
}
}
public static class Dog extends Pet{
public Dog(){
super("Dog");
}
}
public static class Cat extends Pet{
public Cat(){
super("Cat");
}
}
public static class PetEnter{
private Pet pet;
private int count;
public PetEnter(Pet pet){
this.pet = pet;
this.count = 0;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
public static class CatDog{
public Queue<PetEnter> catQueue;
public Queue<PetEnter> dogQueue;
public int index;
public CatDog(){
this.catQueue = new LinkedList<PetEnter>();
this.dogQueue = new LinkedList<PetEnter>();
this.index = 0;
}
public void add(PetEnter petEnter){
petEnter.setCount(index++);
if("Dog".equals(petEnter.pet.getPetType())){
this.dogQueue.add(petEnter);
}else if("Cat".equals(petEnter.pet.getPetType())){
this.catQueue.add(petEnter);
}else{
throw new IllegalArgumentException("It is not a cat or dog.");
}
}
public void pollAll(){
if(isEmpty()){
throw new IllegalArgumentException("The Dog and Cat queue are empty.");
}else if(!isDogEmpty() && !isCatEmpty()){
while(!isDogEmpty() && !isCatEmpty()){
if(dogQueue.peek().count < catQueue.peek().count){
pollDog();
}else{
pollCat();
}
}
while(!isDogEmpty()){
pollDog();
}
while(!isCatEmpty()){
pollCat();
}
}
}
public Dog pollDog(){
if(isDogEmpty()){
throw new IllegalArgumentException("The Dog queue is empty.");
}
Dog dog = (Dog) dogQueue.peek().pet;
System.out.println(dogQueue.peek().getCount());
dogQueue.poll();
return dog;
}
public Cat pollCat(){
if(isCatEmpty()){
throw new IllegalArgumentException("The Cat queue is empty.");
}
Cat cat = (Cat) catQueue.peek().pet;
System.out.println(catQueue.peek().getCount());
catQueue.poll();
return cat;
}
public boolean isEmpty(){
return catQueue.isEmpty() && dogQueue.isEmpty();
}
public boolean isDogEmpty(){
return dogQueue.isEmpty();
}
public boolean isCatEmpty(){
return catQueue.isEmpty();
}
}
public static void main(String[] args) {
CatDog catDog = new CatDog();
Dog dog1 = new Dog();
Dog dog2 = new Dog();
Dog dog3 = new Dog();
Cat cat1 = new Cat();
Cat cat2 = new Cat();
catDog.add(new PetEnter(dog1));
catDog.add(new PetEnter(cat1));
catDog.add(new PetEnter(dog2));
catDog.add(new PetEnter(dog3));
catDog.add(new PetEnter(cat2));
catDog.pollCat();
catDog.pollDog();
System.out.println("---");
catDog.pollAll();
}
}
6. 流数据中的中位数
题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num)
- 从数据流中添加一个整数到数据结构中。
double findMedian()
- 返回目前所有元素的中位数。来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof
思路:
- 构建一个大根堆
maxHeap
和一个小根堆minHeap
:大根堆是递减的,小根堆是递增的。 - 添加数字
- 当添加数的时候,先判断小根堆是否为空,如果为空,加入小根堆
- 如果小根堆不为空,和小根堆的根节点比较,如果比小根堆的根节点大,加入小根堆;否则加入大根堆。
- 加入一个数后,判断两个堆的尺寸是否相差2,是的话,从数量较多的堆中弹出堆顶到另一个堆中。
- 查找数字
- 如果两个堆尺寸相同,分别取堆首,求和然后除以二。
- 如果不相同,取数量最多的堆的堆顶。
注:也可以先加入大根堆,然后以后再让里面加入数的时候,跟大根堆的堆顶比较。
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;
public class Solution_MedianFinder {
public static class MedianFinder{
Queue<Integer> maxHeap;//大根堆
Queue<Integer> minHeap;//小根堆
public MedianFinder(){
maxHeap = new PriorityQueue<>(new MyComparator());
minHeap = new PriorityQueue<>();
}
public static class MyComparator implements Comparator<Integer>{
@Override
public int compare(Integer t1, Integer t2) {
return t2 - t1;
}
}
public void addNum(int num){
if(minHeap.isEmpty()){
minHeap.add(num);
}else{
int temp = minHeap.peek();
if(num >= temp){
minHeap.add(num);
}else{
maxHeap.add(num);
}
if(minHeap.size() - maxHeap.size() > 1){
maxHeap.add(minHeap.poll());
}
if(maxHeap.size() - minHeap.size() > 1){
minHeap.add(maxHeap.poll());
}
}
}
public double findMedian(){
if(minHeap.size() == 0){
return 0;
}
if(maxHeap.size() == minHeap.size()){
return (double) (maxHeap.peek() + minHeap.peek()) / 2;
}else{
return maxHeap.size() > minHeap.size() ? maxHeap.peek() : minHeap.peek();
}
}
}
public static void main(String[] args) {
MedianFinder finder = new MedianFinder();
finder.addNum(1);
finder.addNum(2);
finder.addNum(3);
finder.addNum(4);
System.out.println(finder.findMedian());
}
}