個人Github地址java
小碼哥《戀上數據結構與算法》筆記node
iOS大廠面試高頻算法題總結github
參考:小碼哥數據結構與算法(二): 鏈表算法
鏈式存儲
的線性表, 全部元素的內存地址不必定是連續的。LinkedList
類,用來管理鏈表數據,其中的size
屬性記錄存儲數據的數量,first
屬性引用鏈表的第0
個元素。Node
,其中的element
屬性用於存儲元素,next
屬性用於指向鏈表中的下一個節點。public class LinkedList<E> {
private int size;
private Node<E> first;
// 元素的數量
int size();
// 是否爲空
boolean isEmpty();
// 是否包含某個元素
boolean contains(E element);
// 添加元素到最後面
void add(E element);
// 返回index位置對應的元素
E get(int index);
// 設置index位置的元素
E set(int index, E element);
// 往index位置添加元素
void add(int index, E element);
// 刪除index位置對應的元素
E remove(int index);
// 查看元素的位置
int indexOf(E element);
// 清除全部元素
void clear();
// 私有類, 鏈表中的節點
private class Node<E> {
E element;
Node<E> next;
// 構造方法
public Node(E element, Node<E> next) {
this.element = element;
this.next = next;
}
}
}
複製代碼
空間
屬性,來決定這個數組的容量
。但鏈表元素是在添加時才建立的,內存地址不必定是連續的。因此鏈表不須要在單獨設計構造方法,使用默認構造方法便可。size加1
。當前鏈表沒有數據,新節點拼接到first
和當前鏈表有數據,新節點拼接到最後的節點
。public void add(E element) {
// 當first等於null時, 說明此事沒有節點, 因此first引用新節點
if (first == null) {
first = new Node<E>(element, null);
}
// 當fitst不等於null時, 說明鏈表中有節點, 此時獲取最後一個節點, 並將該節點的next指向新節點
else {
Node<E> node = node(size - 1);
node.next = new Node<E>(element, null);
}
size++;
}
複製代碼
變動插入位置前一個元素next指針指向
,插入指定位置便可。插入到0的位置,使用first指向新節點
和插入到非0位置,找到前一個節點進行處理
兩種狀況。不能小於0, 也不能大於等於size
,因此咱們在插入元素以前須要先進行索引檢查。protected void outOfBounds(int index) {
throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
}
protected void rangeCheck(int index) {
if (index < 0 || index >= size) {
outOfBounds(index);
}
}
複製代碼
插入
元素代碼以下:public void add(int index, E element) {
// 檢查索引是否越界
rangeCheckForSize(index);
// 當插入到0的位置時
if (index == 0) {
// 將first指向新節點, 新節點的next指向first以前指向的節點
first = new Node<E>(element, first.next);
}else {
// 找到指定位置前面的節點
Node<E> prev = node(index - 1);
// 將前面節點的next指向新節點, 新節點的next指向prev以前指向的節點
prev.next = new Node<>(element, prev.next);
}
size++;
}
複製代碼
添加
元素也能夠簡寫:public void add(E element) {
// 元素添加到size位置, 即添加到最後面
add(size, element);
}
複製代碼
(delete_node)
的前一個節點(pre_node)
,而後經過變動(pre_node)節點next指針指向刪除節點(delete_node)的下一個節點
便可,而後size
減1
。第0個元素
,若是是,則使用first指向第1個節點
。public E remove(int index) {
// 檢查索引是否越界
rangeCheck(index);
// 記錄須要刪除的節點
Node<E> old = first;
// 當刪除第0個元素時, 將first的next指向索引爲`1`的節點便可
if (index == 0) {
first = first.next;
}else {
// 找到前一個元素
Node<E> prev = node(index - 1);
// 記錄須要刪除的節點
old = prev.next;
// 將prev的next指向須要刪除節點的後一個節點
prev.next = old.next;
}
// size-1
size--;
// 返回刪除的元素
return old.element;
}
複製代碼
first
指向null
,釋放鏈表全部node,同時size
置爲0
便可。public void clear() {
first = null;
size = 0;
}
複製代碼
private Node<E> node(int index) {
//越界判斷
rangeCheck(index);
Node<E> node = first;
for (int i = 0; i < index; i++) {
node = node.next;
}
return node;
}
複製代碼
node
節點的element
便可。public E set(int index, E element) {
// 找到對應節點, node方法中已經判斷了索引是否越界
Node<E> node = node(index);
// 記錄舊元素
E old = node.element;
// 覆蓋元素
node.element = element;
// 返回舊元素
return old;
}
複製代碼
public E get(int index) {
// node方法中已經判斷了索引是否越界
return node(index).element;
}
複製代碼
element
爲null
,則須要分兩種狀況處理。private static final int ELEMENT_ON_FOUND = -1;
public int indexOf(E element) {
// 取出頭結點
Node<E> node = first;
// 當element爲null時的處理
if (element == null) {
// 遍歷節點, 找到存儲爲null的節點, 返回索引
for (int i = 0; i < size; i++) {
if (node.element == null) return i;
node = node.next;
}
}else {
for (int i = 0; i < size; i++) {
// 遍歷節點, 找到存儲的元素與指定元素相等的節點, 返回索引
if (element.equals(node.element)) return i;
node = node.next;
}
}
// 沒有找到元素對應的節點, 返回ELEMENT_ON_FOUND
return ELEMENT_ON_FOUND;
}
複製代碼
size
的值。public int size() {
return size;
}
複製代碼
size
是否等於0
便可。public boolean isEmpty() {
return size == 0;
}
複製代碼
ELEMENT_ON_FOUND
便可。public boolean contains(E element) {
return indexOf(element) != ELEMENT_ON_FOUND;
}
複製代碼
public String toString() {
StringBuilder string = new StringBuilder();
string.append("size = ").append(size).append(", [");
Node<E> node = first;
for (int i = 0; i < size; i++) {
if (i != 0) {
string.append(",");
}
string.append(node.element);
node = node.next;
}
string.append("]");
return string.toString();
}
複製代碼
到此爲止,咱們成功的實現了鏈表。markdown