LeetCode刷題記錄(五)
阿新 • • 發佈:2018-11-08
LeetCode刷題記錄(五)
今天開始佇列&棧卡片的學習。
1、設計迴圈佇列
題目:
我的思路:
迴圈佇列的工作原理可以參考LeetCode上的介紹,從介紹中我們可以看到:
- 可以定義一個數組來儲存佇列中的元素,迴圈佇列定義了兩個指標,一個指標指向的是佇列的起始位置,一個指標指向的是佇列的結尾;
- 當佇列為空時,起始指標和尾指標都是指向的佇列之外,向佇列中新增一個元素之後,起始指標和尾指標都指向佇列的第一位,以後每新增一個元素,尾指標都會向後移動一位;
- 當刪除一個元素時,起始指標都要向後移動一位,當刪除佇列最後一個元素之後,起始指標和尾指標都指向佇列之外;
- 如果移動起始指標和尾指標到了陣列的結尾,下一個位置可以重新指向陣列的起始位置,以形成一個環形的結構;
- 當佇列起始指標和尾指標都指向佇列之外的時候,可以認為佇列是空的;
- 如果佇列的尾指標指向位置的下一個位置也是起始指標指向的位置,可以認為佇列是滿的。
迴圈佇列實現的關鍵是判斷佇列是滿的和空的的策略。
按照這個思路實現的程式碼如下:
public class MyCircularQueue {
//起始指標
private int front;
//尾指標
private int rear;
//定義陣列存放佇列的元素
private int[] queue;
/** Initialize your data structure here. Set the size of the queue to be k. */
public MyCircularQueue(int k) {
//初始化起始指標、尾指標和佇列
queue = new int[k];
front = -1;
rear = -1;
}
/** Insert an element into the circular queue. Return true if the operation is successful. */
public boolean enQueue(int value) {
//如果佇列已滿,則無法插入元素,返回false
if(isFull()) {
return false;
}
if(isEmpty()) {
//佇列是空的,起始指標和截止指標都要向後移動一位,在尾指標指向的位置插入值
front++;
rear++;
queue[rear] = value;
return true;
} else {
//尾指標向後移動一位,如果已經移動到最後,則尾指標指向0,在尾指標指向的位置插入值
rear = rear + 1 >= queue.length ? 0 : rear + 1;
queue[rear] = value;
return true;
}
}
/** Delete an element from the circular queue. Return true if the operation is successful. */
public boolean deQueue() {
//如果佇列是空的,則無法取出元素,返回false
if(isEmpty()) {
return false;
}
if(front == rear) {
//如果起始指標和尾指標指向同一個值,說明這個值是佇列的最後一個元素,移除這個元素後佇列就空了,需要將起始指標和尾指標都置為-1以便從頭開始
front = -1;
rear = -1;
return true;
} else {
//起始指標向後移動一位,如果起始指標已經移動到最後,則指向0
front = front + 1 >= queue.length ? 0 : front + 1;
return true;
}
}
/** Get the front item from the queue. */
public int Front() {
//如果佇列為空,則返回-1,否則返回起始指標指向的值
if(isEmpty()) {
return -1;
}
return queue[front];
}
/** Get the last item from the queue. */
public int Rear() {
//如果佇列為空,則返回-1,否則返回尾指標指向的值
if(isEmpty()) {
return -1;
}
return queue[rear];
}
/** Checks whether the circular queue is empty or not. */
public boolean isEmpty() {
//如果起始指標指向-1,說明指標還沒開始移動,佇列為空
return front == -1;
}
/** Checks whether the circular queue is full or not. */
public boolean isFull() {
//根據尾指標獲取佇列結尾下一個元素,如果下一個元素就是起始指標指向的元素,說明佇列已經滿了
int next = rear + 1 >= queue.length ? 0 : rear + 1;
if(next == front) {
return true;
} else {
return false;
}
}
}
2、島嶼的個數
題目:
我的思路:
這一題思路是廣度優先搜尋(BFS),在佇列&棧卡片中有介紹 ,根據這裡的介紹我的想法是這樣的:
- 可以定義一個佇列用於臨時存放陣列中的元素,定義一個列表儲存使用過的元素;
- 依次遍歷二維陣列中每個元素,如果這個元素的值是’0’,或者這個元素已經被使用過了,則直接跳過;
- 如果元素值是’1’並且沒有被使用過,則將該元素加入到佇列和列表中,如果佇列不為空,遍歷佇列,獲取佇列中第一個元素的前後左右四個位置的元素,如果獲取到的元素值為’1’並且沒有使用過,就加入到佇列和列表中,然後將佇列的第一個元素刪除,每次迴圈都刪除上次迴圈獲取到的所有佇列元素,就這樣迴圈直到最後一個元素從佇列中刪除說明獲取到一個島嶼;
- 按照這個思路依次遍歷陣列中所有的元素,獲取到所有的島嶼個數。
按照這個思路實現的程式碼如下:
public class IslandNum {
public static void main(String[] args) {
char[][] grid = new char[4][5];
grid[0] = new char[] {'1', '1', '0', '0', '0'};
grid[1] = new char[] {'1', '1', '0', '0', '0'};
grid[2] = new char[] {'0', '0', '1', '0', '0'};
grid[3] = new char[] {'0', '0', '0', '1', '1'};
System.out.println(new IslandNum().numIslands(grid));
}
public int numIslands(char[][] grid) {
//佇列臨時存放元素
Queue<Index> islands = new LinkedList<Index>();
//列表儲存已經使用過的元素
List<Index> usedIndex = new ArrayList<Index>();
int num = 0;
//二維陣列是空的情況
if(grid == null || grid.length == 0 ||
grid[0] == null || grid[0].length == 0) {
return num;
}
//遍歷二維陣列
for(int i = 0; i < grid.length; i++) {
for(int j = 0; j < grid[0].length; j++) {
//如果陣列元素是0或者已經使用過了,直接跳過
if(grid[i][j] == '0' || usedIndex.contains(new Index(i, j))) {
continue;
}
//如果陣列元素是1且沒有使用過,則進行遍歷
if(grid[i][j] == '1' && !usedIndex.contains(new Index(i, j))) {
//向佇列和列表中新增該元素
islands.add(new Index(i, j));
usedIndex.add(new Index(i, j));
while(!islands.isEmpty()) {
int size = islands.size();
//遍歷佇列,獲取佇列中第一個元素前後左右的元素,如果是1並且沒有使用過,加入到佇列和列表中
//因為這裡迴圈每次都會刪除第一個元素,所以每一輪迴圈次數都是迴圈上一輪迴圈新新增進佇列元素的個數
//這個思路就類似於BFS
for(int k = 0; k < size; k++) {
Index head = islands.peek();
if(head.row + 1 < grid.length) {
if(grid[head.row + 1][head.col] == '1'
&& !usedIndex.contains(new Index(head.row + 1, head.col))) {
islands.add(new Index(head.row + 1, head.col));
usedIndex.add(new Index(head.row + 1, head.col));
}
}
if(head.col + 1 < grid[0].length) {
if(grid[head.row][head.col + 1] == '1'
&& !usedIndex.contains(new Index(head.row, head.col + 1))) {
islands.add(new Index(head.row, head.col + 1));
usedIndex.add(new Index(head.row, head.col + 1));
}
}
if(head.row - 1 >= 0) {
if(grid[head.row - 1][head.col] == '1'
&& !usedIndex.contains(new Index(head.row - 1, head.col))) {
islands.add(new Index(head.row - 1, head.col));
usedIndex.add(new Index(head.row - 1, head.col));
}
}
if(head.col - 1 >= 0) {
if(grid[head.row][head.col - 1] == '1'
&& !usedIndex.contains(new Index(head.row, head.col - 1))) {
islands.add(new Index(head.row, head.col - 1));
usedIndex.add(new Index(head.row, head.col - 1));
}
}
//刪除佇列的第一個元素
islands.poll();
}
}
//當佇列全部清空後,島嶼數量加1
num++;
}
}
}
return num;
}
}
/**
* 定義一個內部類儲存二維陣列的索引
*
*/
class Index {
int row;
int col;
public Index(int row, int col) {
this.row = row;
this.col = col;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Index)) {
return false;
}
Index i = (Index) obj;
if(this.row == i.row && this.col == i.col) {
return true;
}
return false;
}
@Override
public String toString() {
return " (" + row + "," + col + ") ";
}
}
反思:
按照上面這個思路雖然能夠解決問題,但是時間複雜度比較高,我在LeetCode上提交之後也是最後一個有十多萬資料的大陣列會報超時的問題,暫時沒有得到解決,上網搜了一下有通過遞迴的思路解題的,我暫時還沒有研究,後續解決了再更新這題的解法。