单链表是一种非常基本的数据结构. 虽然C++ STL中已经存在功能强大的List,但我们通过自己实现单链表,对于锻炼C++的基本功,加深对编程的理解与认识还是很有益的. 另外,自己的代码,在使用过程中更容易和敢于修改,更容易灵活定制自己的需求.
在实现单链表的过程中,选用模板,可以使得链表得到极大的灵活性.代码的可复用性大大增强.
核心代码分析解读:
1. 定义存放单个节点的数据结构: T可以在调用过程中灵活变换数据类型(基本数据类型,该程序的T不能支持对象数据类型). data为存放的数据, next为指向的下一个节点.
template struct LinkNode
{
T data;
LinkNode* next;
};
2.在添加节点时,我们只传入值.在函数内部构造节点.注意用new来分配内存.之后先将新节点的next指向node后面的节点(假定在node后面插入节点),然后将node的next指向新生成的节点.顺序很重要.
LinkNode *linkNode = new LinkNode();
linkNode->data = data;
linkNode->next = node->next;
node->next = linkNode;
3. 在删除时,需要准备要删除的前驱节点进行记录.是前驱节点指向后继节点.之后可以删除当前节点.注意释放内存,避免内存泄露.之后将指针指向NULL,避免野指针.
preNode->next = needRemoveNode->next;
delete needRemoveNode;
needRemoveNode = NULL;
4. 在对象释放的时候,C++会自动调用析构函数.在析构函数中,要记得释放内存,与将指针置NULL.
LinkNode *iter = head;
LinkNode *preIter = iter;
while (NULL != preIter->next)
{
iter = preIter->next;
preIter->next = iter->next;
cout << iter->data << ” will be deleted in destruct function.” << endl;
delete iter;
iter = NULL;
}
5. C++使用模板时,要注意在调用时,同时要包含.h与.cpp文件.或者将模板函数实现也写在.h文件中(双刃剑). 与通常只包含.h文件不同. 另外,要注意代码改动后,有时候需要进行进行全部重新编译,这个是因为模板在实例化时会产生二次编译,仅使用增量编译有时会产生错误.
程序运行结果如下图:
C++头文件如下:
#ifndef _LINK_LIST_H_
#define _LINK_LIST_H_
#include "Constant.h"
template<typename T> struct LinkNode
{
T data;
LinkNode* next;
};
template<typename T, const unsigned int capacity> class LinkList
{
public:
LinkList();
~LinkList();
bool isEmpty();
bool isFull();
bool insertFromHead(T data);
bool insertAtLocation(T data, unsigned int n);
bool deleteFromHead();
bool deleteFromHead(T& data);
bool deleteAtLocation(unsigned int n);
bool deleteViaData(T data);
bool getFirst(T& data);
unsigned int getSize();
void print();
private:
unsigned int linkNodeNum;
LinkNode<T> *head = NULL;
};
#endif // !_LINK_LIST_H_
C++源文件如下:
#include "LinkList.h"
#include <string>
#include <iostream>
using namespace std;
template<typename T, const unsigned int capacity>
LinkList<T, capacity>::LinkList()
{
linkNodeNum = 0;
head = new LinkNode<T>;
head->next = NULL;
memset(head, 0, sizeof(head));
}
template<typename T, const unsigned int capacity>
LinkList<T, capacity>::~LinkList()
{
LinkNode<T> *iter = head;
LinkNode<T> *preIter = iter;
while (NULL != preIter->next)
{
iter = preIter->next;
preIter->next = iter->next;
cout << iter->data << " will be deleted in destruct function." << endl;
delete iter;
iter = NULL;
}
}
template<typename T, const unsigned int capacity>
bool LinkList<T, capacity>::isEmpty()
{
if (NULL == head->next)
return true;
return false;
}
template<typename T, const unsigned int capacity>
bool LinkList<T, capacity>::isFull()
{
if (capacity == linkNodeNum)
return true;
return false;
}
template<typename T, const unsigned int capacity>
bool LinkList<T, capacity>::insertFromHead(T data)
{
if (NULL == head)
{
head = new LinkNode<T>;
head->next = NULL;
}
if (isFull())
{
return false;
}
LinkNode<T> *linkNode = new LinkNode<T>();
linkNode->data = data;
linkNode->next = NULL;
LinkNode<T> *afterNode = head->next;
head->next = linkNode;
linkNode->next = afterNode;
linkNodeNum++;
return true;
}
//Insert data after nth node
template<typename T, const unsigned int capacity>
bool LinkList<T, capacity>::insertAtLocation(T data, unsigned int n)
{
if (isFull())
return false;
if (linkNodeNum < n)
{
return false;
}
else
{
//Insert after n
LinkNode<T> *node = head;
//Insert at last
for (unsigned int i = 0; i < n; i++)
{
if (NULL != node->next)
{
node = node->next;
}
else
{
//need log the error
return false;
}
}
LinkNode<T> *linkNode = new LinkNode<T>();
linkNode->data = data;
linkNode->next = node->next;
node->next = linkNode;
linkNodeNum++;
}
return true;
}
template<typename T, const unsigned int capacity>
bool LinkList<T, capacity>::deleteFromHead()
{
bool rs = false;
if (isEmpty())
{
rs = false;
}
else
{
LinkNode<T> *needRemoveNode = head->next;
if (NULL == needRemoveNode)
{
rs = false;
}
else
{
LinkNode<T> *nextNode = needRemoveNode->next;
head->next = nextNode;
needRemoveNode->next = NULL;
delete needRemoveNode;
linkNodeNum--;
rs = true;
}
}
return rs;
}
template<typename T, const unsigned int capacity>
bool LinkList<T, capacity>::deleteFromHead(T& data)
{
bool rs = false;
if (isEmpty())
{
rs = false;
}
else
{
LinkNode<T> *needRemoveNode = head->next;
if (NULL == needRemoveNode)
{
rs = false;
}
else
{
data = needRemoveNode->data;
LinkNode<T> *nextNode = needRemoveNode->next;
head->next = nextNode;
needRemoveNode->next = NULL;
delete needRemoveNode;
linkNodeNum--;
rs = true;
}
}
return rs;
}
template<typename T, const unsigned int capacity>
bool LinkList<T, capacity>::deleteAtLocation(unsigned int n)
{
if (isEmpty())
{
return false;
}
if (n > linkNodeNum)
{
return false;
}
else
{
LinkNode<T> *needRemoveNode = head;
LinkNode<T> *preNode = NULL;
for (unsigned int i = 0; i < n; i++)
{
preNode = needRemoveNode;
needRemoveNode = needRemoveNode->next;
if (NULL == needRemoveNode)
{
return false;
}
}
preNode->next = needRemoveNode->next;
delete needRemoveNode;
needRemoveNode = NULL;
linkNodeNum--;
}
return true;
}
template<typename T, const unsigned int capacity>
bool LinkList<T, capacity>::deleteViaData(T data)
{
if (isEmpty())
{
return false;
}
bool isFind = false;
LinkNode<T> *iter = head;
while(NULL != iter->next)
{
LinkNode<T> *preNode = iter;
iter = iter->next;
try
{
if (data == iter->data)
{
preNode->next = iter->next;
delete iter;
iter = NULL;
isFind = true;
linkNodeNum--;
break;
}
}
catch (exception e)
{
cout << e.what() << endl;
return false;
}
}
return isFind;
}
template <typename T, const unsigned int capacity>
bool LinkList<T, capacity>::getFirst(T& data)
{
bool rs = false;
if (isEmpty())
{
rs = false;
}
else
{
if ((NULL == head) || (NULL == head->next))
{
rs = false;
}
else
{
data = head->next->data;
rs = true;
}
}
return rs;
}
template<typename T, const unsigned int capacity>
unsigned int LinkList<T, capacity>::getSize()
{
return linkNodeNum;
}
template<typename T, const unsigned int capacity>
void LinkList<T, capacity>::print()
{
LinkNode<T>* iter = head;
while (NULL != iter->next)
{
iter = iter->next;
cout << iter->data << " ";
}
cout << endl;
}
测试代码如下:
void testLinkList()
{
LinkList<int,LINK_NODE_SIZE> *linkList = new LinkList<int, LINK_NODE_SIZE>();
linkList->insertFromHead(9);
linkList->print();
linkList->insertFromHead(7);
linkList->print();
linkList->insertAtLocation(8, 1);
linkList->print();
linkList->insertAtLocation(6, 8);
linkList->print();
linkList->insertAtLocation(5, 3);
linkList->print();
linkList->insertFromHead(4);
linkList->print();
linkList->insertFromHead(3);
linkList->print();
linkList->deleteFromHead();
linkList->print();
linkList->deleteViaData(7);
linkList->print();
linkList->deleteAtLocation(3);
linkList->print();
linkList->deleteAtLocation(14);
linkList->print();
linkList->deleteViaData(0);
linkList->print();
linkList->deleteViaData(1.2);
linkList->print();
delete linkList;
linkList = NULL;
}
void testLinkList2()
{
LinkList<string, LINK_NODE_SIZE> *linkList = new LinkList<string, LINK_NODE_SIZE>();
linkList->insertFromHead("AA");
linkList->print();
linkList->insertFromHead("BB");
linkList->print();
linkList->insertAtLocation("CC", 1);
linkList->print();
linkList->insertAtLocation("DD", 8);
linkList->print();
linkList->insertAtLocation("EE", 3);
linkList->print();
linkList->insertFromHead("FF");
linkList->print();
linkList->insertFromHead("GG");
linkList->print();
linkList->deleteFromHead();
linkList->print();
linkList->deleteViaData("FF");
linkList->print();
linkList->deleteAtLocation(3);
linkList->print();
linkList->deleteAtLocation(14);
linkList->print();
linkList->deleteViaData("DDD");
linkList->print();
delete linkList;
linkList = NULL;
}