王道数据结构代码---第二章:线性表(顺序表+单链表+双链表)

目录

1.顺序表

        顺序表的结构体定义

        插入:在第i个位置中插入元素e

        删除:删除第i个位置的元素

        查找:查找e在第几个位置(位置从1开始)

        完整的代码

2.单链表

        单链表的结构体定义

        头插法新建单链表L

        尾插法新建单链表L

        按序号查找结点值

        按值查找表结点:值第一次出现的位置

        新结点插入第i个位置,值为e

        删除结点操作

        完整代码

3.双链表

        双链表结构体定义

        头插法

        尾插法

        按序号查找结点值:与单链表一样

        新结点插入第i个位置,值为e

        删除结点

        完整代码

1.顺序表

        顺序表的结构体定义

顺序表的一大特点:逻辑上上相邻的两个元素,在物理位置上也相邻。

//静态分配法
typedef struct SeqList      //顺序表的结构体定义
{
	ElemType  data[MaxSize];   //定义的数组,用来存元素
	int length;         //顺序表当前的长度
}SeqList;     

//动态分配法

        插入:在第i个位置中插入元素e

//插入操作:函数为ListInsert
// 函数功能:在位置为i的地方,插入元素e。(注:i从1开始,而数组的下标从0开始)
//L代表的是顺序表,i代表要插入的位置,从1开始,e是要插入的元素
//假如在位置为2的地方插入60,也就是在下标为1的地方插入
bool ListInsert(seqList& L, int i, ElemType e)
{
	if (i<1 || i>L.length + 1)
		return false;                 //判断插入位置是否合法
	if (L.length >= MaxSize)   //元素存满了,不能在存了
		return false;            //超出空间了,只能访问下标为0到49
	for (int j = L.length; j >= i; j--) //移动顺序表中的元素,依次往后移动  
		L.data[j] = L.data[j - 1];  
	L.data[i - 1] = e; //数组下标从0开始,插入第一个位置,访问下标为0
	L.length++;      //插入元素后,顺序表长度需要加1
	return true;
}

        删除:删除第i个位置的元素

//删除操作:函数为ListDelete
//函数功能:删除第i个位置的元素,用引用变量e返回
bool ListDelete(SeqList& L, int i, ElemType& e)
{
	if (i < 1 || i>L.length)
		return false;          //判断i的范围是否合法
	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--;     //删除后,顺序表长度需要减1
	return true;

}

        查找:查找e在第几个位置(位置从1开始)

//查找操作:函数为LocateElem
//函数功能:在顺序表中,查找e,查找成功,返回位置(位置从1开始)。查找失败,返回0.
int LocateElem(SeqList L, ElemType e)
{
	int i;
	for (i = 0; i < L.length; i++)
		if (L.data[i] == e)
			return i + 1;
	return 0;
}

        完整的代码

#include <stdio.h>  //头文件
#include <stdlib.h>

#define MaxSize 50    //定义线性表的最大长度
typedef int ElemType;   //对int起别名,为了代码注释。顺序表元素的类型

typedef struct SeqList      //顺序表的结构体定义
{
	ElemType  data[MaxSize];   //定义的数组,用来存元素
	int length;         //顺序表当前的长度
}SeqList; 

//插入操作:函数为ListInsert
// 函数功能:在位置为i的地方,插入元素e。(注:i从1开始,而数组的下标从0开始)
//L代表的是顺序表,i代表要插入的位置,从1开始,e是要插入的元素
//假如在位置为2的地方插入60,也就是在下标为1的地方插入
bool ListInsert(SeqList &L, int i, ElemType e)
{
	if (i<1 || i>L.length + 1)
		return false;                 //判断插入位置是否合法
	if (L.length >= MaxSize)   //元素存满了,不能在存了
		return false;            //超出空间了,只能访问下标为0到49
	for (int j = L.length; j >= i; j--) //移动顺序表中的元素,依次往后移动  
		L.data[j] = L.data[j - 1];   
	L.data[i - 1] = e; //数组下标从0开始,插入第一个位置,访问下标为0
	L.length++;      //插入元素后,长度需要加1
	return true;
}

//删除操作:函数为ListDelete
//函数功能:删除第i个位置的元素,用引用变量e返回
bool ListDelete(SeqList& L, int i, ElemType& e)
{
	if (i < 1 || i>L.length)
		return false;          //判断i的范围是否合法
	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--;     //删除后,顺序表长度需要减1
	return true;

}

//查找操作:函数为LocateElem
//函数功能:在顺序表中,查找e,查找成功,返回位置(位置从1开始)。查找失败,返回0.
int LocateElem(SeqList L, ElemType e)
{
	int i;
	for (i = 0; i < L.length; i++)   //遍历这个顺序表
		if (L.data[i] == e)
			return i + 1;   //加1就是元素在顺序表中的位置
	return 0;
}

//打印顺序表元素
void PrintList(SeqList &L)
{
	for (int i = 0; i < L.length; i++)
	{
		printf("%3d", L.data[i]);   //要求所有元素打印到一排,%3d是输出的长度
		//注:%3d是右对齐
	}
	printf("\n");
}

int main()
{
	SeqList L;  //顺序表的名称
	bool ret;   //查看返回值,布尔型是True,或者False
	ElemType del; //用来存要删除的元素
	//首先手动在顺序表中前3个元素赋值
	L.data[0] = 1;
	L.data[1] = 2;
	L.data[2] = 3;
	L.length = 3;   //总共3个元素,顺序表的长度为3

	ret = ListInsert(L, 2, 60);  //往位置为2的位置插入60这个元素
	if (ret)
	{
		printf("插入成功\n");
		PrintList(L); //打印成功后的顺序表
	}
	else
	{
		printf("插入失败\n");
	}

	ret = ListDelete(L, 1, del);//在顺序表中,删除第一个位置的元素,并把删除元素的值输出
	if (ret)
	{
		printf("删除成功\n");
		printf("删除元素值为%d\n", del);
		PrintList(L);
	}
	else
	{
		printf("删除失败\n");
	}

	ret = LocateElem(L, 30);
	if (ret)
	{
		printf("查找成功\n");
		printf("元素位置为%d\n", ret);
		PrintList(L);
	}
	else
	{
		printf("查找失败\n");
	}
	return 0;
}

2.单链表

        单链表的结构体定义

示意图:

typedef struct LNode
{
	ElemType data;      //数据域
	struct LNode* next;  //指针域
}LNode,*LinkList;

        头插法新建单链表L

理解的示意图:头插法和中间插入元素的代码是一样的。

最重要的三点:

1:    s->next=L->next;   

2:       L->next=s;

//头插法新建链表
//函数为list_head_insert
LinkList list_head_insert(LinkList& L)
{
	LNode* s; int x;      //s是要插入的结点,x是插入元素的值
	L = (LinkList)malloc(sizeof(LNode));    //带头结点的链表
	L->next = NULL;    //L->data里边没放东西,这个也就是初始化的操作
	scanf("%d", &x);     //从标准输入读取数据
	//3 4 5 6 7 9999
	while (x != 9999) {
		s = (LNode*)malloc(sizeof(LNode));   //给s开辟一个新空间,强制类型转换
		s->data = x;     //把读取到的值,给新空间中的data成员(data指的是链表的数据域)
		s->next = L->next;     //让新结点(也就是待插入节点)的next指针指向链表的第一个元素
		L->next = s;    //L->next指向谁,谁就是第一个元素。让s作为第一个元素(不是头结点)
		scanf("%d", &x);    //读取标准输入
	}
	return L;
}

         尾插法新建单链表L

注意:尾插法它是从空的链表开始插的。

最重要的两步:

1:        r->next = s;         //让尾部结点指向新结点
 2:       r = s;                  //r指向新的表尾结点

//尾插法新建链表
//函数为:list_tail_insert
LinkList list_tail_insert(LinkList& L)
{
	int x;
	L = (LinkList)malloc(sizeof(LNode));   //带头节点的链表,和头插法是一样的。
	LNode* s, * r = L;       //Linklist s,r=L;等价     r代表链表表尾结点,指向链表尾部
	//3 4 5 6 7 9999
	scanf("%d", &x);
	while (x != 9999) {
		s = (LNode*)malloc(sizeof(LNode));    //s是新插入的结点
		s->data = x;    //赋值
		r->next = s;   //让尾部结点指向新结点
		r = s;    //r指向新的表尾结点
		scanf("%d", &x);
	}
	r->next = NULL;   //尾结点的next指针赋值为NULL
	return L;
}

        按序号查找结点值

//按序号查找结点值
//函数为:GetElem
LNode* GetElem(LinkList L, int i)  //i是位置      LNode*也可以写LinkList    
{
	int j = 1;   //从位置1开始,遍历
	LNode* p = L->next;  //p移动了一次,让p指向第一个结点。 也可以写成LinkList  p = L->next;
	if (i == 0) 
		return L;    //i是0返回头结点
	if (i < 1)
		return NULL;   //i是负值就返回空
	while (p && j < i)
	{
		p = p->next;  //让p指向下一个结点
		j++;
	}
	return p;
}

        按值查找表结点:值第一次出现的位置

//按值查找
//函数为:LocateElem
LNode *LocateElem(LinkList L,ElemType e)
{
	LNode *p=L->next;
	while(p!=NULL&&p->data!=e)
		p=p->next;
	return p;
}

        新结点插入第i个位置,值为e

插入结点的示意图:

//新结点插入第i个位置,值为e
//函数为:ListFrontInsert
bool ListFrontInsert(LinkList L, int i, ElemType e)  
{
	LinkList p = GetElem(L, i - 1);  //拿到要插入位置的前一个位置的地址值
	if (p == NULL)
	{
		return false;
	}
	LinkList s = (LNode*)malloc(sizeof(LNode));//为新插入的结点申请空间
	s->data = e;   //赋值
	s->next = p->next;    //插入结点的操作
	p->next = s;      
	return true;
}

        删除结点操作

示意图:

//删除第i个结点
//函数为:ListDelete
bool ListDelete(LinkList L, int i)
{
	LinkList p = GetElem(L, i - 1);  //查找删除位置的前驱结点  
	if (p == NULL)        //删除的位置不存在 
	{ 
		return false;
	} 
	LinkList q;  
	q = p->next;
	p->next = q->next;//断链
	free(q);//释放对应结点的空间
	q = NULL;
	return true;
}

        完整代码

#define  _CRT_SECURE_NO_WARNINGS   //解决scanf编译报错问题
#include <stdio.h>  //头文件
#include <stdlib.h>

typedef int ElemType;
//结构体定义
typedef struct LNode     
{
	ElemType data;    //数据域    
	struct LNode* next;   //指针域----结构体指针
}LNode,*LinkList;

//头插法新建链表
//函数为list_head_insert
LinkList list_head_insert(LinkList& L)
{
	LNode* s; int x; //s是要插入的结点,x是插入元素的值
	L = (LinkList)malloc(sizeof(LNode));//带头结点的链表
	L->next = NULL;//L->data里边没放东西,这个也就是初始化的操作
	scanf("%d", &x);//从标准输入读取数据
	//3 4 5 6 7 9999
	while (x != 9999) {
		s = (LNode*)malloc(sizeof(LNode));//给s开辟一个新空间,强制类型转换
		s->data = x;  //把读取到的值,给新空间中的data成员(data指的是链表的数据域)
		s->next = L->next;     //让新结点(也就是待插入节点)的next指针指向链表的第一个元素
		L->next = s;    //L->next指向谁,谁就是第一个元素。让s作为第一个元素(不是头结点)
		scanf("%d", &x);//读取标准输入
	}
	return L;
}

//尾插法新建链表
//函数为:list_tail_insert
LinkList list_tail_insert(LinkList& L)
{
	int x;
	L = (LinkList)malloc(sizeof(LNode));//带头节点的链表,和头插法是一样的。
	LNode* s, * r = L;       //Linklist s,r=L;等价     r代表链表表尾结点,指向链表尾部
	//3 4 5 6 7 9999
	scanf("%d", &x);
	while (x != 9999) {
		s = (LNode*)malloc(sizeof(LNode));    //s是新插入的结点
		s->data = x;    //赋值
		r->next = s;   //让尾部结点指向新结点
		r = s;//r指向新的表尾结点
		scanf("%d", &x);
	}
	r->next = NULL;   //尾结点的next指针赋值为NULL
	return L;
}

//打印链表中每个结点的值
//函数为:PrintList
void PrintList(LinkList L)
{
	L = L->next;      //相当于移动到第一个有元素的位置,也及时移动到头结点后一个元素
	while (L != NULL)
	{
		printf("%3d", L->data);       //打印当前结点数据,%3d是右对齐,长度为3。
		L = L->next;     //指向下一个结点
	}
	printf("\n");
}

//按序号查找结点值
//函数为:GetElem
LNode* GetElem(LinkList L, int i)  //i是位置      LNode*也可以写LinkList    
{
	int j = 1;   //从位置1开始,遍历
	LNode* p = L->next;  //p移动了一次,让p指向第一个结点。 也可以写成LinkList  p = L->next;
	if (i == 0) 
		return L;    //i是0返回头结点
	if (i < 1)
		return NULL;   //i是负值就返回空
	while (p && j < i)
	{
		p = p->next;  //让p指向下一个结点
		j++;
	}
	return p;
}

//按值查找
//函数为:LocateElem
LNode* LocateElem(LinkList L, ElemType e)   //e是要查的值,   LNode*也可以写LinkList   
{
	LNode* p = L->next;   //p移动了一次,让p指向第一个结点。 也可以写成LinkList  p = L->next;
	while (p != NULL && p->data != e)   //p!=NULL和p是一个意思
		p = p->next;        
	return p;
}

//新结点插入第i个位置,值为e
//函数为:ListFrontInsert
bool ListFrontInsert(LinkList L, int i, ElemType e)  
{
	LinkList p = GetElem(L, i - 1);  //拿到要插入位置的前一个位置的地址值
	if (p == NULL)
	{
		return false;
	}
	LinkList s = (LNode*)malloc(sizeof(LNode));//为新插入的结点申请空间
	s->data = e;   //赋值
	s->next = p->next;    //插入结点的操作
	p->next = s; 
	return true;
}

//删除第i个结点
//函数为:ListDelete
bool ListDelete(LinkList L, int i)
{
	LinkList p = GetElem(L, i - 1);  //查找删除位置的前驱结点  
	if (p == NULL)        //删除的位置不存在 
	{ 
		return false;
	} 
	LinkList q;  
	q = p->next;
	p->next = q->next;//断链
	free(q);//释放对应结点的空间
	q = NULL;
	return true;
}


int main()
{
	LinkList L;  //链表头,是结构体指针类型
	LinkList search;     //也可以写成 LNode *search;
	//list_head_insert(L); //头插法:输入数据可以为3 4 5 6 7 9999
	//printf("--------------------单链表的头插法--------------------------\n");

	list_tail_insert(L);  //尾插法
	printf("--------------------单链表的尾插法-----------------------------\n");
	PrintList(L);   //打印链表

	search = GetElem(L, 2);    //查找链表第二个位置的元素值
	if (search != NULL)
	{
		printf("---------------------按序号查找成功-------------------------\n");
		printf("%d\n", search->data);
	}

	search = LocateElem(L, 6);//按值查询
	if (search != NULL)
	{
		printf("---------------------按值查找成功--------------------------\n");
		printf("%d\n", search->data);
	}

	ListFrontInsert(L, 2, 99);//新结点插入第2个位置,值为99
	printf("--------------------在第二个位置插入99------------------\n");
	PrintList(L);

	ListDelete(L, 4);//删除第4个结点
	printf("--------------------删除第四个结点------------------\n");
	PrintList(L);

	return 0;
}

3.双链表

        双链表结构体定义

示意图:

typedef struct DNode
{
	ElemType data;  //数据域
	struct DNode* prior, * next;  //前驱指针,  后继指针
}DNode, * DLinkList;

        头插法

示意图

//双链表头插法
//函数为:Dlist_head_insert
DLinkList Dlist_head_insert(DLinkList& DL)
{
	DNode* s;   //s为要插入的新结点
	int x;     //x为暂存的数据
	DL = (DLinkList)malloc(sizeof(DNode));//带头结点的链表,申请空间,DL就是头结点
	DL->next = NULL;  
	DL->prior = NULL;  //初始化操作,把前驱和后继指针都写为NULL
	scanf("%d", &x);//从标准输入读取数据
	//3 4 5 6 7 9999
	while (x != 9999) {
		s = (DLinkList)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 DL;
}

        尾插法

示意图

//双链表尾插法
//函数为:Dlist_tail_insert
DLinkList Dlist_tail_insert(DLinkList& DL)
{
	int x;         //要插入元素的值
	DL = (DLinkList)malloc(sizeof(DNode));//带头节点的链表
	DNode* s, * r = DL;   //r代表尾指针,   s为要插入的结点
	DL->prior = NULL;
	//3 4 5 6 7 9999
	scanf("%d", &x);
	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;//尾结点的next指针赋值为NULL
	return DL;
}

           按序号查找结点值:与单链表一样

//按序号查找结点值
DNode* GetElem(DLinkList DL, int i)
{
	int j = 1;
	DNode* p = DL->next;
	if (i == 0)
		return DL;
	if (i < 1)
		return NULL;
	while (p && j < i)
	{
		p = p->next;
		j++;
	}
	return p;
}

        新结点插入第i个位置,值为e

//新结点插入第i个位置
bool DListFrontInsert(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;
	p->next->prior = s;
	s->prior = p;
	p->next = s;
	return true;
}

        删除结点

示意图

//删除第i个结点
bool DListDelete(DLinkList DL, int i)
{
	DLinkList p = GetElem(DL, i - 1);
	if (NULL == p)
	{
		return false;
	}
	DLinkList q;
	q = p->next;
	if (q == NULL)//删除的元素不存在
		return false;
	p->next = q->next;//断链
	if (q->next != NULL)
	{
		q->next->prior = p;
	}
	free(q);//释放对应结点的空间
	return true;
}

          完整代码

#define  _CRT_SECURE_NO_WARNINGS   //解决scanf编译报错问题
#include <stdio.h>  //头文件
#include <stdlib.h>

typedef int ElemType;

//双链表结构体定义
//与单链表相比,增加了一个prior指针(也就是前驱指针)
typedef struct DNode
{
	ElemType data;  //数据域
	struct DNode* prior, * next;  //前驱指针,  后继指针
}DNode, * DLinkList;

//双链表头插法
//函数为:Dlist_head_insert
DLinkList Dlist_head_insert(DLinkList& DL)
{
	DNode* s;   //s为要插入的新结点
	int x;     //x为暂存的数据
	DL = (DLinkList)malloc(sizeof(DNode));//带头结点的链表,申请空间,DL就是头结点
	DL->next = NULL;  
	DL->prior = NULL;  //初始化操作,把前驱和后继指针都写为NULL
	scanf("%d", &x);//从标准输入读取数据
	//3 4 5 6 7 9999
	while (x != 9999) {
		s = (DLinkList)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 DL;
}

//双链表尾插法
//函数为:Dlist_tail_insert
DLinkList Dlist_tail_insert(DLinkList& DL)
{
	int x;         //要插入元素的值
	DL = (DLinkList)malloc(sizeof(DNode));//带头节点的链表
	DNode* s, * r = DL;   //r代表尾指针,   s为要插入的结点
	DL->prior = NULL;
	//3 4 5 6 7 9999
	scanf("%d", &x);
	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;//尾结点的next指针赋值为NULL
	return DL;
}

//按序号查找结点值
DNode* GetElem(DLinkList DL, int i)
{
	int j = 1;
	DNode* p = DL->next;
	if (i == 0)
		return DL;
	if (i < 1)
		return NULL;
	while (p && j < i)
	{
		p = p->next;
		j++;
	}
	return p;
}

//新结点插入第i个位置
bool DListFrontInsert(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;
	p->next->prior = s;
	s->prior = p;
	p->next = s;
	return true;
}

//删除第i个结点
bool DListDelete(DLinkList DL, int i)
{
	DLinkList p = GetElem(DL, i - 1);
	if (NULL == p)
	{
		return false;
	}
	DLinkList q;
	q = p->next;
	if (q == NULL)//删除的元素不存在
		return false;
	p->next = q->next;//断链
	if (q->next != NULL)
	{
		q->next->prior = p;
	}
	free(q);//释放对应结点的空间
	return true;
}

//双链表打印
void PrintDList(DLinkList DL)
{
	DL = DL->next;
	while (DL != NULL)
	{
		printf("%3d", DL->data);
		DL = DL->next;
	}
	printf("\n");
}

int main()
{
	DLinkList DL;   //链表头
	DLinkList search;

	//Dlist_head_insert(DL);  //头插法
	//printf("------------双链表的头插法-------------\n");
	//输入的数据可为     3 4 5 6 7 9999
	Dlist_tail_insert(DL);  //尾插法
	printf("------------双链表的尾插法-------------\n");
	PrintDList(DL);  //打印链表

	search = GetElem(DL, 2);      //查找第二个位置的元素值
	if (search != NULL)
	{
		printf("------------按序号查找成功----------\n");
		printf("%d\n", search->data);
	}

	DListFrontInsert(DL, 3, 99);
	printf("------------在第三个位置插入元素99后----------\n");
	PrintDList(DL);

	DListDelete(DL, 2);
	printf("------------删除第2个位置----------\n");
	PrintDList(DL);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值