我用Java寫的鏈表是帶頭尾節點的雙向鏈表,這次用C++透徹一點,不帶頭尾節點的單向鏈表。
實現了一些常用方法,沒順序表詳細。
鏈表的查找、插入、刪除的時間複雜度都是O(N)。因爲它要遍歷到指定位置,如果在原地進行上述操作的話,時間複雜度爲O(1)。
但總歸是比不過順序表,這大概就是爲什麼我們總是被教導:大部分時間都只需要vector(ArrayList)!
但鏈表還是有它自己的優勢的,以它爲基礎的高級數據結構能有更好地性能。
#include <iostream>
#include <Windows.h>
#include <sstream>
#include "ArrayListException.h"
using namespace std;
template <typename T>
class LNode
{
public:
T element;//存放數據
LNode<T> *next;//指向下一個節點
LNode(const T &e, LNode<T> *nxt=nullptr)
:element(e),next(nxt) {}
};
template <typename T>
class CppLinkedList //不帶頭結點的單項鍊表
{
private:
unsigned list_size;//鏈表長度
LNode<T> *firstNode;//第一個節點
void checkIndex(unsigned idx) const;
public:
CppLinkedList() :list_size(0), firstNode(nullptr) {}//初始化鏈表
CppLinkedList(const CppLinkedList<T> &c);//拷貝複製
~CppLinkedList();
//返回表是否爲空
bool isEmpty() { return list_size == 0; }
//返回表中元素數
unsigned size() const { return list_size; }
//在末端插入元素e
void add_back(const T &e);
//在索引出插入元素e
void insert(unsigned idx, const T &e);
//獲取索引出元素的引用
T get(unsigned idx) const;
//將索引出元素值改爲e
void set(unsigned idx, const T &e);
//刪除索引出元素
void erase(unsigned idx);
//返回元素的索引
int indexOf(const T &e) const;
//刪除一個範圍內的元素,[s , r)
void removeRange(int s, int r);
//打印
void print_list() const;
//forward iterator
class iterator;
iterator begin() { return iterator(firstNode); }
iterator end() { return iterator(NULL); }
class iterator
{
public:
// 類定義
typedef forward_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
// 構造器
iterator(LNode<T>* theNode = NULL)
{
node = theNode;
}
// 解引用
T& operator*() const { return node->element; }
T* operator->() const { return &node->element; }
iterator& operator++() // 前置++
{
node = node->next;
return *this;
}
iterator operator++(int) // 後置++
{
iterator old = *this;
node = node->next;
return old;
}
bool operator!=(const iterator right) const
{
return node != right.node;
}
bool operator==(const iterator right) const
{
return node == right.node;
}
protected:
LNode<T>* node;
};
};
template <typename T>
CppLinkedList<T>::CppLinkedList(const CppLinkedList<T> &c)
:list_size(c.list_size),firstNode(nullptr)
{
if (list_size == 0)
return;
LNode<T> *srcNode = c.firstNode;
firstNode = new LNode<T>(srcNode->element,nullptr);
LNode<T> *currentNode = firstNode;
while (srcNode->next != nullptr)
{
currentNode->next = new LNode<T>(srcNode->next->element, nullptr);
srcNode = srcNode->next;
currentNode = currentNode->next;
}
}
template <typename T>
CppLinkedList<T>::~CppLinkedList()
{
while (firstNode != nullptr)
{
LNode<T> *currentNode = firstNode;
cout << currentNode->element << " ";
firstNode = firstNode->next;
delete currentNode;
currentNode = firstNode;
}
}
template <typename T>//O(1) 檢查get、erase、操作符[]的索引
void CppLinkedList<T>::checkIndex(unsigned idx) const
{
if (idx < 0 || idx >= list_size)
throw invalidIndex("index out of range");
}
template <typename T>//O(N)
void CppLinkedList<T>::add_back(const T &e)
{
LNode<T> *newNode = new LNode<T>(e, nullptr);
++list_size;//在if語句前++,免得寫兩遍
if (list_size == 1)
{
firstNode = newNode;
return;
}
LNode<T> *currentNode = firstNode;
for (int i = 0; i < list_size - 2; ++i)
currentNode = currentNode->next;
currentNode->next = newNode;
}
template <typename T>//O(N)
void CppLinkedList<T>::insert(unsigned idx, const T &e)
{
if (idx < 0 || idx > list_size)//檢查索引位置
throw invalidIndex("index out of range!");
if (idx == 0)//插入第一個位置
firstNode = new LNode<T>(e, firstNode);
else {
LNode<T> *currentNode = firstNode;
//讓currenetNode跑到索引的前一個位置
for (int i = 0; i < idx - 1; ++i)
currentNode = currentNode->next;
currentNode->next = new LNode<T>(e, currentNode->next);
}
++list_size;
}
template <typename T>//O(N)
T CppLinkedList<T>::get(unsigned idx) const
{
checkIndex(idx);
LNode<T> *currentNode = firstNode;
//遍歷到索引位置
for (int i = 0; i < idx; ++i)
currentNode = currentNode->next;
return currentNode->element;
}
template <typename T>//O(N)
void CppLinkedList<T>::set(unsigned idx, const T &e)
{
checkIndex(idx);
LNode<T> *currentNode = firstNode;
for (int i = 0; i < idx; ++i)
currentNode = currentNode->next;
currentNode->element = e;
}
template <typename T>//O(N),因爲要遍歷到索引位置,索引還是會花費線性時間。刪除操作只花費常數時間。
void CppLinkedList<T>::erase(unsigned idx)
{
checkIndex(idx);
LNode<T> *currentNode = firstNode;
if (idx == 0)
{
firstNode = firstNode->next;
delete currentNode;
}
else
{
for (int i = 0; i < idx - 1; ++i)
currentNode = currentNode->next;
LNode<T> *oldNode = currentNode->next;
currentNode->next = oldNode->next;
delete oldNode;
}
--list_size;
}
template <typename T>
void CppLinkedList<T>::removeRange(int s, int r)
{
//檢查索引合法性
if (s > r) {
ostringstream st;
st << s << "必須小於等於" << r;
throw invalidIndex(st.str());
}
checkIndex(s);
checkIndex(r - 1);
LNode<T> *leftNode = firstNode;
for (int i = 0; i < s - 1; ++i)//到s的前一個元素
leftNode = leftNode->next;
if (s == 0)
leftNode = new LNode<T>(firstNode->element, firstNode);
LNode<T> *rightNode = leftNode->next;
for (int i = 0; i < r - s; ++i)//到索引爲r的元素
{
LNode<T> *deleteNode = rightNode;
rightNode = rightNode->next;
delete deleteNode;
}
leftNode->next = rightNode;
list_size -= (r - s);
if (s == 0)
{
delete leftNode;
firstNode = rightNode;
}
}
template <typename T>//O(N)
int CppLinkedList<T>::indexOf(const T &e) const
{
LNode<T> *currenetNode = firstNode;
for (int i = 0; i < list_size; ++i)
{
if (currenetNode->element == e)
return i;
currenetNode = currenetNode->next;
}
return -1;
}
template <typename T>//O(N)
void CppLinkedList<T>::print_list() const
{
LNode<T> *currentNode = firstNode;
while (currentNode != nullptr)
{
cout << currentNode->element << " ";
currentNode = currentNode->next;
}
cout << endl;
}
int main()
{
CppLinkedList<int> c;
for (int i = 0; i < 100; ++i)
c.add_back(i);
CppLinkedList<int> d = c;
d.removeRange(0,100);
d.print_list();
cout << d.indexOf(8899) << " " << d.indexOf(55) <<" " << d.size()<< endl;
system("pause");
return 0;
}