数据结构学习——线性表的链式表示及实现--单链表

线性表的链式表示及实现

基本概念

与链式存储有关的术语

结点:数据元素的存储映像。由数据域和指针域两部分组成
链表:n个结点由指针链组成的一个链表
它是线性表的链式存储映像,称为线性表的链式存储结构
单链表、双链表、循环链表:
结点只有一个指针域的链表,称为单链表或线性链表
结点有两个指针域的链表,称为双链表
首尾相接的链表称为循环链表
头指针、头结点和首元结点: 重要概念要理解清楚
头指针:是指向链表中第一个结点的指针
首元结点:是指链表中存储第一个数据元素a1的结点
头结点:是在链表的首元结点之前附设的一个结点

链表(链式存储结构)的特点

1.结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻
2.访问时只能通过头指针进入链表,并通过每个结点的指针域依次向后顺序扫描其余结点,所以寻找第一个结点和最后一个结点所花费的时间不等,即存取元素的方法为顺序存取法

链表的存储结构两种形式

一、不带头结点

表示空表:头指针为空时表示空表

二、带头结点(使用这种)

表示空表:当头结点的指针域为空时表示空表
设置头结点的好处:
1.便于首元结点的处理
首元结点的地址保存在头结点的指针域中,所以在链表的第一个位置上的操作和其他位置一致,无需进行特殊处理
2.便于空表和非空表的统一处理
无论链表是否为空,头指针都是指向头结点的非空指针,因此空表和非空表的处理也就统一了
头结点的数据域内装的是什么
头结点的数据域可以为空,也可以存放线性表长度等附加信息,注意此节点不能计入链表长度值

单链表存储表示(带头节点的)

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

带头结点的单链表的存储结构

typedef int ElemType;
typedef struct LNode{
ElemType data;
struct Lnode *next;
}LNode, *LinkList;
LinkList L;	//定义链表L等效于Lnode *L;
LNode *p;	//定义节点指针p

单链表的基本操作

1.链表的初始化

算法步骤:

①生成新结点作为头结点,用头指针L指向头结点
②将头结点的指针域置空

2.判空

3.销毁链表

**算法思想:**从头指针开始,依次释放所有结点
关键代码:

while(L){
	p = L; 
	L = L->next;  
	delete p;
} 

4.清空链表

链表仍存在,但链表中无元素,称为空链表(头指针和头结点均在)
**算法思路:**从首元结点开始依次释放所有的结点,并将头结点指针域设置为空

p = L->next;
while(p){
	q = p->next;  
	delete p;  
	p = q;
}
L->next = NULL;	//头结点指针域置为空

5.求链表表长

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

int length = 0;
p = L->next;
while(p){
	length++;  
	p = p->next; 
}

6.取值

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

//返回链表第loc位置的元素值 查找范围为1到n 
int getValue(const LinkList &l, int loc){
	LNode *p = l->next;
	int j = 1;
	while(p && j<loc){
		p = p->next;
		j++;
	}
	if(!p || j>loc) return error;//位置不合法
	return p->data; 
}

7.查找

  1. 按值查找:根据指定数据返回对应的数据元素
    算法步骤:

①从第一个结点起,依次和e相比较
②如果找到一个其值与e相等的数据元素,则返回该元素
③若查遍整个链表都没有找到其值与e相等的元素,则返回0或NULL

//按值查找,返回元素地址  查找范围为1到n(逻辑) 
LNode* getElem(const LinkList &l, int e){
	LNode *p = l->next;
	while(p && p->data!=e){
		p = p->next;
	}
	return p; //若位置不合法返回NULL 
}
  1. 按值查找:返回位置序号
//按值查找,返回元素逻辑位置  查找范围为1到n(逻辑) 
int getLoc(const LinkList &l, int e){
	LNode *p = l->next;
	int j = 1;
	while(p && p->data!=e){
		p = p->next;
		j++;
	}
	if(!p) return error;//位置不合法
	return j; 
}

时间复杂度为O(n)

8.插入:在第i个结点前插入新结点

算法思想:找到第i-1个结点p;p->next为原第i位置的结点

s->next=p->next;	//s新结点
p->next=s;
//插入  在第loc个结点前插入新元素  插入范围为1到n+1 
int insertElem(LinkList &l, int loc, int e){
	//1.找到第loc-1个结点  范围为0到n
	LNode *p = l;
	int j = 0;
	while(p && j<loc-1){
		p = p->next;
		j++;
	}
	if(!p || j>loc-1) return error;//位置不合法
	//2.生成新结点
	LNode *s = new LNode;
	s->data = e;
	//3.插入进链表
	s->next = p->next;
	p->next = s;
	return ok;
}

时间复杂度为O(n)
注意:线性表不需要移动元素,只要修改指针,一般情况下时间复杂度为O(1),但在单链表进行前插或删除操作,要从头查找前驱结点,故时间复杂度为O(n)

9.删除:删除第i个结点

算法思想:找到第i-1个结点p;保存删除结点q的值,返回删除结点的值

q=p->next;
e=q->data;
p->next=q->next;
delete q;
//删除  删除第loc个结点,并返回删除的元素值  删除范围为1到n
int deleteElem(LinkList &l, int loc){
	//1.找到第loc-1个结点  范围为0到n-1 
	LNode *p = l;
	int j = 0;
	while(p->next && j<loc-1){	
	//判断条件是p->next 因为loc-1结点范围为0到n-1 
		p = p->next;
		j++;
	}
	if(!(p->next) || j>loc-1) return error;//位置不合法
	//2.删除结点q
	LNode *q = p->next;
	p->next = q->next;
	int e = q->data;
	delete q;
	return e;
} 

时间效率:O(n)
注意:线性表不需要移动元素,只要修改指针,一般情况下时间负责度为O(1),但在单链表进行前插或删除操作,要从头查找前驱结点,故时间复杂度为O(n)

10.单链表的建立

  1. 头插法 :元素插入在链表头部
    算法思想:

①从一个空表开始,重复读入数据;
②生成新结点,将读入数据存放到新结点的数据域中
③依次将各结点插入到链表的前端

//建立单链表 头插法(已知元素个数) 
int createList(LinkList &l, int n){
	//1.初始化单链表
	initList(l);
	//2.依次插入到链表的头部
	for(int i = n; i > 0; i--){
		LNode *p = new LNode;
		cin >> p->data;
		p->next = l->next;
		l->next = p;
	} 
	return ok;
} 

时间复杂度O(n)

  1. 尾插法:元素插入到链表尾部,需要设定尾指针
    算法思想:

①从一个空表L开始,将新结点逐个插入到链表的尾部,尾指针r指向链表的尾结点
②初始时,r和L均指向头结点,每读入一个数据元素则申请一个新结点,将新结点插入到尾结点的下一个域,r指向新结点

//建立单链表 尾插发(已知元素个数)
int createList_r(LinkList &l, int n){
	//1.初始化单链表
	initList(l);
	//2.初始化尾指针r
	LNode *r = l;
	//3.依次插入到链表的尾部
	for(int i = 0; i < n; i++){
		LNode *p = new LNode;
		cin >> p->data;
		p->next = r->next;	//p->next = NULL 也行 
		r->next = p;
		r = p;
	}
	return ok;
} 

时间复杂度:O(n)

全部代码整合完整cpp

#include<iostream>
using namespace std;

#define ok 1
#define error 0

typedef int elemType;
typedef struct LNode{
	elemType data;
	struct LNode *next;
}LNode, *LinkList; 

int initList(LinkList &l){
	l = new LNode;
	l->next = NULL;
	return ok;
}

int destroyList(LinkList &l){
	LNode *p;
	while(l){
		p = l;
		l = l->next;
		delete p; 
	}
	return ok;
}

int clearList(LinkList &l){
	LNode *p;
	while(l->next){
		p = l->next;
		l->next = p->next;
		delete p;
	}
	return ok;
}

int isEmpty(const LinkList &l){
	if(l->next == NULL) return 1;
	return 0;
}

int getLength(const LinkList &l){
	LNode *p = l->next;
	int len = 0;
	while(p){
		len++;
		p = p->next;
	}
	return len;
}
//返回链表第loc位置的元素值 查找范围为1到n 
int getValue(const LinkList &l, int loc){
	LNode *p = l->next;
	int j = 1;
	while(p && j<loc){
		p = p->next;
		j++;
	}
	if(!p || j>loc) return error;//位置不合法
	return p->data; 
}
//按值查找,返回元素地址  查找范围为1到n(逻辑) 
LNode* getElem(const LinkList &l, int e){
	LNode *p = l->next;
	while(p && p->data!=e){
		p = p->next;
	}
	return p; //若位置不合法返回NULL 
}
//按值查找,返回元素逻辑位置  查找范围为1到n(逻辑) 
int getLoc(const LinkList &l, int e){
	LNode *p = l->next;
	int j = 1;
	while(p && p->data!=e){
		p = p->next;
		j++;
	}
	if(!p) return error;//位置不合法
	return j; 
}
//插入  在第loc个结点前插入新元素  插入范围为1到n+1 
int insertElem(LinkList &l, int loc, int e){
	//1.找到第loc-1个结点  范围为0到n
	LNode *p = l;
	int j = 0;
	while(p && j<loc-1){
		p = p->next;
		j++;
	}
	if(!p || j>loc-1) return error;//位置不合法
	//2.生成新结点
	LNode *s = new LNode;
	s->data = e;
	//3.插入进链表
	s->next = p->next;
	p->next = s;
	return ok;
}
//删除  删除第loc个结点,并返回删除的元素值  删除范围为1到n
int deleteElem(LinkList &l, int loc){
	//1.找到第loc-1个结点  范围为0到n-1 
	LNode *p = l;
	int j = 0;
	while(p->next && j<loc-1){	//判断条件是p->next 因为loc-1结点范围为0到n-1 
		p = p->next;
		j++;
	}
	if(!(p->next) || j>loc-1) return error;//位置不合法
	//2.删除结点q
	LNode *q = p->next;
	p->next = q->next;
	int e = q->data;
	delete q;
	return e;
} 
//建立单链表 头插法(已知元素个数) 
int createList(LinkList &l, int n){
	//1.初始化单链表
	initList(l);
	//2.依次插入到链表的头部
	for(int i = n; i > 0; i--){
		LNode *p = new LNode;
		cin >> p->data;
		p->next = l->next;
		l->next = p;
	} 
	return ok;
} 
//建立单链表 尾插发(已知元素个数)
int createList_r(LinkList &l, int n){
	//1.初始化单链表
	initList(l);
	//2.初始化尾指针r
	LNode *r = l;
	//3.依次插入到链表的尾部
	for(int i = 0; i < n; i++){
		LNode *p = new LNode;
		cin >> p->data;
		p->next = r->next;	//p->next = NULL 也行 
		r->next = p;
		r = p;
	}
	return ok;
} 
//建立单链表 头插法(未知元素个数) 
int createList(LinkList &l){
	//1.初始化单链表
	initList(l);
	//2.依次插入到链表的头部
	int e, j = 0;
	while(1){
		cin >> e;
		LNode *p = new LNode;
		p->data = e;
		p->next = l->next;
		l->next = p;
		if(cin.get() == '\n')//要放在最后面否则会少一个数据 
			break;
	} 
	return ok;
}
//建立单链表 尾插发(未知元素个数)
int createList_r(LinkList &l){
	//1.初始化单链表
	initList(l);
	//2.初始化尾指针r
	LNode *r = l;
	//3.依次插入到链表的尾部
	int e;
	while(1){
		cin >> e;
		LNode *p = new LNode;
		p->data = e;
		p->next = r->next;
		r->next = p;
		r = p;
		if(cin.get() == '\n')
			break;
	}
	return ok;
}
//输出链表元素
int printList(const LinkList &l){
	if(isEmpty(l))
		cout << "链表为空" << endl;
	LNode *p = l->next;
	int j = 0;
	while(p){
		j++;
		cout << p->data << " ";
		p = p->next;
	}
	cout << endl;
	return ok;
} 
int main(){
	LinkList l1, l2;
	createList_r(l1, 4);
	insertElem(l1, 2, -1);
	printList(l1);
	cout << "第2个位置元素的值=" << getValue(l1, 2) << endl;
	cout << "删除第3个元素,第3个元素的值=" << deleteElem(l1, 3) << endl;
	printList(l1);
	int loc = getLoc(l1, 7);
	
	cout << "值为7的元素的逻辑位置是" << loc;
	LNode *t = getElem(l1, 7);
	cout << t << "    "  << t->data << endl;
	createList_r(l2);
	printList(l2);
	cout << getLength(l2) <<endl;
	return 0;
} 
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值