程式設計師程式碼面試指南 —— 棧和佇列(二)
注:題目來自於《程式設計師程式碼面試指南:IT名企演算法與資料結構題目最優解》,該書是左程雲老師的著作,值得推薦,這裡僅是記錄一下該書中題目的解法和個人理解
題目:貓狗佇列
寵物、貓、狗的類如下:
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(String type) { super("dog"); } } public class Cat extends Pet { public Cat(String type) { super("cat"); } }
問題描述:
實現一種貓狗佇列的結構,要求如下:
使用者可以呼叫add方法將cat類或dog類的例項放入到佇列中;
使用者可以呼叫pollAll方法,將佇列中的所有例項按照進佇列的先後順序依次彈出
使用者可以呼叫pollDog方法,將佇列中的Dog類的例項按照進佇列的先後順序依次彈出
使用者可以呼叫pollCat方法,將佇列中的Cat類的例項按照進佇列的先後順序依次彈出
使用者可以呼叫isEmpty方法,檢查佇列中是否還有dog或cat的例項
使用者可以呼叫isDogEmpty方法,檢查佇列中是否還有dog的例項
使用者可以呼叫isCatEmpty方法,檢查佇列中是否還有cat的例項
思路:
本題考察的是針對特定條件的資料結構設計問題。
改寫一個類,記錄時間戳
建立兩個佇列,一個放Dog例項 dogQ,一個放Cat例項catQ
在將例項入隊的時候,生成對應的PetEnterQueue類的例項,放入對應的佇列中
彈出例項的時候,從對應佇列彈出
如果是按實際順序彈出例項時(pollAll方法),比較對頭的時間戳,誰更早就彈出誰
圖解:
程式碼:
包裝類:
public class PetEnterQueue { /** 使用者原有例項 */ private Pet pet; /** 時間戳 */ private long count; public PetEnterQueue(Pet pet, long count) { this.pet = pet; this.count = count; } public Pet getPet() { return this.pet; } public long getCount() { return this.count; } public String getEnterPetType() { return this.pet.getPetType(); } }
目標對列:
public class DogCatQueue {
private Queue<PetEnterQueue> dogQ;
private Queue<PetEnterQueue> catQ;
private long count;
public DogCatQueue() {
this.dogQ = new LinkedList<>();
this.catQ = new LinkedList<>();
this.count = 0;
}
public void add(Pet pet) {
if ("dog".equals(pet.getPetType())) {
dogQ.add(new PetEnterQueue(pet, this.count++));
} else if ("cat".equals(pet.getPetType())) {
catQ.add(new PetEnterQueue(pet, this.count++));
} else {
throw new RuntimeException("err,not dog or cat");
}
}
public Queue<Pet> pollAll() {
Queue<Pet> petQ = new LinkedList<>();
while (!dogQ.isEmpty() && !catQ.isEmpty()) {
if (dogQ.peek().getCount() < catQ.peek().getCount()) {
petQ.offer(dogQ.poll().getPet());
} else {
petQ.offer(catQ.poll().getPet());
}
}
while (!dogQ.isEmpty()) {
petQ.offer(dogQ.poll().getPet());
}
while (!catQ.isEmpty()) {
petQ.offer(catQ.poll().getPet());
}
return petQ;
}
public Queue<Dog> pollDog() {
Queue<Dog> printDogQ = new LinkedList<>();
while (!isDogQueueEmpty()) {
printDogQ.offer((Dog) dogQ.poll().getPet());
}
return printDogQ;
}
public Queue<Cat> pollCat() {
Queue<Cat> printCatQ = new LinkedList<>();
while (!isCatQueueEmpty()) {
printCatQ.offer((Cat) catQ.poll().getPet());
}
return printCatQ;
}
public boolean isEmpty() {
return dogQ.isEmpty() && catQ.isEmpty();
}
public boolean isCatQueueEmpty() {
return catQ.isEmpty();
}
public boolean isDogQueueEmpty() {
return dogQ.isEmpty();
}
}
測試類:
public class Test {
public static void main(String[] args) {
DogCatQueue queue = new DogCatQueue();
Dog pet1 = new Dog("dog");
Dog pet2 = new Dog("dog");
Dog pet3 = new Dog("dog");
Cat pet4 = new Cat("cat");
queue.add(pet1);
queue.add(pet2);
queue.add(pet3);
queue.add(pet4);
System.out.println(queue.isEmpty());
queue.pollAll();
System.out.println(queue.isDogQueueEmpty());
System.out.println(queue.isCatQueueEmpty());
queue.add(pet4);
Queue<Dog> dogs = queue.pollDog();
for (Dog dog : dogs) {
System.out.println(dog.getPetType());
}
Queue<Cat> cats = queue.pollCat();
for (Cat cat : cats) {
System.out.println(cat.getPetType());
}
System.out.println(queue.isEmpty());
}
}
測試結果,親測可用:
false
true
true
cat
true
題目:用一個棧實現另一個棧的排序
問題描述:
一個棧中元素型別為整型,現在想將該棧從頂到底按大到小的順序排序,只允許申請一個棧,除此之外,可 以申請新的變數,但不能申請額外的資料結構,如何完成排序?
思路:
待排序棧dataStack,輔助棧helpStack
對dataStack進行出棧操作,記作cur,如果cur大於helpStack的棧頂元素,或者helpStack為空,可直接入棧
若cur小於helpstack棧頂元素,則使helpStack棧頂元素出棧,比較新的棧頂元素,知道滿足條件,剩下元素 再入棧(遞迴實現)
圖解:
程式碼:
public class StackOrder {
public static void main(String[] args) {
Stack<Integer> dataStack = new Stack<>();
Stack<Integer> helpStack = new Stack<>();
dataStack.add(3);
dataStack.add(1);
dataStack.add(4);
order(dataStack, helpStack);
for (Integer i : helpStack) {
System.out.println(i);
}
}
private static void order(Stack<Integer> dataStack, Stack<Integer> helpStack) {
if (dataStack == null || dataStack.size() == 0) {
return;
}
while (!dataStack.isEmpty()) {
int cur = dataStack.pop();
helpStackFunction(cur, helpStack);
}
}
private static void helpStackFunction(int cur, Stack<Integer> helpStack) {
if (helpStack.size() == 0 || cur < helpStack.peek()) {
helpStack.push(cur);
return;
}
int i = helpStack.pop();
helpStackFunction(cur, helpStack);
helpStack.push(i);
}
}
測試結果,親測可用:
4
3
1
注:原著中使用的是迴圈方法,這裡採用的是遞迴的方法。
關於此書:可以分兩次刷,第一次刷1-2星題,第二遍刷3-4星題。循序漸進
關於遞迴:遞迴的寫法可能不是特別好理解,這裡分享一下心得,設計遞迴時,可以在草紙上畫出棧記憶體的情況,只考慮變數值即可,想象成一個輔助棧,即可降低遞迴演算法的設計難度,屢試不爽。