链表
单向链表
链表是链式存储的,非顺序存储的,与顺序存储的数组不同;数组存储必须先申请足够的内存的空间,然后再存储数据,这可能导致内存空间浪费,链表则不同,链表是需要多少存储空间,则向内存空间申请多少内存,不会导致内存浪费。
链表:
优点:存储数据灵活,节省内存空间,增删查改数据方便等;
缺点:访问速度不如数组快
链表的存储机制,如图:
代码:
以 类模板 的形式写的代码,所以只有一个头文件:linkedList.h
Node 模板类 以嵌套类的方式定义在类内部,以实现数据隐藏和封装
#ifndef SIMPLY_LINKEDLIST_LINKEDLIST_H
#define SIMPLY_LINKEDLIST_LINKEDLIST_H
#include <iostream>
template <class T>
class LinkedList{
private:
// 嵌套类模板:private
template <class >
class Node{
public:
T element;
Node<T>* next;
Node(){
element = T();
next = nullptr;
}
Node(T element, Node<T>* next):element(element){
// this->element = element;
this->next = next;
}
~Node()=default;
};
private:
const int ELEMENT_NOT_FOUND = -1;
int Size;
Node<T>* first;
Node<T>* node(int index) const{
Node<T>* node = first;
for(int i=0; i < index; ++i){
node = node->next;
}
return node;
}
public:
LinkedList();
LinkedList(T element, Node<T>* next);
~LinkedList();
int size() const;
void clear();
bool contains(T element);
void add(T element);
void add(T element, int index);
T get(int index) const;
T set(T element, int index);
T remove(int index) ;
int getPos(T element);
void display() const;
};
template <class T>
LinkedList<T>::LinkedList():Size(0){
Node<T>();
first = nullptr;
}
template <class T>
LinkedList<T>::LinkedList(T element, Node<T>* next):Size(0){
Node<T>(element, next);
first = nullptr;
}
template <class T>
LinkedList<T>::~LinkedList() {
Node<T>* ptr = first;
int cnt = 0;
while(ptr != nullptr){
auto q = ptr->next;
delete ptr;
ptr = q;
++cnt;
}
first = nullptr;
std::cout << "...Finally, calling destructor..." << cnt << std::endl;
}
template <class T>
int LinkedList<T>::size() const {
return Size;
}
template <class T>
void LinkedList<T>::clear() {
Node<T>* node = first;
int cnt = 0;
for (int i=0; i < Size; ++i){
auto q = node->next;
delete node;
node = q;
++cnt;
}
Size = 0;
first = nullptr;
std::cout << "clear--->cnt: " << cnt << std::endl;
}
template <class T>
bool LinkedList<T>::contains(T element) {
return getPos(element) != ELEMENT_NOT_FOUND;
}
template <class T>
void LinkedList<T>::add(T element) {
add(element, Size);
}
template <class T>
void LinkedList<T>::add(T element, int index) {
if (index == 0){
first = new Node<T>(element, first);
}else{
Node<T>* prev = node(index-1);
prev->next = new Node<T>(element, prev->next);
}
++Size;
}
template <class T>
T LinkedList<T>::get(int index) const{
return node(index)->element;
}
template <class T>
T LinkedList<T>::set(T element, int index) {
Node<T>* ptr = node(index);
T old = ptr->element;
ptr->element = element;
return old;
}
template <class T>
T LinkedList<T>::remove(int index) {
Node<T>* oldPtr = first;
if (index == 0){
first = first->next; // 删除 第一个节点;
}else{
/* 这里容易错:假如要删除 node(1), 则要获取指向 node(0) 处的指针,并将 node(0)->next = node(0)->next->next */
Node<T>* prev = node(index-1);
oldPtr = prev->next;
prev->next = prev->next->next;
}
T old = oldPtr->element;
--Size;
delete oldPtr; // 删除节点后,释放 add 时 new 出来的节点内存;
return old;
}
template <class T>
int LinkedList<T>::getPos(T element) {
int position = ELEMENT_NOT_FOUND;
Node<T>* ptr = first;
for (int i=0; i < Size; ++i){
if(element == ptr->element){
position = i;
break;
}
ptr = ptr->next;
}
return position;
}
template <class T>
void LinkedList<T>::display() const {
for(int i=0; i < Size; ++i){
std::cout << get(i) << " ";
}
std::cout << std::endl;
}
#endif //SIMPLY_LINKEDLIST_LINKEDLIST_H
注意:
- C++ 内存的使用和释放需注意:向堆申请内存,程序退出后应释放多少内存。
- 删除节点时(即:remove() ):记得释放被删除节点的内存。
- 清空节点时(即:clear() ):要释放所有 节点 的内存,这和析构函数的作用有点像。
- 析构函数: 释放所有 节点 占用的堆内存空间,并将 first(即:头结点)指向 nullptr。
测试函数
main.cpp
#include <iostream>
#include <utility>
#include "linkedList.h"
int main(){
// auto listPtr = new LinkedList_struct<T>();
auto listPtr = new LinkedList<T>(0, nullptr);
// function---->add()
listPtr->add(100, 0);
listPtr->add(10000, listPtr->size());
listPtr->add(20);
listPtr->add(500);
for (int i = 0; i < 10; ++i){
listPtr->add(i*100, i+2);
}
listPtr->display();
cout << "---------------------------------------------------------------------" << endl;
// function---->remove()
listPtr->remove(0);
T a = listPtr->remove(3);
cout << "a<--remove: " << a << endl;
listPtr->remove(listPtr->size()-1);
listPtr->display();
cout << "---------------------------------------------------------------------" << endl;
// function---->set()
a = listPtr->set(10, 0);
cout << "a<--set: " << a << endl;
a = listPtr->set(20000, listPtr->size()-1);
cout << "a<--set: " << a << endl;
a = listPtr->set(30000, 3);
cout << "a<--set: " << a << endl;
listPtr->display();
cout << "---------------------------------------------------------------------" << endl;
// function---->get()
a = listPtr->get(0);
cout << "a<--get: " << a << endl;
a = listPtr->get(listPtr->size()-1);
cout << "a<--get: " << a << endl;
a = listPtr->get(3);
cout << "a<--get: " << a << endl;
cout << "---------------------------------------------------------------------" << endl;
// function---->getPos()
int pos = listPtr->getPos(10);
cout << "pos(element->10): " << pos << endl;
pos = listPtr->getPos(20000);
cout << "pos(element->20000): " << pos << endl;
pos = listPtr->getPos(30000);
cout << "pos(element->30000): " << pos << endl;
cout << "---------------------------------------------------------------------" << endl;
// function---->contains()
bool tr = listPtr->contains(10);
cout << "true(1) or false(0): " << tr << endl;
tr = listPtr->contains(20000);
cout << "true(1) or false(0): " << tr << endl;
tr = listPtr->contains(-10);
cout << "true(1) or false(0): " << tr << endl;
cout << "---------------------------------------------------------------------" << endl;
// function---->clear()
listPtr->clear();
listPtr->display();
cout << "listPtr---->size: " << listPtr->size() << endl;
cout << "---------------------------------------------------------------------" << endl;
delete listPtr;
return 0;
}
运行结果:
E:\programme\C_C++\CLion\C++\algorithm\LinkedList\simply_LinkedList\cmake-build-debug\simply_LinkedList.exe
100 10000 0 100 200 300 400 500 600 700 800 900 20 500
---------------------------------------------------------------------
a<--remove: 200
10000 0 100 300 400 500 600 700 800 900 20
---------------------------------------------------------------------
a<--set: 10000
a<--set: 20
a<--set: 300
10 0 100 30000 400 500 600 700 800 900 20000
---------------------------------------------------------------------
a<--get: 10
a<--get: 20000
a<--get: 30000
---------------------------------------------------------------------
pos(element->10): 0
pos(element->20000): 10
pos(element->30000): 3
---------------------------------------------------------------------
true(1) or false(0): 1
true(1) or false(0): 1
true(1) or false(0): 0
---------------------------------------------------------------------
clear--->cnt: 11
listPtr---->size: 0
---------------------------------------------------------------------
...Finally, calling destructor...0
Process finished with exit code 0