数组和链表(为何引入链表)
数组是计算机根据事先定义好的数据类型和长度自动分配一个连续的存储单元,相同数组的位置和距离都是固定的,换句话说,任何一个数组元素的地址都可以用一个公式计算出来,因此这种数据结构可以有效的对数组元素进行随机访问。但是如果要对数组元素进行插入和删除操作,就会引起大量数据的移动,从而使简单的数据处理变得非常复杂、低效。
为了有效解决这一问题,引入了链表这一数据结构。
链表
链表是动态数据结构,它用一组任意的存储单元(可连续,也可不连续)存放数据元素。
链表中每个元素称为节点,每个节点由数据域和指针域组成,每个节点中的指针域指向下一个节点。头指针表示链表的开始,用来指向第一个节点,尾指针的指针域为NULL,表示链表的结束。
链表中每个节点都可包含若干数据域和指针域,节点中只有一个指针的链表成为单链表。
定义一个单链表节点:
struct Node{
int nData;
Node* pNext;
Node(){
nData = 0;
pNext = NULL;
}
};
定义一个单链表类,包含链表的插入、删除、输出等成员函数。
class CList{
private:
Node* pHead;
Node* GetHead(){
return pHead;
};
public:
CList(){
pHead = new Node;
}
~CList(){
delete pHead;
}
void Insert(int nData,int nBehindData);
void Delete(int nData);
void Output();
};
链表节点的访问
链表中各个节点是由指针连接在一起的,其存储地址未必连续,所以,其访问地址不能像数组那样由公式计算出来。链表的访问只能从头指针(pHead)开始,通过节点指针遍历整个链表。
void CList::Output(){
while(pHead != NULL){
printf("%d\n",pHead->nData);
pHead = pHead->pNext;
}
}
链表节点的插入
若在链表的节点a之前插入节点b,考虑以下情况:
* 当前链表是个空表,则 pHead->b * 若a是链表第一个节点,插入后b为第一个节点,即 pHead->b->a * 链表中存在节点a,并且a不是第一个节点,则需找出a的上一个节点a‘,使a’指向b,b指向a,即a’->b->a * 如果链表中不存在节点a,则将节点b作为最后一个节点,即b->NULL
void CList::Insert(int nData,int nBehindData){
Node* pData = new Node;
pData->nData = nData;
if(pHead == NULL){
pHead = pData;
}else{
if(pHead->nData == nBehindData){
pData->pNext = pHead;
pHead = pData;
}else{
while(pHead->pNext->nData != nBehindData && pHead->pNext != NULL){
pHead = pHead->pNext;
}
if(pHead->pNext->nData == nBehindData){
pData = pHead->pNext;
pHead = pData;
}else{
pHead->pNext = pData;
}
}
}
}
链表节点的删除
若在链表中删除节点a并释放空间,考虑以下情况:
* 节点a是第一个节点,则 pHead 指向a的下一个节点 * 节点a存在链表中并且不是第一个节点,则使a的上一个节点指向a的下一个节点 * 链表为空或a不存在,不做处理
void CList::Delete(int nData){
Node* temp = NULL;
if(pHead == NULL) return ;
if(pHead->nData == nData){
temp = pHead->pNext;
delete pHead;
pHead = temp;
}else{
while(pHead->pNext->nData != nData && pHead != NULL){
pHead = pHead->pNext;
}
if(pHead != NULL){
temp = pHead->pNext->pNext;
delete pHead->pNext;
pHead = temp;
}
}
}
至此,简单的单链表基本操作就完成了