单链表示意图:
单链表的每个结点分别由数据域data和指针域next组成,data里面装的是数据,next里面装的是下一个数据所在的地址,由next我们就可以找到下一个数据,这里next作用相当于“链条”,把一个个数据连接起来,这样在存储中,我们可以不必像顺序那样连续的存储,而是分开来存储,这也能体现出单链表的***优点***,即更充分的使用内存;相对比一下,使用顺序表时,为了不使得溢出,就必须认为估测一个合适的大小,往往都偏大,甚至为了安全而申请的很大(数组),但是如果使用单链表就不会有这样的问题。
这是定义链表的结点:
#ifndef NODE_H
#define NODE_H
class Node
{
public:
int data;
Node *next;
void printNode();
} ;
#endif
这是实现打印结点数据:
#include"Node.h"
#include<iostream>
using namespace std;
void Node::printNode()
{
cout<<data<<endl;
}
这是List.h文件的代码:
/*
单链表
*/
#ifndef LIST_H
#define LIST_H
#include"Node.h"
class List
{
public:
List(); //创建线性表
~List(); //销毁线性表
void ClearList(); //清空
bool ListEmpty(); //判空
int ListLength(); //获取线性表长度
bool GetElem(int i,Node *pNode); //获取指定元素
int LocateElem(Node *pNode); //定位元素 寻找第一个满足e的元素的位序
bool PriorElem(Node *currentElem,Node *pPreNode);//获取指定元素的前驱
bool NextElem(Node *currentElem,Node *pNextNode);//获取指定元素的后继
void ListTraverse(); //遍历线性表
bool ListInsert(int i,Node *pNode); //在第i个位置插入元素
bool ListDelete(int i,Node *pNode); //删除第i个位置的元素
bool ListInsertHead(Node *pNode);
bool ListInsertTail(Node *pNode);
private:
Node *m_pList;
int m_iLength;//当前长度
};
#endif
这是List.cpp文件的代码:
// List.cpp : 定义控制台应用程序的入口点。
//
#include<iostream>
#include"List.h"
using namespace std;
List::List()
{
m_pList = new Node;
m_pList->data = 0;
m_pList->next = NULL;
m_iLength = 0;
}
List::~List()
{
ClearList();
delete m_pList;
m_pList = NULL;
}
void List::ClearList()
{
Node *currentNode = m_pList->next;
while(currentNode!=NULL)
{
Node *temp = currentNode->next;
delete currentNode;
currentNode = temp;
}
m_pList->next =NULL;
m_iLength = 0;
}
bool List::ListEmpty()
{
if (0 == m_iLength)
return true;
else
return false;
}
int List::ListLength()
{
return m_iLength;
}
bool List::GetElem(int i, Node *pNode)
{
if (i<0 || i >= m_iLength)
return false;
Node *currentNode = m_pList;
for(int k=0;k<=i;k++)
{
currentNode=currentNode->next;
}
pNode->data = currentNode->data;
return true;
}
int List::LocateElem(Node *pNode)
{
Node *currentNode = m_pList;
int count=0;
while(currentNode->next!=NULL)
{
currentNode = currentNode->next;
if(currentNode->data == pNode->data)
{
return count; //小细节:当有重复时,只会返回第一次出现的
}
count++;//为什么放后面?小细节:头节点数据域无意义,0是我们找到的头节点后的第一个节点
}
return -1;
}
bool List::PriorElem(Node *pCurrentNode, Node *pPreNode)
{
Node *currentNode = m_pList;
Node *currentNodeBefore =NULL;
while(currentNode->next!=NULL)
{
currentNodeBefore=currentNode;
currentNode = currentNode->next;
if(currentNode->data == pCurrentNode->data)
{
if(currentNodeBefore == m_pList)
return false;
pPreNode->data=currentNodeBefore->data;
return true;
}
}
return false;
}
bool List::NextElem(Node *pCurrentNode, Node *pNextNode)
{
Node *currentNode = m_pList;
while(currentNode->next!=NULL)
{
currentNode = currentNode->next;
if(currentNode->data == pCurrentNode->data)
{
if(currentNode->next==NULL)
return false;
pNextNode->data=currentNode->next->data;
return true;
}
}
return false;
}
void List::ListTraverse()
{
Node *currentNode = m_pList;
while(currentNode->next!=NULL)
{
currentNode = currentNode->next;
currentNode->printNode();
}
cout<<"m_iLength = "<<m_iLength<<endl;
}
bool List::ListInsertHead(Node *pNode)
{
Node *temp = m_pList->next; //头节点指向下一个结点的地址赋给temp保存起来
Node *newNode = new Node;
if(newNode == NULL) //如果申请内存失败
return false;
newNode ->data = pNode->data;//数据域先赋给新结点
m_pList->next = newNode; //头结点与新结点连接
newNode->next = temp; //新结点与后面结点连接的
m_iLength++; //插入成功长度加1
return true;
}
bool List::ListInsertTail(Node *pNode)
{
Node *currentNode= m_pList;
while(currentNode->next!=NULL) //先找到尾结点
{
currentNode = currentNode->next;
}
Node *newNode = new Node;
if(newNode == NULL) //申请内存是否成功
return false;
newNode->data = pNode->data; //数据域先赋给新结点
newNode->next=NULL; //插入后充当尾部
currentNode->next =newNode; // 插入前的尾部与新结点连接
m_iLength++; //插入成功长度加1
return true;
}
bool List::ListInsert(int i,Node *pNode)
{
if( i<0 ||i>m_iLength) //插入位置合理性判断
return false;
Node *currentNode = m_pList;
for(int k=0;k<i;k++) //找到要插入的位置
{
currentNode=currentNode->next;
}
Node *newNode = new Node; //申请新结点
if(newNode == NULL) //申请是否成功
return false;
newNode->data = pNode->data; //数据域传入
newNode->next=currentNode->next;//当前结点所指向的下一结点的地址传给新结点
currentNode->next=newNode;//当前结点与新结点连接
m_iLength ++; //插入成功长度加1
return true;
}
bool List::ListDelete(int i,Node *pNode)
{
if( i<0 || i>=m_iLength) //删除合理性判断
return false;
Node *currentNode = m_pList;
Node *currentNodeBefore =NULL;//当前结点前一结点
for(int k=0;k<=i;k++) //找到删除位置和前一结点
{
currentNodeBefore=currentNode ;
currentNode=currentNode->next;
}
currentNodeBefore->next=currentNode->next;//当前结点的前一结点与其后一结点直接相连,相当于删除当前结点
pNode->data=currentNode->data;//删除数据传出
delete currentNode; //释放内存,已经没用了
currentNode =NULL; //为了安全指为NULL
m_iLength--; //删除成功长度-1
return true;
}
上面代码中插入与删除配合结构图更便于理解,结构图示意图如下:
这是验证代码domo.cpp文件:
#include<iostream>
#include"List.h"
using namespace std;
int main(void)
{
Node node1;
node1.data=3;
Node node2;
node2.data=4;
Node node3;
node3.data=5;
Node node4;
node4.data=6;
Node node5;
node5.data=77;
List *pList = new List();
cout<<"从头部插入:"<<endl;
pList->ListInsertHead(&node1);
pList->ListInsertHead(&node2);
pList->ListInsertHead(&node3);
pList->ListInsertHead(&node4);
pList->ListTraverse();
cout<<"清除~~~";
pList->ClearList();
cout<<"清除~~~DONE"<<endl;
cout<<"从尾部插入:"<<endl;
pList->ListInsertTail(&node1);
pList->ListInsertTail(&node2);
pList->ListInsertTail(&node3);
pList->ListInsertTail(&node4);
pList->ListTraverse();
cout<<"从位置2插入 :"<<endl;
pList->ListInsert(2,&node5);
pList->ListTraverse();
cout<<"从位置3删除 :"<<endl;
Node temp;
pList->ListDelete(3,&temp);
pList->ListTraverse();
cout<<"从位置1取出放入temp"<<endl ;
pList->GetElem(1,&temp);
cout<<"temp = "<<temp.data<<endl;
pList->PriorElem(&node2,&temp);
cout<<"从位置1取前驱temp"<<endl ;
cout<<"temp = "<<temp.data<<endl;
pList->NextElem(&node2,&temp);
cout<<"从位置1取后继temp"<<endl ;
cout<<"temp = "<<temp.data<<endl;
cout << "isEmpte:"<<boolalpha<<pList->ListEmpty()<<endl;
delete pList;
pList = NULL;
return 0;
}
运行如图: