概念
一、单链表是什么?
单链表是线性表的链式存取结构;是用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的;
结点结构:
![](https://i-blog.csdnimg.cn/blog_migrate/8f4d3b27b1021bccfc163b3421d315db.png)
申请新的结点:s = new Node<int> ;
![](https://i-blog.csdnimg.cn/blog_migrate/3082f2b334fd51c178727e88c427d0be.png)
二、带头结点与不带头结点的单链表,在进行操作时的区别
当对单链表的表首元素进行删除或插入操作时,要进行区别处理,无头结点的单链表进行头指针的更新。(对表头特殊处理)
带头结点的单链表,无论单链表是否为空链表,头指针都指向头结点。不带头结点的单链表,当单链表为非空链表时,头指针指向链表第一个结点(head),当单链表为空链表时,头指针指向空(NULL)。
创建单链表
1.结点结构
//创建结点
template<classT>
struct Node {
T data;
Node<T> *next;
};
LinkList模板类
template<classT>
class LinkList
{
Node<T> *first,*rear,*p,*s,*q; //一般情况头指针用*first
// 带结点单链表中头结点用*head
public:
LinkList();
LinkList(Ta[], intn);
~LinkList();
int Length(); //求链表长度
T Get(intpos); //按值查找
void Insert(inti, Titem);
void HeadInsert(Ta[], intn); //头插法
void TailInsert(Ta[], intn); //尾插法
void Insert(inti, Titem);
void Detele(inti);
int CountX(Tdata);
void PrintLinkList(); //遍历单链表
};
3. 构造函数
图解
![](https://i-blog.csdnimg.cn/blog_migrate/28f6d7b83b5b51976afc08e2d64fb76d.png)
带头结点
//头指针指向的结点为该单链表的头结点
template<classT>
LinkList<T>::LinkList() //无参构造函数
{
head=new Node<T>; //声明一个头结点
head->next=NULL; //头结点指向的首结点为空
}
template<classT>
LinkList<T>::LinkList(Ta[], intn) //有参构造函数
{
Node<T>*s;
Node<T>*head=new Node<T>; //创建头结点
rear=head;
for (inti=0; i<n; i++) {
s=new Node<T>; //申请新结点
s->data=a[i];
rear->next=s;
rear=s;
}
rear->next=NULL;
}
不带头结点
//头指针指向的结点为该单链表的首结点
template<classT> //无参构造函数
LinkList<T>::LinkList()
{
first=new Node<T>; //头指针指向新节点
first=NULL; //首结点数据为空
}
template<classT>
LinkList<T>::LinkList(Ta[], intn) {
Node<T>*s=new Node<T>; //建立新结点指针
first=s; //头指针指向新结点 s
Node<T>*rear=first; // rear指向首结点
for (inti=0; i<n; i++) {
s=newNode<T>; //新结点
s->data=a[i];
rear->next=s; //插入 s
rear=s; // s为尾结点
}
rear->next=NULL; //尾结点next设为空
}
4.析构函数
template<classT>
LinkList<T>::~LinkList() {
Node<T>*rear=first; // 头指针赋值给尾结点
while (rear) { //当结点指针不为零
Node<T>*q=rear; // Node<T> *q = first;
rear=rear->next; //尾指针rear 递推 , 最后rear=NULL
deleteq;
}
first=NULL; //当结点指针为零 ,则为空
}
5. 头插法建表
[] 将待插入结点插在头结点的后面
带头结点
![](https://i-blog.csdnimg.cn/blog_migrate/f972490ade819c0f1e997255073e9fd0.png)
template<classT>
LinkList<T>::~LinkList() {
Node<T>*rear=first; // 头指针赋值给尾结点
while (rear) { //当结点指针不为零
Node<T>*q=rear; // Node<T> *q = first;
rear=rear->next; //尾指针rear 递推 , 最后rear=NULL
deleteq;
}
first=NULL; //当结点指针为零 ,则为空
}
template<classT>
void LinkList<T>::HeadInsert(Ta[], intn) {
head=new Node<T>;
head->next=NULL;
for (inti=0; i<n; i++) {
//生成新结点
//链接在头结点和首结点之间
s=newNode<T>;
s->data=a[i];
s->next=head->next;
head->next=s;
}
}
不带头结点
![](https://i-blog.csdnimg.cn/blog_migrate/d1ea80784e37e6d1ec9224db875263dc.png)
//插入到第一个位置
s->next=first;
first=s;
//插入到其他位置
//rear = 插入之前的结点;
s->next=rear->next;
rear->next=s;
template<classT>
void LinkList<T>::HeadInsert(Ta[], intn) {
for (inti=0; i<n; i++) {
rear=first; // rear指针指向原首元结点地址first
s=new Node<T>; //新结点 s
s->data=a[i];
if (i==0) {
s->next=NULL; // 头插法数组第一个元素最终为表尾元素
}
else {
s->next=rear; //在尾部加上新结点 s
}
first=s; // 头指针指向 s, s为首元结点 (更新头指针)
}
}
6. 尾插法建表
[将待插入结点插在终端结点的后面]
带头结点
![](https://i-blog.csdnimg.cn/blog_migrate/0eedf10b23942de385a5e5635e7e7d53.png)
template<classT>
void LinkList<T>::TailInsert(Ta[], intn) {
rear=head;
for (inti=0; i<n; i++) {
//生成新结点
//链接在尾结点后面
//尾结点后移
s=new Node<T>;
s->data=a[i];
rear->next=s;
rear=s;
}
rear->next=NULL;
}
不带头结点
template<classT>
void LinkList<T>::TailInsert(Ta[], intn)
{
rear=first; // rear指针指向原首元结点地址first
for (inti=0; i<n; i++) {
s=new Node<T>; //新结点 s
s->data=a[i];
s->next=NULL;
if(i==0){
first=s ; // 如果当前是第一个元素则将头指针指向当前节点
}
else {
rear->next=s; //在尾部加上新结点 s
rear=s; // s 变为尾结点
}
rear=s;
rear->next=NULL; //尾结点 next为空
}
}
7.插入(后插)
[] 在第 i个位置插入元素 item
带头结点
template<classT>
void LinkList<T>::Insert(inti, Titem) {
p=head;
int j=0;
while (p&&j<i-1) {
p=p->next;
j++;
}
if (!p||i==0) {
cerr<<"插入位置非法。";
exit(1);
}
s=newNode<T>;
s->data=item;
s->next=p->next;
p->next=s;
}
不带头结点
template<classT>
void LinkList<T>::Insert(inti, Titem) {
Node<T>*p=first; // p指向首元结点
Node<T>*s=new Node<T>;
if(i==1){ //插入第1个结点的操作
s->data=item;
s->next=first; //将 s放到first(首元素)前面
first=s; //头指针指向 s (更改头指针)
}
else {
intj=1; //第二个元素开始
while (p&&j<i-1) { //找到插入元素位置的前驱结点
p=p->next;
j++;
}
if (!p||i==0) {
cerr<<"插入位置非法。";
exit(1);
}
s->data=item;
s->next=p->next; // s->next为第 i个元素地址
p->next=s; // 第 i-1元素 next为新元素 s
}
}
8.求链表长度
//带头结点
template<classT>
int LinkList<T>::Length() {
p=head->next; //不带头结点:p = first;
int num=0;
while (p) {
p=p->next;
num++;
}
return num;
}
9.查找
按位查找
//根据位置获取元素
template <class T>
T LinkList<T>::Get(int pos)
{
p = head->next;
int j=1;
while(p&&j<pos) {
p=p->next;
j++;
}
if(!p||j>pos) {
cerr<<"查找位置非法"<<endl;
exit(1);
}
else {
return p->data;
}
}
按值查找
//寻找某元素的位置
template <class T>
int LinkList<T>::Locate(T item) {
p = head->next;
int j=1;
while(p&&p->data!=item) {
p=p->next;
j++;
}
if(p) {
return j;
}
else {
return 0;
}
}
10.遍历单链表
//带头结点
template<classT>
void LinkList<T>::PrintLinkList() {
p=head->next; //不带头结点 p = first;
if(!p){ //空链表
cout<<"The LinkList is empty !"<<endl;
}
else{
cout<<"LinkList: " ;
while (p) { // p不为NULL时依次输出
cout<<p->data ;
if(p->next!=NULL) { cout<<","; }
p=p->next;
}
}
cout<<endl;
}
完整代码及实例
LinkList.h
带头结点
#include<iostream>
using namespace std;
template<classT>
structNode {
Tdata;
Node<T>*next;
};
template<classT>
class LinkList
{
Node<T> *head,*rear,*q,*p,*s;
public:
LinkList();
LinkList(Ta[], intn);
~LinkList();
int Length();
T Get(intpos);
int CountX(Tdata);
void PrintLinkList();
void HeadInsert(Ta[], intn);
void TailInsert(Ta[], intn);
void Insert(inti, Titem);
void Detele(inti);
};
template<classT>
LinkList<T>::LinkList()
{
head=new Node<T>;
head->next=NULL;
}
template<classT>
LinkList<T>::LinkList(Ta[], intn) {
Node<T>*s;
head=new Node<T>;
rear=head;
for (inti=0; i<n; i++) {
s=new Node<T>;
s->data=a[i];
rear->next=s;
rear=s;
}
rear->next=NULL;
}
template<classT>
LinkList<T>::~LinkList() {
p=head;
while (p) {
q=p;
p=p->next;
deleteq;
}
head=NULL;
}
template<classT>
int LinkList<T>::Length() {
p=head->next;
int num=0;
while (p) {
p=p->next;
num++;
}
return num;
}
template<classT>
T LinkList<T>::Get(intpos) {
p=head->next;
int j=1;
while (p&&j<pos) {
p=p->next;
j++;
}
if (!p||j>pos)
{
cerr<<"查找位置非法。";
exit(1);
}
else return p->data;
}
template<classT>
int LinkList<T>::CountX(Tdata) {
int count=0;
p=head->next;
intj=0;
while(p)
{
if(p->data==data) {
count++;
}
p=p->next;
}
return count;
}
template<classT>
void LinkList<T>::Insert(inti, Titem) {
p=head;
intj=0;
while (p&&j<i-1) {
p=p->next;
j++;
}
if (!p||i==0) {
cerr<<"插入位置非法。";
exit(1);
}
s=new Node<T>;
s->data=item;
s->next=p->next;
p->next=s;
}
template<classT>
voidLinkList<T>::HeadInsert(Ta[], intn) {
head=new Node<T>;
for (inti=0; i<n; i++) {
s=new Node<T>;
s->data=a[i];
s->next=head->next;
head->next=s;
}
}
template<classT>
void LinkList<T>::TailInsert(Ta[], intn) {
rear=head;
for (inti=0; i<n; i++) {
s=new Node<T>;
s->data=a[i];
rear->next=s;
rear=s;
}
rear->next=NULL;
}
template<classT>
void LinkList<T>::Detele(inti) {
p=head;
intj=0;
while (p&&j<i-1) {
p=p->next;
j++;
}
if (!p||!p->next||i==0) {
cerr<<"删除位置非法。";
exit(1);
}
else
{
q=p->next;
//T x = q->data;
cout<<q->data<<endl;
p->next=q->next;
delete q;
}
}
template<classT>
void LinkList<T>::PrintLinkList() {
p=head->next;
if(!p){
cout<<"The LinkList is empty !"<<endl;
}
else{
cout<<"LinkList: " ;
while (p) {
cout<<p->data ;
if(p->next!=NULL) { cout<<","; }
p=p->next;
}
}
cout<<endl;
}
不带头结点
#include<iostream>
using namespace std;
template<classT>
struct Node {
T data;
Node<T> *next;
};
template<classT>
class LinkList
{
Node<T> *first,*rear,*p,*s,*q;
public:
LinkList();
LinkList(Ta[], intn);
~LinkList();
int Length();
T Get(intpos);
int CountX(Tdata);
void PrintLinkList();
void HeadInsert(Ta[], intn);
void TailInsert(Ta[], intn);
void Insert(inti, Titem);
void Detele(inti);
};
template<classT> //无参构造函数
LinkList<T>::LinkList()
{
first=new Node<T>; //头指针指向新节点
first=NULL; //新节点数据为空
}
template<classT>
LinkList<T>::LinkList(Ta[], intn) {
Node<T> *s=new Node<T>; //建立新结点指针
first=s; //头指针指向新结点 s
Node<T> *rear = first; // 头指针赋值给尾结点
for (inti=0; i<n; i++) {
s = new Node<T>; //新结点
s->data=a[i];
rear->next = s; //插入 s
rear = s; // s为尾结点
}
rear->next = NULL; //尾结点next设为空
}
template<classT>
LinkList<T>::~LinkList() {
Node<T> *rear = first; // 头指针赋值给尾结点
while (rear) { //当结点指针不为零
Node<T> *q = rear; // Node<T> *q = first;
rear = rear->next; //尾指针rear 递推 , 最后rear=0
delete q;
}
first=NULL; //当结点指针为零 ,则为空
}
template<classT>
int LinkList<T>::Length() {
Node<T>*p = first;
int num=0;
while (p) {
p=p->next;
num++;
}
return num;
}
template<classT>
T LinkList<T>::Get(intpos) {
Node<T>*p = first;
int j = 1;
while (p && j<pos) { //找到第 pos的元素
p = p->next;
j++;
}
if (!p||j>pos)
{
cerr<<"查找位置非法。";
exit(1);
}
else return p->data; //返回
}
template<classT>
int LinkList<T>::CountX(Tdata) {
int count=0;
Node<T>*p = first;
intj = 0;
while(p)
{
if(p->data==data) {
count++;
}
p=p->next;
}
return count;
}
template<classT>
void LinkList<T>::Insert(inti, Titem) {
Node<T>*p = first; // p指向首元结点
Node<T>*s = new Node<T>;
if(i==1){ //插入第1个结点的操作
s->data = item;
s->next = first; //将 s放到first(首元素)前面
first = s; //头指针指向 s (更改头指针)
}
else {
int j=1; //第二个元素开始
while (p && j<i-1) { //找到插入元素位置的前驱结点
p = p->next;
j++;
}
if (!p||i==0) {
cerr<<"插入位置非法。";
exit(1);
}
s->data = item;
s->next = p->next; // s->next为第 i个元素地址
p->next = s; // 第 i-1元素 next为新元素 s
}
}
template<classT>
void LinkList<T>::HeadInsert(Ta[], intn) {
for (inti=0; i<n; i++) {
Node<T> *rear = first; // rear指针指向原首元结点地址first
Node<T>*s =new Node<T>; //新结点 s
s->data=a[i];
if (i==0) {
s->next=NULL; // 头插法数组第一个元素最终为表尾元素
}
else {
s->next=rear; //在尾部加上新结点 s
}
first=s; // 头指针指向 s, s为首元结点 (更新头指针)
}
}
template<classT>
void LinkList<T>::TailInsert(Ta[], intn)
{
Node<T>*rear=first; // p指针指向原首元结点地址first
for (inti=0; i<n; i++) {
Node<T>*s = newNode<T>; //新结点 s
s->data = a[i];
s->next = NULL;
if(i==0){
first = s ; // 如果当前是第一个元素则将头指针指向当前节点
}
else {
rear->next = s; //在尾部加上新结点 s
rear=s; // s 变为尾结点
}
rear=s;
rear->next=NULL; //尾结点 next为空
}
}
template<classT>
void LinkList<T>::Detele(inti) {
if(i==1) //删除第一个结点需要更换头指针
{
Node<T> *q = first; // q指向第一个元素头指针指向的首元结点地址
T x = first->data; //存放准备删除的元素
first = q->next; //头指针指向第二个元素的地址
delete q;
cout<<x<<endl;
}
else {
Node<T> *p = first; // p先从指向首元素开始
int j = 1; //第二个元素开始
while (p && j<i-1) { // p指向将删除元素 q的前驱结点
p = p->next;
j++;
}
Node<T> *q = p->next; // q指向将删除结点
T x = q->data;
p->next = q->next; //将*q结点从链中"断开"
delete q;
cout<<x<<endl;
}
}
template<classT>
void LinkList<T>::PrintLinkList() {
Node<T> *p = first;
if(!p){ //空链表
cout<<"The LinkList is empty !"<<endl;
}
else{
cout<<"LinkList: " ;
while (p) { // p不为NULL时依次输出
cout<<p->data ;
if(p->next!=NULL) { cout<<","; }
p=p->next;
}
}
cout<<endl;
}
LinkList.cpp
#include<iostream>
#include "LinkList_1.h"
using namespace std;
int main(){
intmyData[] = {4,8,8,2,9,1,7,6,4,3,2,9,11,7,9};
cout<<" — — — — > Practice One < — — — — "<<endl;
/*利用无参构造函数建立单链表(myLinkList1) ,并打印出该单链表讯息。
此时,单链表中应无数据,打印出无数据的讯息。
在主程序中,采用 for 循环依序将数组 myData 中的数据插入单链表(myLinkList1)中,并打印出该单链表讯息。
int myData[15] = { 4,8,8,2,9,1,7,6,4,3,2,9,11,7,9 };
*/
LinkList<int> myLinkList_1;
myLinkList_1.PrintLinkList();
int myLength=sizeof(myData)/sizeof(*myData);
cout<<"The length of input data is :"<<myLength<<endl;
for(inti=1;i<=myLength;i++){
myLinkList_1.Insert(i,myData[i-1]);
}
myLinkList_1.PrintLinkList();
cout<<endl<<endl;
LinkList<int>myLinkList_2(myData,myLength);
cout<<" — — — — > Practice Two < — — — — "<<endl;
/*利用有参构造函数建立单链表 ,并打印出该单链表讯息。
int myData[15] = { 4,8,8,2,9,1,7,6,4,3,2,9,11,7,9 };
头插法: myLinkList2
尾插法: myLinkList3
*/
cout<<"Inserting a node at the Beginning of a linked listed list ."<<endl;
myLinkList_2.HeadInsert(myData,myLength);
myLinkList_2.PrintLinkList();
cout<<endl;
cout<<"Inserting a node at the End of a linked listed list ."<<endl;
myLinkList_2.TailInsert(myData,myLength);
myLinkList_2.PrintLinkList();
cout<<endl<<endl;
cout<<" — — — — > Practice Three < — — — — "<<endl;
/*
删除单链表中的元素
试写一个函数删除单链表中的结点,并返回被删除结点的内容。
针对 myLinkList1 中的内容进行删除。
删除第 1 个元素,并打印出删除元素的内容。
删除第 5 个元素,并打印出删除元素的内容。
*/
cout<<"The data of the detele node is : " ;
myLinkList_1.Detele(1);
myLinkList_1.PrintLinkList();
cout<<endl;
cout<<"The data of the detele node is : " ;
myLinkList_1.Detele(5);
myLinkList_1.PrintLinkList();
cout<<endl<<endl;
cout<<" — — — — > Practice Four < — — — — "<<endl;
/*试写一个函数計算单链表中所储存数据的个数,并返回计算的结果。
针对 myLinkList1 中的内容进行计算。
统计 9 的个数。
*/
cout<<"The number of 9 "<<"is "<<myLinkList_1.CountX(9) <<endl;
}
运行结果
![](https://i-blog.csdnimg.cn/blog_migrate/915d044dd9a348b5c10f70ca011d587b.png)