佇列:最近的請求次數 (Leetcode 933 / Leetcode 232 / 劍指09 / Leetcode 225 / Leetcode 862 )
阿新 • • 發佈:2021-08-01
/** * 暴力解法 * 1.建立陣列,存放所有的請求 * 整型陣列,存放10000個元素 * 2.把當前請求存入陣列 * 記錄最後一次存入的索引,從0開始 * 3.統計距離此次請求前3000毫秒之間的請求次數 * 從最後一次存放位置倒序遍歷 * * @param t 時長為 t(單位:毫秒) 的請求 * @return 過去3000毫秒內有多少次請求:[t-3000, t] */ public int ping(int t) { // 2.把當前請求存入陣列 int end = 0; for (int i = 0; i < array.length; i++) { if (array[i] == 0) { // 細節:陣列元素是0,該位置沒有存過請求 array[i] = t; end = i; // 記錄最近一次請求存放的索引 break; } } // 3.統計距離此次請求前3000毫秒之間的請求次數 int count = 0; // 計數器 while (array[end] >= t - 3000) { count++; if (--end < 0) { // 防止越界 break; } } return count; }
// 1.建立陣列,存放所有的請求 // int[] array = new int[10000]; int[] array = new int[3002]; // 2.記錄起止索引,從0開始 int start = 0, end = 0;/** * 優化解法:雙指標 * 1.建立陣列存放請求:int[3002] * 2.額外定義開始指標 * start=0,end=0,記錄起止索引 * 3.存放請求後,更新起止索引 * end++; 從上次的開始索引(start)向後查詢 * 直到新的合法的起始位置 * 4.通過end與start差值計算請求次數 * * @param t 時長為 t(單位:毫秒) 的請求 * @return 過去3000毫秒內有多少次請求:[t-3000, t] */ public int ping(int t) { // 3.存放請求後,更新起止索引 array[end++] = t; // 存放最近一次請求,結束索引加 1 end = end == array.length ? 0 : end; //越界後,從0開始 // 從start位置開始,正向查詢符合要求的請求次數 while (array[start] < t - 3000) { // 過濾掉所有不符合要求的數 據 start ++; start = start == array.length ? 0 : start; } // 4.通過end與start差值計算請求次數 if (start > end) { // 請求次數超過陣列容量,發生了溢位 return array.length - (start - end); } // 此時,end為最新一次請求 + 1 的索引,start是3000毫秒前的第一次合 法請求索引 return end - start; }
/** * 最優解:佇列解法 * 1.使用連結串列實現一個佇列 * 定義屬性:隊頭-head、隊尾-tail、長度-size * 定義方法:新增節點-add(int)、移除節點-poll() 、佇列長度-size() * 定義內部類:Node,封裝每次入隊的請求資料和指向下一個節點的指標 * 2.每次請求向佇列尾部追加節點 * 3.迴圈檢查隊頭資料是否合法 * 不合法則移除該節點 * 4.返回佇列長度 * @param t * @return */ public int ping(int t) { // 2.每次請求向佇列尾部追加節點 q.add(t); // 3.迴圈檢查隊頭資料是否合法 while (q.head.getVal() < t - 3000) q.poll(); // 4.返回佇列長度 return q.size(); }
/** * 最優解:佇列解法 * 1.使用連結串列實現一個佇列 * 定義屬性:隊頭-head、隊尾-tail、長度-size * 定義方法:新增節點-add(int)、移除節點-poll() 、佇列長度-size() * 定義內部類:Node,封裝每次入隊的請求資料和指向下一個節點的指標 * 2.每次請求向佇列尾部追加節點 * 3.迴圈檢查隊頭資料是否合法 * 不合法則移除該節點 * 4.返回佇列長度 * @param t * @return */ public int ping(int t) { // 2.每次請求向佇列尾部追加節點 q.add(t); // 3.迴圈檢查隊頭資料是否合法 while (q.head.getVal() < t - 3000) q.poll(); // 4.返回佇列長度 return q.size(); }
佇列實現程式碼:
Queue q; public RecentCounter() { q = new Queue(); } class Queue { // 1.使用連結串列實現一個佇列 Node head; Node tail; int size = 0; public Queue() { } public void add(int x) { // 向尾部新增一個節點 Node last = tail; Node newNode = new Node(x); tail = newNode; // 尾指標指向新節點 if (last == null) { // 第一次新增資料 head = newNode; tail = newNode; } else { last.next = newNode; // 前一個節點指向新節點 } size++; // 每新增一個節點,佇列長度+1 } public int poll() { // 從頭部移除一個節點 int headVal = head.val; // 獲取頭節點的資料 Node next = head.next; head.next = null; // 連結串列第一個節點斷開 head = next; // head指標指向後一個節點 if (next == null) { // 佇列中的最後一個元素 tail = null; // 處理尾指標 } size--; // 每移出一個節點,佇列長度減1 return headVal; } public int size() { return size; } class Node { // 佇列節點:連結串列結構 int val; Node next; Node(int x) { val = x; } int getVal() { return val; } } }
輔助資料結構:佇列。程式碼如下:
class Queue { // 1.使用連結串列實現一個佇列 Node head; Node tail; int size = 0; public Queue() { } public void add(int x) {} public int poll() {} public int size() {} class Node { // 佇列節點:連結串列結構 int val; Node next; Node(int x) { val = x; } int getVal() { return val; } } }
輸入:inputs = [[1],[100],[3001],[3002]]
輸出:[1,2,3,3]