数据结构——链表

存取方式:顺序存取

与链式存储有关的术语

  • 结点:数据元素的存储映像。由数据域和指针域两部分组成
  • 链表:n个节点由指针链组成一个链表。(它是线性表的链式存储映像,称为线性表的链式存储结构)0bd037b7dd3c48b0bd49b8a363f8f955.jpg

986675d53dac40079d4a94cba27a93c4.jpg

 单链表、双链表、循环链表:

  • 结点只有一个指针域的链表,称为单链表或线性链表301836c6ca86489eb3413fa7ad61a61c.jpg
  •  结点有两个指针域的链表,称为双链表a6b6794273c448fb8136e6965c79c2c8.jpg
  •  首尾相接的链表称为循环链表3756ef8a8c4840f483b495db246f37db.jpg

 头指针、头结点和首结点:

37c20fb64a9f459ca7cfd935234b82b6.jpg

 头指针:是指向链表中第一个结点的指针

首元结点:是指链表中存储第一个数据元素a1的结点

头结点:是在链表的首元结点之前附设的一个结点

  • 在链表中设置头结点的好处:
  1. 便于首元结点的处理:首元结点的地址保存在头结点的指针域中,所以在链表的第一个位置上操作和其他位置一致,无需进行特殊处理
  2. 便于空表非空表的统一处理:无论是链表是否为空,头指针都是指向头结点的非空指针,因此空表和非空表的处理也就统一

单链表:

  • 单链表的存储结构

21a626d019f5438c8a07f0f3f1e17ce0.jpg

单链表的定义:

typedef struct Lnode{   //声明结点的类型和指向结点的指针类型
    ElemType data;  //结点的数据域
    struct Lnode *next;  //结点的指针域
}Lnode,*LinkList; //LinList为指向结构体Lnode的指针类型 

举个列子:存储学生学号,姓名,成绩的单链表结点类型定义如下所示

d8e9f73bd8044229b1756091502b89da.jpg

 1a8bef284cb44926b4bf75e6502da278.jpg

 单链表的基本操作:

单链表的初始化:

【算法步骤】:

  1. 生成新结点作头结点,用头指针L指向头结点

将头结点的指针域置空

Status InitList_L(LinkList &L){
    L=new LNode; //或 L=(LinkList) malloc (sizeof(LNode))
    L->next=NULL;
    return OK;
}

【补充算法1】——判断链表是否为空

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

【算法思路】 判断头结点指针是否为空

Status LinkList(LinkList L){   //若为空表,则返回1,否则返回0
    if(L->next) //非空
        return 0;
    else 
        return 1;    
}

【补充算法2】——单链表的销毁:链表销毁后不存在

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

Status DestroyList(LinkList &L){  //销毁单链表
    Lnode *p;  //或LinkList p;
    while(L){
        p=L;
        L=L->next;
        delete p;
    }
    return OK;
}

【补充算法3】——清空链表

链表仍存在,但链表中无元素,成为空链表(头指针和头结点仍然存在)

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

//清空单链表L
Status ClearList(LinkList &L){
    Lnode *p,*q;     //或LinkList p,q;
    p=L->next;
    while(p){
        q=p->next;
        delete p;
        p=q;
    }
    l->next=NULL;  //头结点指针域为空
    return OK;
}

【补充算法4】——求单链表的表长

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

//计算单链表的表长
Int LinkList_L(LinkList L){  //返回L中数据元素的个数
    LinkList p;
    p=L->next    //p指向第一个结点
    i=0;
    while(p){          //遍历单链表,统计节点数
        i++;
        p=p->next;
    }
}


取值——去单链表中第i个元素的内容

【算法步骤】
  1. 从第一个结点(L->next)顺链扫描,用指针p指向当前扫描到的结点,p初值p=L->next
  2. j做计数器,累计当前扫描过的结点数,j初值为1
  3. 当p指向扫描到的下一个结点时,计数器j加1
  4. 当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; 
}//GetElemn_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;
	
}

按值查找——根据指定数据获取该数据所在的位置序号

【算法描述】

//按值查找——根据指定数据获取该数据所在的位置序号
int LcoateElem_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;
} 

插入——在第i个结点前插入值为e的新结点

【算法步骤】

  1. 首先找到ai-1的存储位置p
  2. 生成一个数据域为e 的新结点s
  3. 插入新结点:新结点指针域指向结点ai,结合ai-1的指针域指向新结点

思考:步骤一和步骤二能互换么?先执行步骤二,后执行步骤一可以么?

答:不可以!会丢失掉ai的地址

 代码实现:

//在L中第i个元素之前插入数据元素e
Status LinkInsert_L(LinkList &L,int i,ElemType e){
	p=L;
	j=0;
	while(p&&j<i-1){
		p=p->next;
		++j;//寻找第i-1个结点,p指向i-1结点  
		if(!p||j>i-1) return ERROR;//i大于表长+1或者小于1,插入位置非法
		s=new LNode;
		s->data=e;//生成新结点s,将结点s的数据域置为e
		s->next=p->next;   //将结点s插入L中 
		p->next=s; 
	}
} //LinkInsert_L 


删除——删除第i个结点

【算法步骤】

  1. 首先找ai-1的存储位置p,保存要删除的a的值
  2. 令p->next 指向ai+1
  3. 释放ai结点的空间

代码实现:

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

单链表的建立:

  • 头插法:——元素插入在链表头部也叫前插法

  1. 从一个空表开始,重复读入数据
  2. 生成新结点,将读入数据存放到新结点的数据域中
  3. 最后一个结点开始,依次将各结点插入到链表的前端

代码实现:

//头插法
void CreateList_H(LinkList &L,int n){
	L=new LNode;
	L->next=NULL;//先建立一个带头结点的单链表 
	for(int 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;
	} 
}//CreateList_H 
  • 尾插法——元素插在链表尾部(后插法)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值