管理系统中的增,删,查,改

一 单链表

1.1什么是链表

链表,别名链式存储结构或单链表,用于存储逻辑关系为 “一对一” 的数据。与顺序表不同,链表不限制数据的物理存储状态,换句话说,使用链表存储的数据元素,其物理存储位置是随机的。
它是以结构体为节点,将一个结构体看成数据域和指针域两个部分,数据域用于存储数据,指针域用于连接下一个节点.

1.2链表的特点

1、链表没有固定的长度,可以自由增加节点
2、链表能够实现快速的插入删除数据,也就是可以快速的插入和删除链表中的节点
3、与数组类似,链表也是一种线性数据结构
4、链表的尾结点的后继必定指向空

1.3单链表的结构示意图

在这里插入图片描述

1.4单链表结构体的声明

在这里插入图片描述

typedef int Type;  //数据类型  通过取别名的形式进行灵活使用
struct Node{
	Type data;		//链表节点的数据域,用于存储数据
	struct Node *next; //链表节点的指针域,用于指向和连接下一个节点
};

1.5单链表的功能

1、创建单链表节点

//1.创建单链表节点,开辟内存空间
Node * list_init(Type data)
{
	Node * temp = (Node *)malloc(sizeof(Node));
	if (NULL == temp)
	{
		ERROR("节点创建失败");
		//assert(temp);	//错误检查函数,函数参数为假就报错
		return NULL;
	}
	temp->data = data;	//给数据域赋值
	temp->next = NULL;	//初始化next指针为空,指向空
	return temp;
}

2、链接单链表节点

//链表各个节点的内存不是连续的
Node * list_create_end(Type arr[], int n)
{
	Node * head = NULL;
	Node * end = NULL;
	for (int i = 0; i < n; i++)
	{
		if (NULL == head)
		{
			head = list_init(arr[i]);
			end = head;
			continue;
		}
		//从尾部链接两个节点
		end->next = list_init(arr[i]);
		end = end->next;
	}
	return head;
}

3、单链表的打印输出

void list_print(Node * head)
{
	for (Node * temp = head; temp != NULL; temp = temp->next)
	{
		printf("%d ->", temp->data);
	}
	printf("NULL\n");
}

4、单链表节点的插入
在这里插入图片描述
插入元素 头 中间 尾部
插入头结点之后 当做首元结点 插入到链表中间的某个位置
插入到链表的最末端,作为链表中最后一个数据元素
两个操作:a.将新结点的next指针指向插入位置后的结点.
b.将插入位置前结点的next指针指向插入结点.

void list_insert(Node * head, int index, Type data)
{
	Node * temp = head;

	if (0 == index)
	{
		Node * newNode = list_init(data);
		newNode->next = head->next;
		head->data = data;
		head->next = newNode;
		return;
	}
	for (int i = 0; i < index - 1; i++)
	{
		assert(temp);
		temp = temp->next;
	}
	Node * newNode = list_init(data);
	newNode->next = temp->next;
	temp->next = newNode;
}

5、单链表节点的删除
在这里插入图片描述
删除元素
(1)将结点从链表上摘下来
(2)手动释放结点 回收内存空间

void list_delete(Node * head, int index)
{
	Node * temp = head;
	if (0 == index)
	{
		Node * temp2 = head->next;
		head->data = head->next->data;
		head->next = head->next->next;
		free(temp2);
		return;
	}
	for (int i = 0; i < index - 1; i++)
	{
		assert(temp);
		temp = temp->next;
	}
	Node * old = temp->next;
	temp->next = old->next;
	//temp->next = temp->next->next;
	free(old);
}

6、通过下标获取单链表指定位置上的数据
在这里插入图片描述

Node * list_get(Node * head, int index)
{
	Node * temp = head;
	for (int i = 0; i < index - 1; i++)
	{
		assert(temp);
		temp = temp->next;
	}
	return temp;
}

7,修改数据

//更新元素
Node *amendElem(Node *p, int add, int newElem)
{
	Node*temp = p;
	temp = temp->next;//遍历之前,temp指向首元结点
    for (int i = 1; i < add; i++)
		temp = temp->next;
	temp->elem = newElem;
	return p;
}

二 双向链表

2.1什么是双向链表

双链表是链表的一种,和单链表一样,也是把多个结构体节点用结构体指针连接起来,只不过单链表只有一个指向下一个节点的结构体指针,而双链表有两个结构体指针分别指向上一个节点和下一个节点。

2.2双向链表的特点

1、双链表和单链表一样,没有固定的长度,可以自由增加节点
2、双链表有两个指针域,一个用于指向前一个节点,另一个用于指向下一个节点。
3、由于有指向前一个节点的指针域,双链表可以很好的支持逆序输出

2.3双链表的结构示意图

在这里插入图片描述

2.4双向链表结构体的声明

在这里插入图片描述

typedef struct Link
{
	int elem;//代表数据域
	struct Link *next;//代表着指针域,指向 直接后继 元素
}link;//这个结构体数据类型

2.5双链表的功能

1、创建双链表节点
创建头结点 头指针指向头节点
temp指向头结点
创建下一个节点 下一个节点的地址给temp->next
temp指向下一个节点的地址

link *initLink()
{
	link *p = (link*)malloc(sizeof(link));//创建头节点
	link *temp = p;//指向头结点
	//生成链表
	for (int i = 1; i < 10; i++)
	{
		link *a = (link*)malloc(sizeof(link));
		a->elem = i;// rand() % 100;
		a->next = NULL;
		//将temp节点与新建立的a节点建立逻辑关系
		temp->next = a;//指向下一个节点
		temp = temp->next;//指针temp每次都指向新链表的最后一个节点,其实就是a节点,这里写temp=a也是可以的
	}
	return p;
}

2、链接双链表的插入
在这里插入图片描述插入元素
头 中间 尾部
插入头结点之后 当做首元结点
插入到链表中间的某个位置
插入到链表的最末端,作为链表中最后一个数据元素
两个操作:1.将新结点的next指针指向插入位置后的结点.
2.将插入位置前结点的next指针指向插入结点.

link * insertElem(link *p, int elem, int add)
{
	link *temp = p;
	for (int i = 1; i < add; i++)
	{
		temp = temp->next;
		if (temp == NULL)
		{
			printf("插入位置无效\n");
			return p;
		}
	}
	//创建插入结点c
	link *c = (link*)malloc(sizeof(link));
	c->elem = elem;
	//向链表中插入结点
	c->next = temp->next;
	temp->next = c;
	return p;
}

3、双链表的打印输出

void display(link *p)
{
	link *temp = p;//游标
	while (temp->next)//非0
	{
		temp = temp->next;
		printf("%d ", temp->elem);
	}
	printf("\n");
}

4、双链表节点的删除
在这里插入图片描述
(1)将结点从链表上摘下来
(2)手动释放结点 回收内存空间
temp->next=temp->next->next

link *delElem(link *p, int add)
{
	link *temp = p;
	//遍历到被删除结点的上一个结点
	for (int i = 1; i < add; i++)
	{
		temp = temp->next;
		if (temp->next == NULL)
		{
			printf("没有该结点\n");
			return p;
		}
	}
	link *del = temp->next;//单独设置一个指针指向被删除结点,以防丢失
	temp->next = temp->next->next;
	free(del);//释放该结点,防止内存泄露
	return p;
}

5、双链表的查找
在这里插入图片描述

//查找元素
int selectElem(link *p, int elem)
{
	link *t = p;
	int i = 1;
	while (t->next)
	{
		t = t->next;
		if (t->elem == elem){
			return i;
		}
		i++;
	}
	return -1;//执行到这里,表示查找失败
}

6、双链表的修改

//更新元素
link *amendElem(link *p, int add, int newElem)
{
	link *temp = p;
	temp = temp->next;//遍历之前,temp指向首元结点
	for (int i = 1; i < add; i++)
		temp = temp->next;
	temp->elem = newElem;
	return p;
}

三 练习-约瑟夫环

已知n个人 围坐在一张圆桌周围 从k开始报数
数到m的那个人出列 他的下一个人又重新数1
从3开始数 数到2出列
4 1 3 2 断掉 释放节点
在这里插入图片描述
代码实现

#include<malloc.h>
#include<stdlib.h>
#include<stdio.h>
typedef struct node
{
	int number;//人数
	struct node *next; //指针域
}person;
person * initLink(int n)//初始化
{
	person * head = (person*)malloc(sizeof(person));
	head->number = 1;//从1开始
	head->next = NULL;
	person * cur = head;//指向头结点
	for (int i = 2; i <= n; i++)
	{
		person * body = (person*)malloc(sizeof(person));
		body->number = i;
		body->next = NULL;
		cur->next = body;//将cur节点与新建立的body节点建立逻辑关系
		cur = cur->next;//可以写cur = body
	}
	cur->next = head;//首尾相连
	return head;
}
void last(person * head, int k, int m)//游戏进行
{
	person * tail = head;//指向头结点
	while (tail->next != head)//一圈
	{
		tail = tail->next;
	}
	person *p = head;//指向头结点
	while (p->number != k)//找编号为k
	{
		tail = p;
		p = p->next;
	}
	while (p->next != p)
	{
		for (int i = 1; i < m; i++)//报数
		{
			tail = p;
			p = p->next;
		}
		tail->next = p->next;
		printf("出列人的编号为:%d\n", p->number);
		free(p);
		p = tail->next;//游戏继续 下一个人开始
	}
	printf("出列人的编号为:%d\n", p->number);
	free(p);
}
int main()
{
	printf("输入圆桌上的人数n:");
	int n;
	scanf("%d", &n);
	person *head = initLink(n);
	printf("从第k人开始报数(k>1且k<%d):", n);
	int k;
	scanf("%d", &k);
	printf("数到m的人出列:");
	int m;
	scanf("%d", &m);
	last(head, k, m);
	system("pause");
	return 0;
}

结果展示
在这里插入图片描述

四 总结

1.链表和数组的区别:
数组和顺序表是顺序存储的,也就是内存是连续的;而链表是通过指针将不连续的内存连接起来,实现链式存储的。
2.双链表和单链表的区别:
单链表只有一个指针域,用于指向下一个节点的首地址,而双链表有两个指针域,可以分别指向前一个节点和下一个节点,链表的遍历更加方便。

猜你喜欢

转载自blog.csdn.net/qq_45893999/article/details/106369912