线性链表
线性链表的定义
用一组任意的存储单元(可以连续,也可以不连续)存储线性表的数据元素。存储的数据只是逻辑上的相邻,不一定要求物理上的相邻。每一个存储的数据元素称为节点,一个节点包括两个域:其中存储数据元素的叫数据域;存储直接后继存储位置的域称为指针域。指针域中存储的信息称作指针或链。n个结点链接称为一个链表,也就是线性表。
易混淆概念
- 首元结点:链表中存储第一个数据元素的结点。
- 头结点:在首元结点之前附加的一个结点,其指针指向首元结点。
- 头指针:指向链表中第一个结点的指针。若有头结点,则指向头结点;若没有,直接指向首元结点。
头结点的作用
- 便于首元结点的处理
增加了头结点,就可以直接通过保存在头结点中的指针来对首元结点进行操作(就像其他元素一样),无需进行特殊处理。 - 便于空表和非空表的统一处理
无论链表是否为空,头指针都是指向头结点的非空指针。要判定链表是否为空,只需要判断头结点的指针域是否为空就可以。
基本操作的思路
- 初始化:
- 生成新节点作为头结点,用头指针L指向头结点。
- 头结点的指针域置空。
- 向前插入元素:
- 生成一个新结点
*s
- 输入元素值赋给新结点
*s
的数据域 - 将新结点
*s
插入到头结点之后
- 向后插入元素
- 生成一个新结点
*s
- 输入元素值赋给新结点
*
的数据域 - 初始化一个定位指针
*p
,遍历后指向尾结点 - 将新结点
*p
插入到尾结点之后
- 在中间插入元素(前插)
- 生成一个新结点
*s
- 输入元素值赋给新结点
*s
的数据域 - 查找结点ai-1并将指针
p
指向结点ai - 将结点
*s
的指针用户指向结点ai - 将结点
*p
的指针域指向新结点*s
- 删除某元素
- 让定位指针
p
指向结点ai-1 - 新建指针临时保存待删除结点ai的地址,以备释放
- 将结点
*p
的指针域指向ai+1 - 释放结点ai的空间
基本操作的具体实现
SingleLinkList.h
#pragma once
#define status bool
#define OK true
#define ERROR false
#define YES true
#define NO false
template<typename DType>
class Node {
public :
DType data;
Node * next;
};
template<typename DType>
class CSingleLinkList {
private:
Node<DType> *phead; //链表头指针
Node<DType> *head; //头结点
public:
CSingleLinkList(); //构造函数
~CSingleLinkList();
public:
//初始化链表
status InitSList();
//获取链表长度
int GetLength();
//前插结点
status FrontInsert(DType data);
//后插结点
status BackInsert(DType data);
//在指定位置向前插入结点
status InsertByOrder(int order, DType);
//获取指定序号的元素值
status GetElemByOrder(int order, DType &receiver);
//查找指定元素的地址,找不到就返回空地址
Node<DType>* GetLocByElem(DType target);
//删除指定序号的结点
status DelByOrder(int order);
//打印链表
void PrintList();
};
CSingelLinkList.cpp
#include<iostream>
#include"SingleLinkList.h"
using namespace std;
template<typename DType>
CSingleLinkList<DType>::CSingleLinkList() {
cout << "链表创建成功" << endl;
InitSList();
}
//初始化链表
template<typename DType>
status CSingleLinkList<DType>::InitSList() {
head = new Node<DType>;
if (head != NULL) {
head->next = NULL;
phead = head;
return OK;
}
return ERROR;
}
//获取链表长度
template<typename DType>
int CSingleLinkList<DType>::GetLength() {
//新建追踪指针指向头结点
Node<DType> *pTrack = this->head; //直接用head也可以
//记录长度
int length = 0;
//遍历链表开始计数
/*if (!head->pnext) {
cout << "head->pnext is null" << endl;
}*/
while (pTrack->next!= NULL) {
//cout << pTrack->next;
pTrack = pTrack->next;
length++;
}
//cout << length;
return length;
}
//前插入结点
template<typename DType>
status CSingleLinkList<DType>::FrontInsert(DType data) {
//新建结点
Node<DType> *p = new Node<DType>;
//录入数据
p->data = data;
//令其指向头结点后一个
p->next = head->next;
//让头结点指向它
head->next = p;
return OK;
}
//后插结点
template<typename DType>
status CSingleLinkList<DType>::BackInsert(DType data) {
//新建结点保存数据
Node<DType> *p = new Node<DType>;
p->data = data;
//跟踪指针,指向头结点
Node<DType> *pTrack = head;
//遍历让其指向表尾
while (pTrack->next != NULL) {
pTrack = pTrack->next;
}
pTrack->next = p;
p->next = NULL;
return OK;
}
//在指定位置向前插入结点
template<typename DType>
status CSingleLinkList<DType>::InsertByOrder(int order, DType data) {
if (order<1 || order>this->GetLength()) {
return ERROR;
}
Node<DType>* pTrack = head;
Node<DType>* q = new Node<DType>;
q->data = data;
int count = 0;
//遍历到前驱结点
while (count < order-1) {
pTrack = pTrack->next;
count++;
}
q->next = pTrack->next;
pTrack->next = q;
return OK;
}
//返回指定序号的元素值
template<typename DType>
status CSingleLinkList<DType>::GetElemByOrder(int order, DType &receiver) {
int count = 0; //记数器
//获取数据用的指针
Node<DType> *pGet = head;
if (order<1 || order>this->GetLength()) {
return ERROR;
}
while (count != order) {
pGet = pGet->next;
count++;
}
receiver = pGet->data;
return OK;
}
//查找指定元素的地址(返回首个符合的)
template<typename DType>
Node<DType>* CSingleLinkList<DType>::GetLocByElem(DType target) {
//跟踪指针
Node<DType> *pTrack = head;
while(pTrack!=NULL) {
if (pTrack->data == target) {
return pTrack;
}
pTrack = pTrack->next;
}
return NULL;
}
//删除指定序号的结点
template<typename DType>
status CSingleLinkList<DType>::DelByOrder(int order) {
int count = 0;
Node<DType>* pTrack = head;
if (order<1 || order>this->GetLength()) {
return ERROR;
}
//找到被删除结点的前驱节点
while (count < order-1) {
pTrack = pTrack->next;
count++;
}
//保存被删除节点,方便删除
Node<DType>* tmp = pTrack->next;
//让前驱节点指向后驱结点
pTrack->next = tmp->next;
//释放被删除结点
delete tmp;
return OK;
}
//打印链表
template<typename DType>
void CSingleLinkList<DType>::PrintList() {
if (this->GetLength() == 0) {
cout << "链表为空!" << endl;
}
//指向首元结点,头结点不保存数据
Node<DType>* pTrack = head->next;
cout << "================打印链表===============" << endl;
while (pTrack!=NULL) {
cout << pTrack->data << " ";
pTrack = pTrack->next;
}
cout << endl;
}