一、線性表(廣義的陣列)
在演算法題中,我們一般使用到的線性表一般有兩種,且它們的優缺點如下:
- 陣列
- 優點:可以使用
[]
運算子進行隨機讀寫 - 缺點:陣列大小固定,不能動態新增資料
- 優點:可以使用
- List物件
- 優點:可以動態新增資料
- 缺點:讀寫資料需要使用
get(int index)
和set(int index, Object object)
,和陣列相比比較麻煩
1. 陣列
這裡陣列的主要用法和c++比較類似,這裡主要寫一下一些特殊的操作以及Arrays工具類提供的一些方法。
一維陣列的定義和初始化
① 直接指定固定大小:
int[] arr = new int[n];
則開闢的空間會填充上預設值:
- 數值型別填充
0
boolean
型別填充false
- 物件型別填充
null
② 定義時進行初始化
int[] arr = new int[]{1, 2, 3, 4, 5};
二維陣列的定義和初始化
① 和一維陣列一樣直接給定兩個維度的大小(行數和列數):
int[][] matrix = new int[m][n];
② 和c++類似,二維陣列也可以像c++中的type** matrix
一樣,先給第一個維度分配空間,然後再為第二個維度分配不同的空間,例如下面的程式碼分配下三角矩陣:
int n = 5;
int[][] matrix = new int[n][];
for (int i = 0; i < n; i++) {
matrix[i] = new int[i + 1];
}
列印展示:
Arrays工具類的一些常用方法
在Java中原生陣列實際上並不是完全的物件導向的,對於List物件希望進行某項操作只需要使用.+方法
即可,但陣列型別本身卻沒有帶有這些操作,因而Arrays工具類填補了這部分的空白。
① Arrays.fill()
Arrays.fill有兩個常見的使用:
Arrays.fill(int[] array, int value)
Arrays.fill(int[] array, int start, int end, int val)
這個函式是用於填充陣列的,第一個引數是陣列,第二個引數是填充的值,而第二種用法規定了填充的起止下標:[start, end)
。
② Arrays.sort()
排序函式,一般也有兩種引數填充方法:
Arrays.sort(int[] array)
Arrays.sort(Object[] array, Comparator c)
tips 只有物件陣列才能使用這種方式
第一種方式是按照預設的方式進行排序,數字型別按從小到大排,字串型別按字典序排,而第二種方式中填寫的第二個引數是用於改變預設的排序規則的,例如這裡我希望從大到小排序():
Integer[] arr = new Integer[]{1, 2, 3, 4, 5};
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
// 或者使用下面的更加簡潔的lambda表示式
Arrays.sort(arr, (num1, num2) -> num2 - num1);
由於新增Comparator物件的這種方式第一個引數只能是物件陣列,因此我這裡不再使用int[]
而是改為了使用Integer[]
。
這裡就會出現一個這樣的需求:如果我原來的型別是
int[]
,那麼如何轉換為Integer[]
呢?這裡我們可以使用下面的程式碼進行轉化(其他的例如boolean到Boolean也可以按照如下方式轉化):int[] arrOrigin = new int[]{1, 2, 3, 4, 5}; Integer[] arr = (Integer[]) Arrays.stream(arrOrigin).boxed().toArray();
即將原陣列轉為stream物件後呼叫boxed方法得到
Stream<Integer>
,最後再呼叫Stream
類中的toArray()
成員方法即可從流重新轉為陣列。而Integer[]
想要轉為int[]
則需要呼叫Stream
類的mapToInt(Integer::intValue).toArray()
。
③ Arrays.toString(int[] array)
這個方法可以得到陣列完整的內容,而如果直接使用arr.toString()
只會得到物件的地址等無用資訊。
④ Arrays.asList(int[] array)
將陣列轉為List物件:
Integer[] arr = new Integer[]{1, 2, 3, 4, 5};
List<Integer> list = Arrays.asList(arr);
此外這個函式還可以分散填寫各個List物件初始元素的值:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
⑤ 反轉陣列
這裡可以使用工具類反轉的陣列也僅限是物件陣列(非物件陣列可能只能手寫反轉演算法了),參考程式碼如下:
String[] strArr = new String[]{"e", "d", "c", "b", "a"};
Collections.reverse(Arrays.asList(strArr));
輸出結果:
[a, b, c, d, e]
另外還有一些例如Arrays.copyOf等方法不太常用,這裡不再詳細介紹。
2. List介面容器
物件的構建
實現類一般使用ArrayList
(此外還有LinkedList
,但不常用),構造物件方式如下:
List<Integer> list = new ArrayList<>();
讀寫和插入刪除資料
讀:E get(int index)
寫:E set(int index, E element)
插入:boolean add(E e)
和void add(int index, E element)
刪除:boolean remove(Object o)
和E remove(int index)
排序
使用List
的介面方法sort(Comparator c)
(數字從大到小排):
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.sort((num1, num2) -> num2 - num1);
輸出:
[5, 4, 3, 2, 1]
或者也可以使用Collections.sort(List l, Comparator c)
,可以達到相同的效果。
反轉陣列
// Collections工具類靜態方法:Collections.reverse(List list)
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Collections.reverse(list);
List轉為陣列
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Integer[] arr = (Integer[]) list.toArray();
二、字串
以下api如果沒有標註則預設為String類成員方法。
操作 | api | 說明 |
---|---|---|
獲取長度 | int length() |
|
獲取下標對應字元 | char charAt(int index) |
|
轉換為字元陣列 | char[] toCharArray() |
|
取子串 | String substring(int beginIdx, int endIdx) |
其中endIdx 是可選的 |
轉為int等數字型別 | 靜態方法 Integer.parseInt(String str) |
這是int型別的,其他型別以此類推 |
數值型別轉為字串 | 靜態方法 String.valueOf(T val) |
這裡的T可以是int、double等型別 |
按分隔符切分字串 | String[] split(String regex) |
填入的是正規表示式 |
反轉字串 | StringBuilder成員方法 string reverse() |
需要藉助StringBuilder |
這些容器類基本都會包含有獲取元素數量(
size()
)和判斷是否為空(empty()
或isEmpty()
)等相同的方法,因此後面的api表格只列出該類特有的操作。
三、Map和Set
1. Map
Map這裡一般使用的實現為HashMap
,少數需要按照鍵進行排序時使用到TreeMap
,構造物件如下:
Map<String, String> map = new HashMap<>();
常用操作和api:
操作 | api |
---|---|
讀 | V get(Object key) |
寫 | V put(K key, V value) |
是否包含key | boolean containsKey(Object key) |
是否包含value | boolean containsValue(Object value) |
遍歷Map:
// 1. 使用forEach + lambda表示式(推薦)
map.forEach((key, value)-> {
...
});
// 2. 使用for結合keySet()
for (String key : map.keySet()) {
String value = map.get(key);
...
}
2. Set
和Map類似,這裡Set的實現一般也選擇HashSet
,少數需要按照鍵進行排序時使用到TreeSet
,構造物件如下:
Set<String> set = new HashSet<>();
常用操作和api:
操作 | api |
---|---|
插入元素 | boolean add(E e) |
刪除元素 | boolean remove(Object o) |
是否包含元素 | boolean contains(Object o) |
同理,set也可以使用forEach+lambda以及增強for兩種寫法遍歷元素。
四、棧Stack和佇列Queue
1. 棧Stack
構造物件:
Stack<String> stack = new Stack<>();
常用操作和api:
操作 | api |
---|---|
壓棧 | E push(E item) |
彈棧 | E pop() |
檢視棧頂元素 | E peek() |
2. 佇列Queue
Queue是一個介面,一般實現類取ArrayDeque。構造物件程式碼如下:
Queue<String> queue = new ArrayDeque<>();
常用操作和api:
操作 | api |
---|---|
入隊 | boolean offer(E e) |
出隊 | E poll() |
檢視隊首 | E peek() |
五、優先佇列
構造物件:
PriorityQueue<Integer> heap = new PriorityQueue<>();
使用無引數建構函式時得到的是小頂堆,如果我們希望得到大頂堆則需要填入一個Comparator引數,如下為構造int型別大頂堆的方式:
PriorityQueue<Integer> heap = new PriorityQueue<>((o1, o2) -> o2 - o1);
其中填入的lambda表示式為新建Comparator匿名內部類的語法糖。
常用操作和api:
操作 | api |
---|---|
入隊 | boolean offer(E e) |
出隊 | boolean poll(E e) |
檢視隊首(堆頂) | E peek() |