哈嘍,大家好,我是程式設計熊,雙非逆襲選手,位元組跳動、曠視科技前員工,ACM亞洲區域賽金牌,保研985研究生,分享演算法與資料結構、計算機學習經驗,幫助大家進大廠~
公眾號:『程式設計熊』
文章首發於: ACM金牌選手演算法講解《線性表》!戳這裡!
線性表
LeetCode刷題過程中,常常用到的線性表主要包括以下四個重要的資料結構: 陣列、連結串列、棧、佇列。
下面將分別講解陣列、連結串列、棧和佇列。
線性表概述
線性: 這裡的線性是邏輯上的連續,而非物理儲存的連續。
儲存的資料: 線性表是一個有n
個相同型別資料的有序序列。
陣列array
介紹
陣列是物理儲存連續的線性表,其常見的形式為 a[0]、a[1] ... a[n-1]
,a[i-1]
是 a[i]
的前驅,a[i+1]
是 a[i]
的後繼。
基本操作
插入
插入元素,要將插入位置後的元素全部向後移動一位。
下圖以陣列長度為6,資料為0、1、2、3、4、5
,在位置3插入一個元素X舉例。
刪除
刪除元素,要講刪除位置後的元素全部向前移動一位。
下圖以陣列長度為6,資料為0、1、2、3、4、5
,刪除位置3上的元素X舉例。
反轉
翻轉陣列,本質是將陣列儲存的資料進行反轉。
下圖以陣列長度為6,資料為0、1、2、3、4、5
,反轉整個陣列舉例。
例題
LeetCode 27. 移除元素
題意
刪除陣列中所有等於 val
的元素,返回移除後陣列的新長度。要求不使用額外的空間。
示例
輸入:nums = [3,2,2,3], val = 3
輸出:2, nums = [2,2]
題解
陣列的刪除操作,但如何不使用額外的空間呢?因為刪除val
後的陣列的長度小於等於原陣列的長度,因此可以一邊將不等於val
的陣列放入原陣列中,同時判斷原陣列的數是否等於val
。
程式碼
class Solution {
public int removeElement(int[] nums, int val) {
// left 存當前nums陣列中不等於val的數字數量
int left = 0;
for (int right = 0; right < nums.length; right++) {
if (nums[right] != val) {
nums[left] = nums[right];
left++;
}
}
return left;
}
}
習題推薦
LeetCode 35. 搜尋插入位置
連結串列
介紹
連結串列的出現是為了解決陣列插入、刪除帶來的線性開銷。
區別於陣列,連結串列中的元素可以不連續儲存,每一個元素包含該 元素的資料 和 指向連結串列下一個節點的指標。
基本操作
插入
插入元素,要將插入元素前一個位置的指標指向插入元素本身,將插入元素的指標指向前一個位置。
刪除
刪除元素,要將 刪除元素前一個元素的指標 指向 刪除元素後一個元素,程式碼實現上需要將 刪除元素指標指向的位置 記錄下來。
下圖是以長度為5的連結串列,刪除位置3上的元素為例子。
翻轉
翻轉連結串列,可以一邊遍歷一邊用一個臨時變數記錄當前元素的下一個元素指標所指向的位置,然後再將當前元素的下一個元素指標指向自己。
下圖是以長度為5的連結串列,翻轉連結串列為例子。
例題
1. LeetCode 206. 反轉連結串列
題意
給單連結串列的頭節點 head
,請反轉連結串列,並返回反轉後的連結串列。
示例
輸入:head = [1,2,3,4,5]
輸出:[5,4,3,2,1]
題解
按上述連結串列翻轉操作思路實現程式碼。
程式碼
public ListNode reverseList(ListNode head) {
// pre 存的是當前節點的上一個節點
ListNode prev = null;
// curr 存的是當前連結串列遍歷到節點
ListNode curr = head;
while (curr != null) {
// next 存的是當前節點下一個節點
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
習題推薦
- LeetCode237. 刪除連結串列中的節點
- LeetCode 21. 合併兩個有序連結串列
- LeetCode 160. 相交連結串列
棧和佇列
棧介紹
棧被限定必須在棧頂進行插入和刪除操作,因此其特點為是後進先出。
下圖是棧的插入(入棧)、刪除(出棧)示意圖。
佇列介紹
佇列被限定在隊頭進行刪除操作,隊尾進行插入操作,因此其特點為先進先出。
下圖是佇列的插入(入隊)、刪除(出隊)示意圖。
基本操作
棧和佇列的插入和刪除操作上圖已解釋。
例題
LeetCode 155. 最小棧
題意
設計一個支援 push
,pop
,top
操作,並能在常數時間內檢索到最小元素的棧。
push(x)
—— 將元素 x 推入棧中。pop()
—— 刪除棧頂的元素。top()
—— 獲取棧頂元素。getMin()
—— 檢索棧中的最小元素。
示例
輸入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
輸出:
[null,null,null,null,-3,null,0,-2]
解釋:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
題解
新建輔助棧,輔助棧的棧頂表示原棧所有數字最小值,下面分別討論題目要求的四種操作,分別如何實現。
push(x)
: 若插入的數字小於等於輔助棧的棧頂元素,則這個數字在原棧是最小值(之一),我們將其插入輔助棧中。pop():
若原棧刪除的數字等於輔助棧的棧頂元素,則這個數字在原棧是最小值(之一),我們同時原棧和輔助棧的棧頂元素;反之,只刪除原棧棧頂元素。top():
返回原棧的棧頂元素。getMin():
返回輔助棧的棧頂元素。
程式碼
class MinStack {
public Stack<Integer> s, min_s;
public MinStack() {
s = new Stack<>();
min_s= new Stack<>();
}
public void push(int x) {
s.push(x);
if(min_s.isEmpty() || x <= min_s.peek())
min_s.push(x);
}
public void pop() {
if(s.pop().equals(min_s.peek()))
min_s.pop();
}
public int top() {
return s.peek();
}
public int getMin() {
return min_s.peek();
}
}
習題推薦
LeetCode 20. 有效的括號
End
我是程式設計熊,雙非逆襲選手,位元組跳動、曠視科技前員工,ACM亞洲區域賽金牌,保研985研究生,分享演算法與資料結構、計算機學習經驗,幫助大家進大廠~
公眾號:『程式設計熊』,回覆【書】免費領取計算機學習核心資料(從小白到大神)。