线性表的链式存储结构

链表和顺序表的区别

链表:动态存储
顺序表:静态存储
时间性能上
在这里插入图片描述
空间性能上
在这里插入图片描述

链表的分类

在这里插入图片描述
在这里插入图片描述

单链表

逻辑描述

存储空间分配特点
1 物理上不连续,动态存储
2 依赖后继指针定位下一个元素的位置
链表的结点
在这里插入图片描述
数据域:存放数据
指针域:存放后继结点的位置


//C++中用结构类型描述结点
template <class T>
struct Node
{
  T data;
  Node<T>  *next;
};

分类
1.带头指针的单链表:
(1)缺点1:空链表、非空链表的处理不同
(2)缺点2:插入、删除首结点的处理和其他结点不同

2.带头结点的单链表:
(1)优点:插入、删除不需要移动大量元素;动态分配内存
(2)缺陷:顺序查找,不能直接定位

举例
1 生成首结点:

  Node<T> *p= new Node<T>

2 为结点赋值:

p->data= ‘a’;      
p->next = NULL; //(尾结点)  
// 或者 p->next = (其他结点的地址);

3 头结点指向首结点

front->next = p;

4 释放结点:

delete p; 
front->next =NULL

实现

template <class T>
class LinkList
{
public:
 LinkList();              //无参构造函数
 LinkList(T a [], int n); //有参构造函数,使用含有n个元素的数组a初始化单链表
 ~LinkList();             //析构函数
 void PrintList();        //按次序遍历线性表中的各个数据元素
 int GetLength();         //获取线性表的长度
 Node <T> * Get(int i);   //获取线性表第i个位置上的元素结点地址
 int Locate (T x);        //查找线性表中值为x的元素,找到后返回其位置
 void Insert (T x ,int i);//在线性表的第i个位置上插入值为x的新元素
 T Delete(int i);         //删除线性表第i个元素,并将该元素返回
private:
 Node <T> * front;  //头指针
};

建立单链表——构造函数

无参构造函数——空链表

在这里插入图片描述

template <class T>
LinkList<T>::LinkList()
{
  front = new  Node<T>;
      front->next  = NULL;
}

含参构造函数

LinkList(T a [], int n)
使用长度为n的数组a来初始化单链表
建立单链表的两种方法:头插法和尾插法
(1)头插法:
在这里插入图片描述


template <class T>
LinkList<T>::LinkList(T a[], int n)
{   //初始化头结点
  front = new  Node<T>;  //在堆中建立新结点
  front->next  = NULL;
      for (int i=0; i<n; i++)
      {
              Node<T> *s=new Node<T>;  
	      s->data=a[i];          //生成新结点
              s->next = front->next;
              front->next  =s;// 链接在头结点和首结点之间
      }
}

(2)尾插法:
在这里插入图片描述

template <class T>
LinkList<T>::LinkList(T a[], int n)
{   
   front = new  Node<T>;
   Node<T> *r = front;//初始化头结点
      for (int i=0; i<n; i++)
      {
              Node<T> *s=new Node<T>;  
	      s->data=a[i]; 
	      s->next = NULL;// 生成新结点
              r->next = s;// 链接在尾结点后面
              r= s;// 尾指针后移
      }
}

析构函数

链表使用完毕,必须要释放内存
在这里插入图片描述

template <class T>
LinkList<T>::~LinkList()    //析构函数
{     
     Node<T> *p=NULL;  //初始化工作指针p 
      while (front!=NULL )  //要释放的结点存在
       {
             p=front->next;   //暂存要释放的结点
             delete front;    //释放结点
             front = p;       //移动工作指针
 }
}

查找

按位查找
查找第i个结点的数据元素的值

伪代码
1.初始化工作指针p和计数器j,p指向第一个结点,j=1;
2.循环以下操作,直到p为空或j等于i
(1) p指向下一个结点;
(2) j加1;
3.返回p;

返回地址:

template <class T>
Node <T> *LinkList<T>:: Get(int i)
 {
 Node <T> * p = front->next; //初始化工作指针
 int j = 1;   //初始化计数器
 while (j!=i) && (p!=NULL)     //两个条件都满足,则继续循环
{
	p = p->next;  //工作指针后移
	j++; 
}
if(p==NULL) throw “位置非法”;
return p;  //查找到第i个元素或没找到
 }

返回值:

template <class T>
T *LinkList<T>:: Get(int i)
{
       Node<T> *p=front->next;  //初始化工作指针
       int j=1;      //初始化计数器
       while (j!=i) && (p!=NULL)  //两个条件都满足,则继续循环
      {
             p=p->next;   //工作指针后移
             j++;
       }
      if(p==NULL) throw “位置非法”;
      return p->data;  
}

按值查找
查找给定值的结点,找到后返回元素地址或序号

template <class T>
int LinkList<T>::Locate (T x)
{  
 Node <T> * p = front->next; //初始化工作指针
 int j=1;
 while (p)
 {
  if (p->data == x) return j;//找到被查元素,返回位置(注意“==”一定情况下要运算符重载)
  p = p->next;
  j++;
 }
 return -1;  //若找不到,返回错误标识-1
}

插入

在这里插入图片描述

//在线性表的第i个位置上插入值为x的新元素
template <class T>
void LinkList<T>::Insert(T x ,int i)
{
Node <T> * p = front;  //初始化工作指针
//若不是在第一个位置插入,得到第i-1个元素的地址。
 if (i != 1) p = Get(i-1); 
 if (p!=NULL)
   {
      Node <T> * s = new Node <T>;//建立新结点
      s->data = x;    
      s->next = p->next;
      p->next = s;  //将新结点插入到p所指结点的后面
   }
   else throw "插入位置错误";
}

改进:给定元素P前插操作:时间复杂度由O(n)变为O(1)

(1)在堆中建立新结点:Node < T> * s = new Node < T>;
(2)将p结点的数据域写入到新结点的数据域:s->data = p->data;
(3)修改新结点的指针域: s->next = p->next;
(4)修改p结点的指针域,将新结点加入到链表中:p->next = s;
(5)将x写入到p结点的数据域:p->data=x;

Node < T> * s = new Node < T>;
s->data = p->data;
s->next = p->next;
p->next = s;
p->data=x;

举一反三

已知L是带头结点的单链表,且P结点既不是首结点,也不是尾结点,试从下列语句中选择合适的序列。

1 在P结点后插入S结点

S->next=P->next;
P->next=S;

2 在P结点前插入S结点

Q=P;
P=L;
while(P->next!=Q)
	P=P->next;
S->next=P->next;
P->next=S;

3 在表首插入S结点的序列

P=L;
S->next=P->next;
P->next=S;

4 在表尾插入S结点的序列

while(P->next!=NULL)
	P=P->next;
S->next=NULL;
P->next=S;	

删除

伪代码
从第一个结点开始,查找第i-1个元素,设为p指向该结点;
若删除位置错误(p为空或p->next为空),抛出异常
设q指向第i个元素:q = p->next;
摘链,即将q元素从链表中摘除:p->next = q->next;
保存q元素的数据:x = q->data;
释放q元素:delete q;


template <class T>
T LinkList<T>::Delete(int i) 
{
Node <T> * p = front;  //初始化工作指针
//若不是在第一个位置删除,得到第i-1个元素的地址
 if (i != 1) p = Get(i-1); 
 if (!p || !p->next) throw"删除位置错误";
 Node <T> * q = p->next;
 p->next = q->next; 
 T x = q->data;
 delete q;
 return x;
}

举一反三

已知L是带头结点的单链表,且P结点既不是首结点,也不是尾结点,试从下列语句中选择合适的序列。

1 删除P结点的直接后继结点

Q=P->next;
P->next=P->next->next;
delete Q;

2 删除P结点的直接前驱结点

Q=P;
P=L;
while(P->next->next!=Q)
	P=P->next;
Q=P->next;
P->next=Q->next;
delete Q;	

3 删除P结点

Q=P;
P=L;
while(P->next!=Q)
	P=P->next;
P->next=P->next->next;
delete Q; 

4 删除首结点

P=L;
Q=P->next;
P->next=P->next->next;
delete Q;

5 删除尾结点

while(P->next->next=NULL)
	P=P->next;
Q=P->next;
P->next=NULL;
delete Q;	

实例

//LinkList.h
template <class T>
struct Node
{
 T data;
 Node<T>  *next;
};

template <class T>
class LinkList
{
public:
 LinkList();              //无参构造函数
 LinkList(T a[], int n); //有参构造函数,使用含有n个元素的数组a初始化单链表
 ~LinkList();             //析构函数
 void PrintList();        //按次序遍历线性表中的各个数据元素
 int GetLength();         //获取线性表的长度
 Node <T> * Get(int i);   //获取线性表第i个位置上的元素结点地址
 int Locate(T x);        //查找线性表中值为x的元素,找到后返回其位置
 void Insert(T x, int i);//在线性表的第i个位置上插入值为x的新元素
 T Delete(int i);         //删除线性表第i个元素,并将该元素返回
private:
 Node <T> * front;  //头指针
};

template <class T>
LinkList<T>::LinkList()
{
 front = new  Node<T>;
 front->next = NULL;
}

template <class T>
LinkList<T>::LinkList(T a[], int n)
{
 front = new  Node<T>;
 Node<T> *r = front;//初始化头结点
 for (int i = 0; i < n; i++)
 {
  Node<T> *s = new Node<T>;
  s->data = a[i];
  s->next = NULL;// 生成新结点
  r->next = s;// 链接在尾结点后面
  r = s;// 尾指针后移
 }
}

template <class T>
LinkList<T>::~LinkList()    //析构函数
{
 Node<T> *p = NULL;  //初始化工作指针p 
 while (front != NULL)  //要释放的结点存在
 {
  p = front->next;   //暂存要释放的结点
  delete front;    //释放结点
  front = p;       //移动工作指针
 }
}

template <class T>
Node <T> *LinkList<T>::Get(int i)
{
 Node<T> *p = front->next;  //初始化工作指针
 int j = 1;      //初始化计数器
 while (j != i&&p != NULL)  //两个条件都满足,则继续循环
 {
  p = p->next;   //工作指针后移
  j++;
 }
 if ( p== NULL) throw "位置非法";
 return p;
}

template <class T>
int LinkList<T>::Locate(T x)
{
 Node <T> * p = front->next; //初始化工作指针
 int j = 1;
 while (p)
 {
  if (p->data == x) return j;//找到被查元素,返回位置(注意“==”一定情况下要运算符重载)
  p = p->next;
  j++;
 }
 return -1;  //若找不到,返回错误标识-1
}

//在线性表的第i个位置上插入值为x的新元素
template <class T>
void LinkList<T>::Insert(T x, int i)
{
 Node <T> * p = front;  //初始化工作指针
 //若不是在第一个位置插入,得到第i-1个元素的地址。
 if (i != 1) p = Get(i-1);
 if (p != NULL)
 {
  Node <T> * s = new Node <T>;//建立新结点
  s->data = x;
  s->next = p->next;
  p->next = s;  //将新结点插入到p所指结点的后面
 }
 else throw "插入位置错误";
}

template <class T>
T LinkList<T>::Delete(int i)
{
 Node <T> * p = front;  //初始化工作指针
 //若不是在第一个位置删除,得到第i-1个元素的地址
 if (i != 1) p = Get(i - 1);
 if (!p || !p->next) throw "删除位置错误";
 Node <T> * q = p->next;
 p->next = q->next;
 T x = q->data;
 delete q;
 return x;
}

template <class T>
void LinkList<T>::PrintList()
{
 Node<T>*p = front->next;     
 cout << "按序号依次遍历线性表中的各个数据元素:" << endl;
 while (p)
 {
  cout <<p->data;
  p = p->next;
 }
 cout << endl;
};

template <class T>
int LinkList<T>::GetLength()
{
 Node<T>    *p = front;      int i = 0;
 while (p->next != NULL)
 {
  p = p->next;
  i++;
 }
 return i;
};
//test.cpp
#include <iostream>
using namespace std;
#include "LinkList.h"

int main()
{
 int a[7] = { 1,2,3,4,5,6,7 };
 LinkList <int> list(a, 7);
 list.PrintList();
 list.Insert(0, 1);
 list.PrintList();
 int x = list.Delete(1);
 cout << "删除元素:" << x << endl;
 list.PrintList();
 int p = list.Locate(4);
 cout << "元素4的位置:" << p << endl;
}

循环链表

单循环链表:

定义:将单链表尾结点指针指向头结点
单循环空链表:
在这里插入图片描述
非空单循环链表
在这里插入图片描述

//构造函数
template <class T>
CLinkList<T>::CLinkList()
{
    rear = new Node <T>;
    rear->next = rear;
}

单循环链表与单链表相同点
(1)存储结构基本相同,仅使用尾指针及其指针指向头结点
(2)基本操作实现类似
单循环链表与单链表不同点
(1)头指针:
单链表:使用front标识头指针,
单循环链表:使用rear->next标识头指针;
(2)判断尾结点的方法:
单链表:p->next== NULL,
单循环链表:p==rear。

双链表

每个结点有两个指针域,指向前驱和后继
结点结构:
在这里插入图片描述

template <class T>
struct DNode 
{
 T data;  
 DNode <T> * prior;
 DNode <T> * next; 
};

在这里插入图片描述
在这里插入图片描述

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值