1. 程式人生 > >程式設計師程式碼面試指南 —— 棧和佇列(二)

程式設計師程式碼面試指南 —— 棧和佇列(二)

注:題目來自於《程式設計師程式碼面試指南: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星題。循序漸進
關於遞迴:遞迴的寫法可能不是特別好理解,這裡分享一下心得,設計遞迴時,可以在草紙上畫出棧記憶體的情況,只考慮變數值即可,想象成一個輔助棧,即可降低遞迴演算法的設計難度,屢試不爽。