第二章 线性表

文章目录

  • 前言
  • 一、线性表
    • 1. 含义
    • 2. 表示
    • 3. 特点
    • 4. 基本操作
  • 二、线性表的顺序表示
    • 1.顺序表的定义
    • A. 顺序表定义:
    • 2.顺序表的基本操作
    • 3.顺序表优缺点
  • 三、线性表的链式表示
    • 1.单链式表的定义
    • 2.单链式表的基本操作
    • 3.双链表
    • 4.循环链表
    • 5.静态链表
  • 总结


前言

主要介绍线性表


一、线性表

1. 含义

由n(n>=0)个相同类型的元素组成的有序集合。

2. 表示

L=(a1,a2, ,ai-1,ai,ai+1, , an)
在这里插入图片描述

3. 特点

表中元素是有限的;
表中元素的数组类型都相同也即每一个元素占用相同大小的空间;
表中元素具有逻辑上的顺序性,在序列中各元素排列有其先后顺序。

4. 基本操作

初始化表;求表长;按值查找;按位查找;插入操作;删除操作;输出操作;判空操作;销毁操作。

二、线性表的顺序表示

1.顺序表的定义

A. 顺序表定义:

线性表的顺序存储称为顺序表。
B. 实现方法:
B.1 静态分配:

//伪代码,只是代码中的重要部分,不可执行
#define MaxSize 50//定义线性表长度
typedef int ElemType;//重定义int型为ElemType
typedef struct {
	ElemType data[MaxSize];//定义顺序表元素
	int length;//定义顺序表当前长度
}SqList;//定顺序表型变量SqList

B.2 动态分配:

//伪代码,只是代码中的重要部分,不可执行
#define InitSize 100//定义线性表长度
typedef int ElemType;//重定义int型为ElemType
typedef struct {
	ElemType *data;//指示动态分配数组的指针
	int MaxSize ,length;//数组的最大容量与当前长度
}SqList;//定顺序表型变量SqList
//C的初试动态分配语句:
L.data=(ElemType*)malloc(sizeof(ElemType)*InitSize);
//C++的初试动态分配语句:
L.data=new ElemType[InitSize];

2.顺序表的基本操作

A. 插入操作
A. 1 核心代码

//伪代码,只是代码中的重要部分,不可执行
if (i<1 || i>L.length + 1)//判断插入的位置是否合法
    return false;
if (L.length>MaxSize)//判断插入的位置是否超出存储空间
	return false;
for (int j = L.length; j >= i; j--)//移动顺序表中的元素,依次向后移动
	L.data[j] = L.data[j - 1];
L.data[i-1] = e;//数组下标从0开始,插入第一个位置,访问的下标为0
L.length++;

A. 2实战代码

#include
#include

//1.静态分配(结构体声明)
#define MaxSize 50
typedef int ElemType;//顺序表中元素的类型
typedef struct {
	ElemType data[MaxSize];//定义数组用来存储元素
	int length;//当前顺序表中元素个数
}Sqlist;

//2.表中插入元素
bool ListInsert(Sqlist& L, int i, ElemType e)
{
	//插入不合理的处理
	if (i<1 || i>L.length + 1)//判断插入的位置是否合法
		return false;
	if (L.length>MaxSize)//判断插入的位置是否超出存储空间
		return false;
	for (int j = L.length; j >= i; j--)//移动顺序表中的元素,依次向后移动
		L.data[j] = L.data[j - 1];
	L.data[i-1] = e;//数组下标从0开始,插入第一个位置,访问的下标为0
	L.length++;
	return true;
}

//3.输出表
void PrintList(Sqlist L)
{
	for (int i = 0; i < L.length; i++)
		printf("L.data[%d]=%d\n",i, L.data[i]);
}

//4.主函数
int main()
{
	Sqlist L;//定义顺序表
	bool ret;//查看返回值,True或False
	//手动在顺序表中赋值
	L.data[0] = 1;
	L.data[1] = 2;
	L.data[2] = 3;
	L.length = 3;//一共三个元素
	ret = ListInsert(L, 2, 60);//往第二个位置插入60
	if (ret)
	{
		printf("插入成功\n");
		PrintList(L);//输出表
	}
	else {
		printf("插入失败\n");
	}
	return 0;
}

B. 删除操作
B. 1 核心代码

//伪代码,只是代码中的重要部分,不可执行
if (i<1 || i>L.length + 1)//判断插入的位置是否合法
    return false;
del=L.date[i-1];//将被删除的元素赋给del
for (int j = i; j<L.length; j++)//将删除位置后的元素依次向前移动
	L.data[j-1] = L.data[j];
L.length--;

B. 2 实战代码

#include
#include
/*
//1.2动态分配
#define InitSize 50
typedef struct {
	ElemType data[MaxSize];
	int length;//当前顺序表中元素个数
}Sqlist;
*/

//1.1静态分配(结构体声明)
#define MaxSize 50
typedef int ElemType;//顺序表中元素的类型
typedef struct {
	ElemType data[MaxSize];//定义数组用来存储元素
	int length;//当前顺序表中元素个数
}Sqlist;


//2.输出表
void PrintList(Sqlist L)
{
	for (int i = 0; i < L.length; i++)
		printf("L.data[%d]=%d\n", i, L.data[i]);
}
//4.1表中删除元素
bool ListDelete(Sqlist& L, int i, ElemType e)
{
	//插入不合理的处理
	if (i<1 || i>L.length + 1)//判断删除的位置是否合法
		return false;
	if (L.length ==0)//顺序表中没有元素,无需删除
		return false;
	e = L.data[i - 1];//获取顺序表中要删除的元素赋值给e
	for (int j =i; j< L.length; j++)
		L.data[j-1] = L.data[j];
	L.length--;
	return true;

}
int main()//
{
	//1.3顺序表赋值
	Sqlist L;//定义顺序表
	//手动在顺序表中赋值
	L.data[0] = 1;
	L.data[1] = 2;
	L.data[2] = 3;
	L.length = 3;//一共三个元素
	//4.2删除操作部分
	bool ret_delete;//查看返回值,True或False
	ElemType del=NULL;
	ret_delete = ListDelete(L, 2, del);//删除第二个位置的元素
	if (ret_delete)
	{
		printf("删除成功\n");
		printf("删除元素为%d\n",del);
		PrintList(L);
	}
	else {
		printf("删除失败\n");
	}
	return 0;
}

C. 查询操作
C. 1 核心代码

//伪代码,只是代码中的重要部分,不可执行
for (int i = 0; i < L.length; i++)
		if (L.data[i]==e)
			return i+1;//数组从0开始,顺序表从1开始

B. 2 实战代码

#include
#include
//1.1静态分配(结构体声明)
#define MaxSize 50
typedef int ElemType;//顺序表中元素的类型
typedef struct {
	ElemType data[MaxSize];//定义数组用来存储元素
	int length;//当前顺序表中元素个数
}Sqlist;


//2.输出表
void PrintList(Sqlist L)
{
	for (int i = 0; i < L.length; i++)
		printf("L.data[%d]=%d\n", i, L.data[i]);
}
//5.1按值查询
int LocateElem(Sqlist& L,ElemType e)
{
	
	for (int i = 0; i < L.length; i++)
		if (L.data[i]==e)
			return i+1;//数组从0开始,顺序表从1开始
	return 0;
}
int main()//
{
	//1.3顺序表赋值
	Sqlist L;//定义顺序表
	//手动在顺序表中赋值
	L.data[0] = 1;
	L.data[1] = 2;
	L.data[2] = 3;
	L.length = 3;//一共三个元素
	//5.2 查询操作部分
	bool ret_search;//查看返回值,True或False
	ret_search = LocateElem(L,2);//删除第二个位置的元素
	if (ret_search)
	{
		printf("查询成功\n");
		printf("元素位置为%d\n", ret_search);//bool与int型可以相互转换
	}
	else {
		printf("查询失败\n");
	}
	return 0;
}

F. 修改操作

在这里插入代码片

3.顺序表优缺点

A. 优点:
可以随机存取(根据表头元素地址和元素序号)表中任意一个元素;
存储密度高,每个节点只存储数据元素。
B. 缺点:
插入与删除操作需要移动大量的元素;
线性表变化较大时,难以确定存储空间的容量;
存储分配需要一整段连续的存储空间,不够灵活。

三、线性表的链式表示

1.单链式表的定义

A. 定义:线性表的链式表存储为单链表
第二章 线性表_第1张图片
头指针:链表中第一个节点的存储位置,用来标识单链表。

头结点:在单链表第一个节点之前附加一个节点,为了操作上的方便。

若链表有头结点,则头指针永远指向头结点,不论链表是否为空,头指针均不为空,头指针是链表的必须元素,他标识一个链表。

头结点是为了操作的方便而设立的,其数据域一般为空,或者存放链表的长度,有头结点后,对在第一个节点前插入和删除第一个节点的操作就统一了,不需要频繁重置指针,但头结点不是必须的。

B. 核心代码

typedef struct LNode//单链表结构点类型
{
ElemType data;//数字域
stryct LNode *next;//指针域
}LNode,*LinkList;

2.单链式表的基本操作

A. 头插法建立单链表

//伪代码,只是代码中的重要部分,不可执行
LinkList CreatList1(LinkList& L)//list_head_insert
{
	LNode* s;
	int x;
	L = (LinkList)malloc(sizeof(LNode));//带头结点的链表
	L->next = NULL;//L->data里面没用放东西
	scanf("%d", &x);//从标准输入读取数据
	while (x != 9999) {
		s = (LNode*)malloc(sizeof(LNode));//申请一个空间,强制类型转换
		s->data = x;//
		s->next = L->next;//让新节点的next指针指向链表的第一个元素
		L->next = s;//让s作为第一个元素
		scanf("%d", &x);//读取标准输入
	}
	return 0;
}

B. 尾插法建立单链表

//伪代码,只是代码中的重要部分,不可执行
LinkList CreatList2(LinkList& L)//list_head_insert
{
	int x;
	L = (LinkList)malloc(sizeof(LNode));//带头结点的链表
	LNode* s, * r = L;//LinkList s,r;r代表表尾节点
	scanf("%d", &x);//从标准输入读取数据
	while (x != 9999) {
		s = (LNode*)malloc(sizeof(LNode));//申请一个空间,强制类型转换
		s->data = x;//
		r->next = s;//让尾部节点指向新节点
		r = s;//r指向新的表尾节点
		scanf("%d", &x);//读取标准输入
	}
	r->next =NULL;//尾部节点的next指针赋值NULL
	return 0;
}

C. 按序号查询

//伪代码,只是代码中的重要部分,不可执行
LNode* GetElem(LinkList L, int i)
{
	int j = 1;
	LNode* p = L->next;//让P指向第一个节点
	if (i == 0)
		return L;
	if (i < 1)
		return NULL;

	while (p && j < i)
	{
		p = p->next;
		j++;
	}
	return p;
}

D. 按值查找操作

LinkList LocateElem(LinkList L, ElemType i)
{
	LinkList p = L->next;
	while (p != NULL && p->data != i)
	{
		p = p->next;
	}
	return p;
}

E.链表的删除

bool ListDelete(LinkList L, int i)
{
	LinkList p = GetElem(L, i-1);
	if (NULL == p)
	{
		return false;
	}
	LinkList q = p->next;
	p->next = q->next;
	free(q);
	q = NULL;//避免野指针
	return true;
}

F.链表的插入

//5.1链表的插入
bool ListFrontInsert(LinkList L, int i, ElemType e)
{
	LinkList p = GetElem(L, i - 1);
	if (NULL == p)
	{
		return false;
	}
	LinkList s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
}

G.实战代码

#define _CRT_SECURE_NO_WARNINGS
#include
#include
//0.0定义链表
typedef int ElemType;
typedef  struct LNode {
	ElemType data;
	struct LNode* next;//指向下一个节点
}LNode,*LinkList;

//1.1头插法新建链表
LinkList CreatList1(LinkList& L)//list_head_insert
{
	LNode* s;
	int x;
	L = (LinkList)malloc(sizeof(LNode));//带头结点的链表
	L->next = NULL;//L->data里面没用放东西
	scanf("%d", &x);//从标准输入读取数据
	while (x != 9999) {
		s = (LNode*)malloc(sizeof(LNode));//申请一个空间,强制类型转换
		s->data = x;//
		s->next = L->next;//让新节点的next指针指向链表的第一个元素
		L->next = s;//让s作为第一个元素
		scanf("%d", &x);//读取标准输入
	}
	return 0;
}
//1.2尾插法新建链表
LinkList CreatList2(LinkList& L)//list_head_insert
{
	int x;
	L = (LinkList)malloc(sizeof(LNode));//带头结点的链表
	LNode* s, * r = L;//LinkList s,r;r代表表尾节点
	scanf("%d", &x);//从标准输入读取数据
	while (x != 9999) {
		s = (LNode*)malloc(sizeof(LNode));//申请一个空间,强制类型转换
		s->data = x;//
		r->next = s;//让尾部节点指向新节点
		r = s;//r指向新的表尾节点
		scanf("%d", &x);//读取标准输入
	}
	r->next =NULL;//尾部节点的next指针赋值NULL
	return 0;
}
//2.1打印链表
void PrintList(LinkList L)
{
	L = L->next;
	while (L!= NULL)
	{
		printf("%3d", L->data);//打印当前节点数据
		L = L->next;
	}
	printf("\n");
};
//3.1链表的按序号查找
LNode* GetElem(LinkList L, int i)
{
	int j = 1;
	LNode* p = L->next;//让P指向第一个节点
	if (i == 0)
		return L;
	if (i < 1)
		return NULL;

	while (p && j < i)
	{
		p = p->next;
		j++;
	}
	return p;
}

//3.2链表的按值查找
LinkList LocateElem(LinkList L, ElemType i)
{
	LinkList p = L->next;
	while (p != NULL && p->data != i)
	{
		p = p->next;
	}
	return p;
}
//4.1链表的删除
bool ListDelete(LinkList L, int i)
{
	LinkList p = GetElem(L, i-1);
	if (NULL == p)
	{
		return false;
	}
	LinkList q = p->next;
	p->next = q->next;
	free(q);
	q = NULL;//避免野指针
	return true;
}

//5.1链表的插入
bool ListFrontInsert(LinkList L, int i, ElemType e)
{
	LinkList p = GetElem(L, i - 1);
	if (NULL == p)
	{
		return false;
	}
	LinkList s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
}


int main()
{
	//1.3链表的建立
	LinkList L;//链表头,是结构体指针类型
	CreatList1(L);//输入数据可以为3 4 5 6 7 9999,头插法新建列表
	//CreatList2(L);//输入数据可以为3 4 5 6 7 9999,尾插法新建列表

	//2.2打印链表
	//PrintList(L);

	//3.3链表的按序号查找
	//LinkList search;//用来存储拿到的某一个节点
	//search = GetElem(L, 6);
	//if(search!=NULL)
	//{
	//	printf("按序号查找成功\n");
	//	printf("%d\n", search->data);
	//}
	 
	//3.4链表的按值查找
	//LinkList search;//用来存储拿到的某一个节点
	//search = LocateElem(L,4);//按值查询
	//if (search != NULL)
	//{
	//	printf("按序号查找成功\n");
	//	printf("%d\n", search->data);
	//}

	//4.2链表的删除
	//ListDelete(L, 3);

	//5.2链表的插入
	//ListFrontInsert(L, 2, 88);
	PrintList(L);
}

3.双链表

A. 定义

//核心代码
typedef struct DNode {//双链表节点类型
	ElemType data;//数据域
	struct DNode* prior;//前驱指针
	struct DNode* next;//后继指针
}DNode, * DLinkList;

B. 插入操作
第二章 线性表_第2张图片

//伪代码,表示主要功能
s->next = p->next;//表示上图第1步
p->next->prior = s;//表示上图第2步
s->prior = p;//表示上图第3步
p->next = s;//表示上图第4步

C.删除操作
第二章 线性表_第3张图片

p->next = q->next;//表示上图第1步
q->next->prior = p;//表示上图第2步
free(q);//表示上图第3步

D.实战代码

#define _CRT_SECURE_NO_WARNINGS
#include
#include
//0双向链表的定义
typedef int ElemType;
typedef struct DNode {//双链表节点类型
	ElemType data;//数据域
	struct DNode* prior;//前驱指针
	struct DNode* next;//后继指针
}DNode, * DLinkList;

//4按序号查找
DNode* GetElem(DLinkList DL, int i)
{
	int j = 1;
	DNode* p = DL->next;//让P指向第一个节点
	if (i == 0)
		return DL;
	if (i < 1)
		return NULL;

	while (p && j < i)
	{
		p = p->next;
		j++;
	}
	return p;
}

//1.1头插法插入操作
DLinkList DList_head_insert(DLinkList &DL)
{
	DNode *s;int x;
	DL = (DLinkList)malloc(sizeof(DNode));//带头结点的链表
	DL->next = NULL;
	DL->prior = NULL;
	scanf("%d", &x);//从标准输入读取数据,//3 4 5 6 7 9999
	while (x != 9999) {
		s = (DNode*)malloc(sizeof(DNode));//申请一个空间,强制类型转换
		s->data = x;
		s->next = DL->next;
		if (DL->next!= NULL)
		{
            DL->next->prior = s;
		}
		s->prior = DL;
		DL->next = s;
		scanf("%d", &x);
	}
	return 0;
}
//1.2尾插法插入操作
DLinkList DList_tail_insert(DLinkList &DL)
{

	int x;
	DL = (DLinkList)malloc(sizeof(DNode));//带头结点的链表
	DNode* s, * r = DL;//r代表尾部指针,等价于DNode* s;DNode** r = DL;
	DL->prior = NULL;
	scanf("%d", &x);//从标准输入读取数据,//3 4 5 6 7 9999
	while (x != 9999) {
		s = (DNode*)malloc(sizeof(DNode));//申请一个空间,强制类型转换
		s->data = x;
		r->next = s;
		s->prior = r;
		r = s;//r指向新的表尾节点
		scanf("%d", &x);
	}
	r->next = NULL;//尾结点指针置空
	return 0;
}
//1.3新节点插入操作
bool DList_Front_insert(DLinkList& DL,int i,ElemType e)
{
	DLinkList p = GetElem(DL,i-1);
	if (NULL == p)
	{
		return false;
	}
	DLinkList s = (DLinkList)malloc(sizeof(DNode));//带头结点的链表
	s->data =e;
	s->next = p->next;
	s->next->prior = s;
	p->next = s;
	return true;
}
//2输出双链表
void PrintDList(DLinkList DL)
{
	DL = DL->next;
	while (DL != NULL)
	{
		printf("%3d", DL->data);//打印当前节点数据
		DL = DL->next;
	}
	printf("\n");
};

//3删除操作
bool DListDelete(DLinkList& DL, int i)
{
	DLinkList p = GetElem(DL, i - 1);
	DLinkList q = p->next;
	if (NULL == p)
	{
		return false;
	}
	if(NULL==q)
	{
		return false;
	}
	q->next->prior = p;
	p->next = q->next;//表示上图第1步
	free(q);//释放节点对应空间
	return true;
}

int main()
{
	DLinkList DL;
	//1.1头插法插入操作
	//DList_head_insert(DL);//list_head_insert
	//1.2尾插法插入操作
	DList_tail_insert(DL);//3 4 5 6 7 9999
	//1.3新节点插入操作
	//DList_Front_insert(DL,2,45);//3 4 5 6 7 9999

	//2输出双链表
	PrintDList(DL);

	3删除操作
	DListDelete(DL, 2);
	PrintDList(DL);

	//4按序号查找
	/*DLinkList search_1;
	search_1 = GetElem(DL,2);
	if(search_1!=NULL)
	{
		printf("按序号查找成功\n");
		printf("%d\n",search_1->data);
	}*/
	return 0;
}

4.循环链表

A. 循环单链表
第二章 线性表_第4张图片
循环链表与单链表的区别:表中最后一个结点的next指针不是NULL,而是指向头结点,从而整个链条形成一个环。
B. 循环双链表
第二章 线性表_第5张图片
循环双链表与双链表的区别:表中最后一个结点的next指针不是NULL,而是指向头结点;表中第一个结点的prior指针不是NULL,而是指向尾结点。

5.静态链表

静态链表是借助数组来描述线性表的链式存储结构,且结构类型如下:

#define Maxsize 50
typedef struct{
    ElemType data;
    int next;
    }SLinkList[Maxsize];

第二章 线性表_第6张图片

总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

你可能感兴趣的:(数据结构,数据结构)