与数组类似,链表也是一种线性数据结构。这里有一个例子:
正如你所看到的,链表中的每一个元素其实是一个单独的对象,而全部对象都经过每一个元素中的引用字段连接在一块儿。node
链表有两种类型:单链表和双链表。上面给出的例子是一个单链表,这里有一个双链表的例子:
单链表中的每一个结点不只包含值,还包含连接到下一个结点的引用字段。经过这种方式,单链表将全部结点按顺序组织起来。ios
下面是一个单链表的例子:
蓝色箭头显示单个连接列表中的结点是如何组合在一块儿的。数组
如下是单链表中结点的典型定义:数据结构
template<typename T> class Node { public: T e; Node<T>*next; Node():e(0),next(nullptr){} Node(T& E):e(E),next(nullptr){} Node(T& E,Node<T>*Next):e(E),next(Next){} };
在大多数状况下,咱们将使用头结点(第一个结点)来表示整个列表。3d
与数组不一样,咱们没法在常量时间内访问单链表中的随机元素。 若是咱们想要得到第 i 个元素,咱们必须从头结点逐个遍历。 咱们按索引来访问元素平均要花费 O(N) 时间,其中 N 是链表的长度。指针
例如,在上面的示例中,头结点是 23。访问第 3 个结点的惟一方法是使用头结点中的“next”字段到达第 2 个结点(结点 6); 而后使用结点 6 的“next”字段,咱们可以访问第 3 个结点。code
若是咱们想在给定的结点 prev 以后添加新值,咱们应该:对象
与数组不一样,咱们不须要将全部元素移动到插入元素以后。所以,您能够在 O(1) 时间复杂度中将新结点插入到链表中,这很是高效。blog
让咱们在第二个结点 6 以后插入一个新的值 9。图片
咱们将首先初始化一个值为 9 的新结点。而后将结点 9 连接到结点 15。最后,将结点 6 连接到结点 9。
插入以后,咱们的链表将以下所示:
template<typename T> void LinkedList<T>::add(int index, T e) { if(index >= 0 && index <= size) { Node<T>*prev = dummyHead; for(int i = 0;i<index;++i){ prev = prev->next; //遍历到node为要插入节点的前一节点 } //第一种写法 // Node<T>*newNode = Node<T>(e); //建立新节点传入直e // newNode->next = node->next; //新节点的next指向要插入节点 // node->next = newNode; //要插入节点的前一节点的next指向新节点 //第二种写法 prev->next = new Node<T>(e,prev->next); //建立一个节点传入直和让新节点的next指向插入节点,而后要插入节点的前一节点的next指向新节点 ++size; } }
众所周知,咱们使用头结点来表明整个列表。
所以,在列表开头添加新节点时更新头结点 head 相当重要。
例如,让咱们在列表的开头添加一个新结点 9。
咱们初始化一个新结点 9 并将其连接到当前头结点 23。
指定结点 9 为新的头结点。
template<typename T> void LinkedList<T>::addFirst(T e) { //第一种写法 // Node<T>*node = new Node<T>(e); //建立一个节点,把直放入节点 // node->next = head; //让建立的节点的下next指向当前头 // head = node; //头指向新建立的节点 //第二种写法 // head = new Node<T>(e,head); //新建立一个节点传入数据和头让新节点的next指向head,而后head在指向新节点 // ++size; add(0,e); }
template<typename T> void LinkedList<T>::addLast(int e) { add(size,e); }
若是咱们想从单链表中删除现有结点 cur,能够分两步完成:
在咱们的第一步中,咱们须要找出 prev 和 next。使用 cur 的参考字段很容易找出 next,可是,咱们必须从头结点遍历链表,以找出 prev,它的平均时间是 O(N),其中 N 是链表的长度。所以,删除结点的时间复杂度将是 O(N)。
空间复杂度为 O(1),由于咱们只须要常量空间来存储指针。
让咱们尝试把结点 6从上面的单链表中删除。
从头遍历链表,直到咱们找到前一个结点 prev,即结点 23
将 prev(结点 23)与 next(结点 15)连接
结点 6 如今不在咱们的单链表中。
template<typename T> T LinkedList<T>::remove(const int index) { if(index>=0 && index<=size) { Node<T>*prev = dummyHead; for(int i = 0;i<index;++i) //找到要删除节点的前一个节点 { prev = prev->next; } Node<T>*retNode = prev->next; //保存要删除的节点 prev->next = retNode->next; //让前一节点next指向要删除节点的后一节点 retNode->next = nullptr; //要删除节点next指向空 --size; return retNode->e; } }
若是咱们想删除第一个结点,策略会有所不一样。
正如以前所提到的,咱们使用头结点 head 来表示链表。咱们的头是下面示例中的黑色结点 23。
若是想要删除第一个结点,咱们能够简单地将下一个结点分配给head。也就是说,删除以后咱们的头将会是结点 6。
链表从头结点开始,所以结点 23 再也不在咱们的链表中。
template<typename T> T LinkedList<T>::removeFirst() { return remove(0); }
template<typename T> T LinkedList<T>::removeLast() { return remove(size-1); }
#ifndef C___LINKEDLIST_H #define C___LINKEDLIST_H #include <iostream> template<typename T> class Node { public: T e; Node<T>*next; Node():e(0),next(nullptr){} Node(T& E):e(E),next(nullptr){} Node(T& E,Node<T>*Next):e(E),next(Next){} }; template<typename T> class LinkedList { public: LinkedList(); //返回连表大小 int getSize()const; //判断是否为空 bool isEmpty()const; //头插入 void addFirst(T e); //为插入 void addLast(T e); //插入 void add(int index, T e); //练习:获取链表第👈index个位置的元素 T get(const int index); //获取链表第一个元素 T getFirst(); //获取链表最后一个元素 T getLast(); //练习:修改链表第👈index个位置的元素 void set(const int index,const T&e); //查找链表是否有元素e bool contains(const T&e)const; //删除元素 T remove(const int index); //删除头 T removeFirst(); //删除尾 T removeLast(); //打印链表 void print()const; private: Node<T>*dummyHead; //虚拟头节点,不存数据 int size; //记录大小 }; template<typename T> int LinkedList<T>::getSize() const { return size; } template<typename T> bool LinkedList<T>::isEmpty() const { return size == 0; } template<typename T> void LinkedList<T>::addFirst(T e) { //第一种写法 // Node<T>*node = new Node<T>(e); //建立一个节点,把直放入节点 // node->next = head; //让建立的节点的下next指向当前头 // head = node; //头指向新建立的节点 //第二种写法 // head = new Node<T>(e,head); //新建立一个节点传入数据和头让新节点的next指向head,而后head在指向新节点 // ++size; add(0,e); } template<typename T> void LinkedList<T>::add(int index, T e) { if(index >= 0 && index <= size) { Node<T>*prev = dummyHead; for(int i = 0;i<index;++i){ prev = prev->next; //遍历到node为要插入节点的前一节点 } //第一种写法 // Node<T>*newNode = Node<T>(e); //建立新节点传入直e // newNode->next = node->next; //新节点的next指向要插入节点 // node->next = newNode; //要插入节点的前一节点的next指向新节点 //第二种写法 prev->next = new Node<T>(e,prev->next); //建立一个节点传入直和让新节点的next指向插入节点,而后要插入节点的前一节点的next指向新节点 ++size; } } template<typename T> void LinkedList<T>::addLast(T e) { add(size,e); } template<typename T> LinkedList<T>::LinkedList() { dummyHead = new Node<T>(); size = 0; } template<typename T> T LinkedList<T>::get(const int index) { if(index>=0 && index<=size) { Node<T>*cur = dummyHead->next; //把第一个元素的位置给cur for(int i = 0;i<index;++i) { cur = cur->next; } return cur->e;//返回👈第index个节点的元素 } } template<typename T> T LinkedList<T>::getFirst() { return get(0); } template<typename T> T LinkedList<T>::getLast() { return get(size-1); } template<typename T> void LinkedList<T>::set(const int index, const T &e) { if(index>=0 && index<=size) { Node<T> *cur = dummyHead->next; for (int i = 0; i < index; ++i) { cur = cur->next; } cur->e = e; } } template<typename T> bool LinkedList<T>::contains(const T &e) const { //第一种遍历 // Node<T>*cur = dummyHead->next; // while(cur!= nullptr) // { // if(cur->e == e) // { // return true; // } // cur = cur->next; // } // return false; //第二种遍历 for(Node<T>*cur = dummyHead->next;cur!= nullptr;cur = cur->next) { if(cur->e == e) //若是找到元素返回true { return true; } } return false; //不然返回false } template<typename T> void LinkedList<T>::print() const { std::cout << "LinkedList: size = " << size << std::endl; std::cout << "["; for(Node<T>*cur = dummyHead->next;cur!= nullptr;cur = cur->next) { std::cout<<cur->e<<"->"; } std::cout<<"NULL"<<"]"<<std::endl; } template<typename T> T LinkedList<T>::remove(const int index) { if(index>=0 && index<=size) { Node<T>*prev = dummyHead; for(int i = 0;i<index;++i) //找到要删除节点的前一个节点 { prev = prev->next; } Node<T>*retNode = prev->next; //保存要删除的节点 prev->next = retNode->next; //让前一节点next指向要删除节点的后一节点 retNode->next = nullptr; //要删除节点next指向空 --size; return retNode->e; } } template<typename T> T LinkedList<T>::removeLast() { return remove(size-1); } template<typename T> T LinkedList<T>::removeFirst() { return remove(0); } #endif
int main() { LinkedList<int> *ll; ll = new LinkedList<int>(); for(int i = 0;i<10;++i) { ll->addFirst(i); ll->print(); } ll->add(2,666); ll->print(); cout<<endl; cout<<"get(2)"<<ll->get(2)<<endl; cout<<"getSize()"<<ll->getSize()<<endl; cout<<"getFirst()"<<ll->getFirst()<<endl; cout<<"getLast()"<<ll->getLast()<<endl; cout<<"isEmpty"<<ll->isEmpty()<<endl; cout<<"contains"<<ll->contains(666)<<endl; ll->set(3,999); ll->addLast(000); ll->print(); cout<<endl; ll->removeLast(); ll->removeFirst(); ll->remove(1); ll->print(); return 0; }