线性表的链式表示和实现

线性表中数据结构在存储器中的位置是任意的,逻辑上相邻的数据元素在物理位置上不一定相邻

n个结点由指针链组成一个链表,链表是顺序存取

  • 单链表:每个结点只有一个指针域
  • 双链表:每个结点由两个指针域
  • (单/双)循环链表:链表结点首尾相接

缺点:

时间:存取效率不高,必须采用顺序存取,即存取 数据元素时只能按链表的顺序进行访问

空间:存储密度小

带头结点的单链表

单链表由表头唯一确定,因此单链表可以用头指针的名字命名。若头指针名是L,则把链表成为表L

单链表的存储结构

结点由数据域和指针域组成,用结构体表示

typedef struct Lnode{
   ElemType  data;
   struct  Lnode *next;
}Lnode,*LinkList;

定义链表L:LinkList L;

定义结点指针p:LNode *p <=> LinkList p;

单链表的初始化

构造一个空表

步骤:

  1. 生成新结点作为头结点,用头指针L指向头结点
  2. 将头指针的指针域置空
Status InitList L(LinkList &L){
   L = new Lnode; //或L = (LinkList) malloc (sizeof (LNode));
   L->next = NULL;
   return OK;
}

补充算法

判断链表是否为空:链表中无元素,称为空链表(头指针和头结点仍然在)

算法思路:判断头结点指针域是否为空

int ListEmpty(LinkList L){
   if(L->next) return 0;
   else return 1;
}
单链表的销毁:链表销毁后不存在

算法思路:从头指针开始,依次释放所有结点

Status DestuoyList_L(LinkList &L){ //销毁单链表L
   Lnode *p;  //或LinkList p;
   while(L) {
      p = L;
      L = L->next;
      delete p;
   }
   return OK;
}
清空链表:链表仍然存在,但链表中无元素,成为空链表(头指针和头结点仍然在)

算法思路:依次释放所有结点,并将头结点指针域设置为空

Status ClearList(LinkLIst &L){  //将L重置为空表
   Lnode *p,*q;   // 或LinkList p,q;
   p = L->next;
   while(p){    //没到表尾
      q = p->next;
      delete p;
      p = q;
   }
   L->next = NULL;   //头结点指针域为空
   return OK;
}
求单链表的表长

算法思路:从首元结点开始,依次计数所有结点

int ListLength_L(LinkList L){  //返回L中数据元素个数
   LinkList p;
   p = L->next;   //p指向第一个结点
   i = 0;
   while(p){   //遍历单链表,统计结点数
      i++;
      p = p->next;
   }
   return i;
}
取值:取单链表中第i个元素的内容

算法思路:从链表的表头出发,顺着链域next逐个结点往下搜索,直至搜索到第i个结点为止,因此,链表不是随机存取结构

算法步骤:

  • 从第一个结点(L->next)顺链扫描,用指针p指向当前扫描到的结点,p初值p = L->next
  • j做计数器,累计当前扫描过的结点数,j初值为1
  • 当p指向扫描到的下一结点时,计数器j+1
  • 当j=i时,p所指的结点就是要找的第i个结点
Status GetElem_L(LinkList L,int i,ElemType &e){   
//获取线性表L中的某个数据元素的内容,通过变量e返回
   p = L->next;
   j = 1;   //初始化
   while(p && j < i){   //向后扫描,直到p指向第i个元素或p为空
      p = p->next;
      ++j;
   }
   if(!p || j > i) return ERROR;   //第i个元素不存在
   e = p->data;   //取第i个元素
   return OK;
}  //GetElem_L
查找
按值查找:根据指定数据获取该数据所在的位置(该数据的地址)

算法步骤:

  1. 从第一个结点起,依次和e比较
  2. 如果找到一个其值与e相等的数据元素,则返回其在链表中的“位置”或地址
  3. 如果查遍整个链表都没有找到其值和e相等的元素,则返回0或“NULL”
Lnode *LocateElem_L(LinkList L,Elemtype e){
//在线性表L中查找值为e的数据元素
//找到,则返回L中值为e的数据元素的地址,查找失败则返回NULL
   p = L->next;
   while(p && p->data != e) p = p->next;
   return p;
}
按值查找:根据指定数据获取该数据所在的位置序号(是第几个数据元素)
//在线性表L中查找值为e的数据元素的位置序号
int LocateElem_L(LinkList L,Elemtype e){
//返回L中值为e的数据元素的位置序号,查找失败返回0
   p = L->next;
   j = 1;
   while(p && p->data != e) {
      p = p->next;
      j++;
   }
   if(p) return j;
   else return 0;
}
删除

算法步骤:

  1. 首先找到ai-1的存储位置p,保存要删除的a的值
  2. 令p->next指向ai+1(p->next = p->next->next)
  3. 释放结点ai的空间
//将线性表L中第i个数据元素删除
Status ListDelete_L(LinkList &L,int i,ElemType &e){
   p = L;
   j = 0;
   while(p->next && j < i-1){
      p = p->next;
      ++j;
      //寻找第i个结点,并令p指向其前驱
   }
   if(!(p->next) || j > i-1) return ERROR;  //删除位置不合理
   q = p->next;   //临时保存被删结点的地址以备释放
   p->next = q->next;   //改变删除结点前驱结点的指针域
   e = q->data;   //保存删除结点的数据域
   delete q;   //释放删除结点的空间
   return OK;
} // ListDelete_L

单链表的查找、插入、删除算法时间效率分析

查找

因线性链表只能顺序存取,即在查找时要从头指针找起,查找的时间复杂度为O(n)

插入和删除

因线性链表不需要移动元素,只要修改指针,一般情况下时间复杂度为O(1)

但是,如果要在单链表中进行前插或删除操作,由于要从头查找前驱节点,所耗时间复杂度为O(n)

单链表的建立

头插法:元素插入在链表头部

从一个空表开始,重复读入数据

生成新结点,将读入数据存放到新结点的数据域中

从最后一个结点开始,依次将各结点插入到链表的前端

void CreateList_H(LinkList &L,int n){
   L = new LNode;
   L->next = NULL;  //先建立一个带头结点的单链表;
   for(i = n;i < 0;--i){
      p = new LNode;  //生成新结点p = (LNode*)malloc(sizeof(LNode));
      cin>>p->data;  //输入元素值scanf(&p->data);
      p->next = L->next;
      L->next = p;
   }
}  //CreatList_H

时间复杂度为O(n) 

尾插法:元素插入在链表尾部

从一个空表L开始,将新结点逐个插入到链表尾部,尾指针r指向链表的尾结点

初始时,r同L均指向头结点。每读入一个数据元素则申请一个新结点,将新结点插入到尾结点后,r指向新结点

//正位序输入n个元素的值,建立代表头结点的单链表L
void CreateList_R(LinkList &L,int n){
   L = new LNode;
   L->next = NULL;
   r = L;   //尾指针r指向头结点
   for(i = 0;i < n;i++){
      p = new LNode;
      cin>>p->data;   //生成新结点,输入元素值
      p->next = NULL;
      r->next = p;   //插入到表尾
      r = p;   //r指向新的尾结点
   }
}//CreateList_R

时间复杂度为O(n) 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值