线性表——单链表笔记

线性表

线性表的主要运算(C++版本)
Listinit(&L):初始化,构造一个空的线性表L
Listdestory(&L):如果线性表L存在,则将其销毁
Listclear(&L):将已经存在的线性表L置为空表
Listlength(L):对于给定的线性表L,求其线性表的长度,也就是返回元素个数
Locatelem(L,x):在线性表L中,返回第一个值为x的元素在线性表中的位序,如果不存在返回0
Listinsert(&L,i,e):在线性表L中第i个元素之前插入新元素e,i的合法值大于等于1且小于等于线性表长度+1;插入后,线性表长度加一
Listdelete(&L,i,&e):从线性表L中删除第i个元素,并用e返回其值,i的合法值大于等于1且小于等于线性表长度;删除后,线性表长度减一。

顺序表和单链表

顺序表

和数组类似,逻辑上相邻的结点物理位置也相邻,这里主要记录单链表

单链表

单链表重点在于理解,头结点头指针
每个结点只含有一个指针的链表是单链表
它不要求逻辑上相邻的元素在物理位置上一定是相邻的,因此是非顺序存储结构

在这里插入图片描述

头结点:
头结点不一定存在
头节点存放在第一个元素的结点之前
它的数据域一般没有意义,(但也可以存储标题、日期、建表人等信息)
头结点的存在是为了在某些时候更方便的处理链表
如对第一个元素前进行插入或者删除该结点,头结点能帮我们找到第一个元素的位置,因为它指向第一个元素。头结点的指针域指向第一个元素。

头指针:
头指针是必须存在
因为我们需要知道元素的位置,这个必须由指针来决定!

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

无论链表是否为空链表,头指针一定要有!

知道了头指针,也可以知道链表其他元素的位置!

头指针也就是平常所说的标识

—————————————————————————————
如图:
带头指针不带头结点,但头指针必须存在!

下图:带头结点的单链表
在这里插入图片描述重点在于理解
比如第二幅图
头指针q指向头结点,那么它已经代表了头结点的位置
那么头结点的数据域 q->data 就是为头结点的数据

q->next 代表指向保存第一个元素的结点
q->next-data 就代表第一个结点的元素
q->next->next 代表指向保存第二个元素的结点
在这里插入图片描述

单链表实现

另外一种模式
在构建链表时,需要逐个创建结点,并且把生成的每个结点加入到链表中。创建结点包括3个步骤:

1.为结点分配内存单元;
2.把数据存储到结点中;
3.把结点插入到链表中。
——————
借用“指针”数据类型来描述链表:

#include <iostream>
#include<malloc.h>//包含malloc()、realloc()函数
using namespace std;
typedef int status;//status 为整形
typedef int elementype;//elemengtype 为整形

typedef struct node
{
	elementype data;//data为整形 因为elementype 为int的意思
	struct node *next;//指向链表中的下一个结点
}node, *linklist;//*LinkList表示的是一个结构体指针变量
//而 &LinkList 则表示引用,注意,这里是C++里面的内容,C里面不支持这种写法,在C编译下会报错,而在C++编译环境下则不会。

这种链表称为动态链表,线性表中的结点类型为node,而linklist为指向结点的指针类型。

构造带头结点的空链表

//构造一个带头结点的空链表
int Initlist_l(linklist &l)//l为单链表的头指针,头指针可以表示一个确定的单链表
{
	if (l = (node*)malloc(sizeof(node)))
		return 0;
	l->next = NULL;//头结点指向下一个为空
	return 1;
}

(node*)malloc(sizeof(node))的意思是分配一个大小为node类型的内存,并将这个内存地址转化为node*型,然后将赋给l,所以l为地址。

创建具有n个结点的线性表

//创建具有n个结点的线性表
status createlist_l(linklist &l, int n)//不清楚这里要不要带&  &是取地址值
{
	linklist q, p;//定义指针变量q,p
	int i;
	q = l;//l代表首地址
	for (i = 1; i <= n; i++)
	{
		p = (linklist)malloc(sizeof(node));//每次申请新的结点
		printf("请依次输入链表:");
		scanf_s("%d", &p->data);//输入下一个结点的数据
		q->next = p;//使得前驱指针指向下一个新的结点
		q = p;//p往后移
	}
	q->next = NULL;//知道n个结点输入完毕,指针域指向NULL跳出
	return 1;
}

打印输入的线性表

//打印输入的线性表
void prinlist_l(linklist l)
{
	linklist p;//一般先把首地址赋予一个指针变量
	int i = 0;
	p = l->next;//l代表首地址  取下一个 就是第一个结点的位置
	while (p != NULL)
	{
		i++;//从第一个开始到最后一个元素
		printf("第%d个元素是:", i);
		printf(" %d \n", p->data);//第一个结点的元素
		p = p->next;//找到该结点的元素后转向下一个结点
	}
}

在第i个位置插入结点e

//在第i个位置插入结点e
status linklist_l(linklist l, int i, elementype e)
{
	linklist p, s;
	int j;
	p = l, j = 0;//l为头指针
	while (p&&j < i - 1)
	{
		p = p->next;//寻找第i-1个结点,循环之后到达i-1所在位置的数据域
		++j;
	}
	if (!p || j > i - 1)//i如果超出链表范围或者j大于等于i  则无效
	{
		printf("输入错误!\n");
		return 0;
	}
	s = (node*)malloc(sizeof(node));//创造新的结点
	s->data = e;//e保存在s结点的数据域中
	s->next = p->next;//将新的结点s->next定为  p->next位置的结点  也就是使得s->next 为 i结点,此时i中元素有e
	p->next = s;//指向下一个结点

    //输出新的链表
	p = l;//l始终为首地址  头指针
	p = p->next;
	printf("得到新的链表:\n");

	while (p != NULL)
	{
		printf(" %d ", p->data);
		p = p->next;
	}
	printf("\n");
	return 1;
}

将第i个位置的结点删除

//将第i个位置的结点删除
status listdelete_l(linklist l, int i)
{
	linklist p, q;
	int j, e;
	p = l; j = 0;
	while (p->next&&j < i - 1)//使得p->next结点在i-1位置上
	{
		p = p->next; ++j;//p->next   赋予  p
	}
	if (!(p->next) || j > i - 1)
	{
		printf("输入错误!\n");
		return 0;
	}
	q = p->next;  //i位置结点为q
	p->next = q->next;//i+1位置的结点取代i位置的结点
	free(q);//释放q
	p = l->next;
	printf("你将得到新的链表\n");
	while (p != NULL)
	{
		e = p->data;
		printf(" %d ", e);
		p = p->next;
	}
	printf("\n");
	return 1;
}

整体实现

#include <iostream>
#include<malloc.h>//包含malloc()、realloc()函数
using namespace std;
typedef int status;//status 为整形
typedef int elementype;//elemengtype 为整形

typedef struct node
{
	elementype data;//data为整形 因为elementype 为int的意思
	struct node *next;//指向链表中的下一个结点
}node, *linklist;
//构造一个带头结点的空链表
int Initlist_l(linklist &l)//l为单链表的头指针,头指针可以表示一个确定的单链表
{
	if (l = (node*)malloc(sizeof(node)))
		return 0;
	l->next = NULL;//头结点指向下一个为空
	return 1;
}

//创建具有n个结点的线性表
status createlist_l(linklist &l, int n)//不清楚这里要不要带&  &是取地址值
{
	linklist q, p;//定义指针变量q,p
	int i;
	q = l;//l代表首地址
	for (i = 1; i <= n; i++)
	{
		p = (linklist)malloc(sizeof(node));//每次申请新的结点
		printf("请依次输入链表:");
		scanf_s("%d", &p->data);//输入下一个结点的数据
		q->next = p;//使得前驱指针指向下一个新的结点
		q = p;//p往后移
	}
	q->next = NULL;//知道n个结点输入完毕,指针域指向NULL跳出
	return 1;
}
//打印输入的线性表
void prinlist_l(linklist l)
{
	linklist p;//一般先把首地址赋予一个指针变量
	int i = 0;
	p = l->next;//l代表首地址  取下一个 就是第一个结点的位置
	while (p != NULL)
	{
		i++;//从第一个开始到最后一个元素
		printf("第%d个元素是:", i);
		printf(" %d \n", p->data);//第一个结点的元素
		p = p->next;//找到该结点的元素后转向下一个结点
	}
}
//在第i个位置插入结点e
status linklist_l(linklist l, int i, elementype e)
{
	linklist p, s;
	int j;
	p = l, j = 0;//l为头指针
	while (p&&j < i - 1)
	{
		p = p->next;//寻找第i-1个结点,循环之后到达i-1所在位置的数据域
		++j;
	}
	if (!p || j > i - 1)//i如果超出链表范围或者j大于等于i  则无效
	{
		printf("输入错误!\n");
		return 0;
	}
	s = (node*)malloc(sizeof(node));//创造新的结点
	s->data = e;//e保存在s结点的数据域中
	s->next = p->next;//将新的结点s->next定为  p->next位置的结点  也就是使得s->next 为 i结点,此时i中元素有e
	p->next = s;//指向下一个结点

    //输出新的链表
	p = l;//l始终为首地址  头指针
	p = p->next;
	printf("得到新的链表:\n");

	while (p != NULL)
	{
		printf(" %d ", p->data);
		p = p->next;
	}
	printf("\n");
	return 1;
}
//将第i个位置的结点删除
status listdelete_l(linklist l, int i)
{
	linklist p, q;
	int j, e;
	p = l; j = 0;
	while (p->next&&j < i - 1)//使得p->next结点在i-1位置上
	{
		p = p->next; ++j;//p->next   赋予  p
	}
	if (!(p->next) || j > i - 1)
	{
		printf("输入错误!\n");
		return 0;
	}
	q = p->next;  //i位置结点为q
	p->next = q->next;//i+1位置的结点取代i位置的结点
	free(q);//释放q
	p = l->next;
	printf("你将得到新的链表\n");
	while (p != NULL)
	{
		e = p->data;
		printf(" %d ", e);
		p = p->next;
	}
	printf("\n");
	return 1;
}
void main()
{
	linklist l;
	int a, n, i;
	elementype e;
	cout << "构造一个带头结点的空链表" << endl;
	Initlist_l(l);
	cout << "创建具有n个结点的线性表" << endl;
	cin >> n;
	createlist_l(l,n);
	cout << "打印输入的线性表" << endl;
	prinlist_l(l);
	cout << "在第i个位置插入结点e" << endl;
	cout << "输入i" << endl;
	cin >> i;
	cout << "输入e" << endl;
	cin >> e;
	linklist_l(l, i, e);
	cout << "将第i个位置的结点删除" << endl;
	cout << "输入i" << endl;
	cin >> i;
	listdelete_l(l, i);


}

在这里插入图片描述

静态链表

用结构数组来描述链表
数组的下标代替指针,指示结点在数组中的位置
o下标作为头结点

线性表做插入与删除时只需修改相应下标就行,但需要预先分配一个较大的存储空间

双向链表

单链表的每个结点只带有一个指针
每个结点含有两个或两个以上的指针域的链表称为多重链表

双向链表
一个结点两个指针域

一个存放它的直接前驱结点的信息
一个存放它的直接后继结点的信息

左指针域和右指针域
数据域在其中间
| 左指针 | 数据域 | 右指针 |

typedef struct
{
elementype data;
struct dnode *llink;
struct dnode *rlink;
}dnode,*dulinklist;

例:在双向链表中,若p是指向表中任意一结点的指针,则有

p=(p->llink)->rlink=(p->rlink)->llink

即两个结点直接可以互相用指针指向
这样对于插入和删除工作变得简单,但是付出了空间的代价

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值