單鏈表的實現(xiàn)

每個鏈表都有一個節(jié)點next和一個value表示這個節(jié)點的值,一般我們都會有個頭節(jié)點指向第一個元素,而最后一個節(jié)點next指向的是NULL
首先需要定義單鏈表的節(jié)點
template相當于Java中的泛型
template<class E>
struct Node {
E value;
Node<E> *next;
public:
Node(E value, Node<E> *next) {
this->value = value;
this->next = next;
}
};
常用的一些方法:添加,刪除,插入等
template<class E>
class LinkedList {
// 頭指針
Node<E> *head = NULL;
//數(shù)組的長度
int len = 0;
public:
/**
* 添加數(shù)據(jù)
* @param e
*/
void push(E e);
int size();
E get(int index);
void insert(int index, E e);
void remove(int index);
~LinkedList();
Node<E> *node(int i);
};
添加數(shù)據(jù)
添加數(shù)據(jù)添加到的是尾部,那么我們首先需要找到這個鏈表的尾部,可以從頭指針開始遍歷到鏈表的大小
template<class E>
Node<E> *LinkedList<E>::node(int index) {//O(n)的時間復雜度
Node<E> *h = head;
for (int i = 0; i < index; ++i) {
h = h->next;
}
return h;
}
添加數(shù)據(jù)就很簡單了
template<class E>
void LinkedList<E>::push(E e) {
//添加一個數(shù)據(jù)
Node<E> *new_node = new Node<E>(e, NULL);
if (head) {//head是否為空
//找到尾節(jié)點
Node<E> *h = node(len - 1);
h->next = new_node;
} else {
head = new_node;
}
len++;
}
獲取數(shù)據(jù)
template<class E>
E LinkedList<E>::get(int index) {
assert(index >= 0 && index < len);
return node(index)->value;
}
插入數(shù)據(jù)
此時分為兩種情況,插入頭部還是其他位置,插入其實就是將要插入的前一個節(jié)點,指向要插入的節(jié)點,而要插入的節(jié)點,指向之前插入的前一個節(jié)點指向的下一個節(jié)點,如下圖

template<class E>
void LinkedList<E>::insert(int index, E e) {
//找到前一個
Node<E> *new_node = new Node<E>(e, NULL);
if (index == 0) {//插入在頭節(jié)點
//當前頭節(jié)點需要保存下
Node<E> *h = head;
head = new_node;
new_node->next = h;
} else {
//首先你必須獲得了要插入的前一個節(jié)點
Node<E> *prev = node(index - 1);
//保存下prev原本指向的下一個指針的位置
Node<E> *next = prev->next;
//然后前一個指針指向新節(jié)點
prev->next = new_node;
//新節(jié)點指向之前prev原本指向的下一個指針的位置
new_node->next = next;
}
len++;
}
刪除節(jié)點

刪除節(jié)點就是將要刪除的節(jié)點的前一個節(jié)點指向要刪除的節(jié)點的i下一個節(jié)點
template<class E>
void LinkedList<E>::remove(int index) {
assert(index >= 0 && index <= len);
if (index == 0) {
Node<E> *h = head;
head = h->next;
//刪除不要的節(jié)點
delete h;
} else {
Node<E> *prev = node(index - 1);
// 找到要刪除的節(jié)點
Node<E> *cur = prev->next;
//獲取刪除節(jié)點的下個節(jié)點
Node<E> *next = cur->next;
prev->next = next;//合并就是prev->next=cur->next
//刪除當前節(jié)點
delete cur;
}
len--;
}
上面我們是通過遍歷去獲得節(jié)點,此時的時間復雜度是O(n)級別,所以我們需要對這個時間復雜度進行優(yōu)化
單鏈表時間復雜度優(yōu)化
首先我們可以測試下我們沒有優(yōu)化前消耗多少時間
LinkedList<int> linkedList;
time_t start = clock();
for (int i = 0; i < 50000; ++i) {
linkedList.push(i);
}
time_t end = clock();
//沒優(yōu)化14s
__android_log_print(ANDROID_LOG_ERROR, "TAG", "%d", (end - start) / CLOCKS_PER_SEC);
我電腦運行消耗的時間是14s。
優(yōu)化思路:我們每次添加數(shù)據(jù)到最后一個位置的時候,如果知道最后一個位置的節(jié)點,這樣就不需要遍歷for循環(huán)了
LinkedList中定義尾節(jié)點
//尾節(jié)點
Node<E> *last = NULL;
修改代碼后
template<class E>
void LinkedList<E>::push(E e) {
//添加一個數(shù)據(jù)
Node<E> *new_node = new Node<E>(e, NULL);
if (head) {//head是否為空
last->next = new_node;
} else {
head = new_node;
}
last=new_node;
len++;
}
此時我們時間復雜度變成了O(1)級別,再運行剛才測試時間代碼,我們發(fā)現(xiàn)消耗時間是0
雙向鏈表的實現(xiàn)

雙向鏈表結(jié)構(gòu)就是有個前節(jié)點+值+后節(jié)點,當前節(jié)點的next節(jié)點指向下個節(jié)點,而下個節(jié)點的prev指向上個節(jié)點
首先定以一個雙鏈表的節(jié)點
template<class E>
struct Node {
E value;
Node<E> *next, *prev;
public:
Node(E value, Node<E> *prev, Node<E> *next) {
this->value = value;
this->next = next;
this->prev = prev;
}
};
雙鏈表節(jié)點的常用方法定義
template<class E>
class LinkedList {
// 頭指針
Node<E> *head = NULL;
//數(shù)組的長度
int len = 0;
//尾節(jié)點
Node<E> *last = NULL;
public:
/**
* 添加數(shù)據(jù)
* @param e
*/
void push(E e);
int size();
E get(int index);
void insert(int index, E e);
E remove(int index);
~LinkedList();
Node<E> *node(int i);
void linkedLast(E e);
void linkBefore(Node<E> *node, E e);
E unlink(Node<E> *node);
};
t添加數(shù)據(jù)
添加數(shù)據(jù)首先要獲取最后一個節(jié)點,讓最后一個節(jié)點等于要添加的新節(jié)點
template<class E>
void LinkedList<E>::push(E e) {
linkedLast(e);
len++;
}
linkedLast代碼
template<class E>
void LinkedList<E>::linkedLast(E e) {
//保存上一個最后一個節(jié)點
Node<E> *l = last;
Node<E> *new_node = new Node<E>(e, last, NULL);
//最后節(jié)點變成new_node
last = new_node;
if (head) {
l->next = new_node;
} else {
head = new_node;
}
}
二分查找的方式獲得當前位置的上個節(jié)點
/**
* 遍歷找到當前節(jié)點的前一個節(jié)點
*/
template<class E>
Node<E> *LinkedList<E>::node(int index) {//O(n)的時間復雜度
if (index < len >> 1) {
//從前往后遍歷
Node<E> *cur = head;
for (int i = 0; i < index; ++i) {
cur = cur->next;
}
return cur;
} else {
// 從后往前遍歷
Node<E> *cur = last;
for (int i = len - 1; i > index; i--) {
cur = cur->prev;
}
return cur;
}
}
獲得數(shù)據(jù)和鏈表的大小
template<class E>
int LinkedList<E>::size() {
return len;
}
template<class E>
E LinkedList<E>::get(int index) {
assert(index >= 0 && index < len);
return node(index)->value;
}
插入數(shù)據(jù)

首先我們要被插入數(shù)據(jù)的節(jié)點是node節(jié)點,node的前節(jié)點需要進行保存,此時,node的前節(jié)點的next指向的是node節(jié)點,而新節(jié)點的next指向的是node節(jié)點
template<class E>
void LinkedList<E>::insert(int index, E e) {
if (index == len) {
linkedLast(e);
} else {
linkBefore(node(index), e);
}
len++;
}
template<class E>
void LinkedList<E>::linkBefore(Node<E> *node, E e) {
Node<E> *prev = node->prev;
Node<E> *new_node = new Node<E>(e, prev, node);
node->prev = new_node;
if (prev) {
prev->next = new_node;
} else {
head = new_node;
}
}
移除節(jié)點

首先要被刪除的節(jié)點定義名為node,我們需要獲取當前node的前后兩個節(jié)點,然后讓node的前節(jié)點的next指向node的后節(jié)點,而node的后節(jié)點的prev指向node的前節(jié)點
template<class E>
E LinkedList<E>::remove(int index) {
assert(index >= 0 && index <= len);
return unlink(node(index));
}
template<class E>
E LinkedList<E>::unlink(Node<E> *node) {
//首先需要獲得移除的左右節(jié)點
Node<E> *prev = node->prev;
Node<E> *next = node->next;
E value = node->value;
//如果prev等于空
if (prev) {
prev->next = next;
} else {
head = next;
}
if (next) {
next->prev = prev;
} else {
last = prev;
}
delete node;
len--;
return value;
}
最后需要調(diào)用析構(gòu)函數(shù)釋放內(nèi)存
template<class E>
LinkedList<E>::~LinkedList() {
// 析構(gòu)釋放內(nèi)存,析構(gòu)所有的節(jié)點指針就可以了
Node<E> *h = head;
while (h) {
//要保存下個指針,不然當釋放的時候找不到下個指針
Node<E> *next = h->next;
delete h;
h = next;
}
//頭指針和尾指針要置空
head = NULL;
last = NULL;
}