动态通讯录实现(C语言)

news2024/11/25 0:35:10

目录

前言:

一:单个节点的设计和主逻辑 

结点设计

主逻辑

二:接口实现

(1)生成一个新的结点

(2)增加信息

(3)打印信息

(4)查找 

(5)删除信息

(6)修改信息

(7)排序

 插入排序

快速排序

(8)已有数据读取

(9)更新数据录入

三:全部代码

contact.h(声明)

contact.c(接口)

test.c(主逻辑)


前言:

本文使用的存储结构是不带哨兵位链表,还包含了文件操作(读取和录入),有类似课程设计的同学可能需要(完整代码在最后)。

链表(知道基础概念,清楚形参实参关系的可以不看):

https://blog.csdn.net/2301_76269963/article/details/129586021?spm=1001.2014.3001.5502


一:单个节点的设计和主逻辑 

结点设计

需求:一个人要包含姓名、年龄、性别、电话号码、地址这些信息,把这些信息封装成一个结构体。


要让每个人的数据链接成一个整体,节点除了个人信息之外还要保存下个结点的地址


主逻辑

(1)每次开始都从文件中读取数据

(2)通讯录的功能包括:①增加数据

                                     ②依据名字删除数据

                                     ③依据名字查找数据

                                     ④依据名字修改数据

                                     ⑤依据年龄排序数据

                                     ⑥打印数据

(3)退出后重新录入数据


二:接口实现

(1)生成一个新的结点

//生成一个新节点
Node* BuyNewNode()
{
	Node* NewNode = (Node*)malloc(sizeof(Node));
	if (NewNode == NULL)
	{
		printf("malloc error\n");
		exit(-1);
	}
	NewNode->next = NULL;

	printf("请输入名字:");
	scanf("%s", NewNode->data.name);
	printf("请输入年龄:");
	scanf("%d", &NewNode->data.age);
	printf("请输入性别:");
	scanf("%s", NewNode->data.sex);
	printf("请输入电话:");
	scanf("%s", NewNode->data.tele);
	printf("请输入地址:");
	scanf("%s", NewNode->data.addr);

	return NewNode;
}

(2)增加信息

//增加信息,可能要更改头指针,传二级指针修改一级指针
void AddContact(Node** pphead)
{
	//生成新节点
	Node* NewNode = BuyNewNode();
	//单独处理空的情况
	if (*pphead == NULL)
	{
		*pphead = NewNode;
		return;
	}
	else
	{
		NewNode->next = *pphead;
		*pphead = NewNode;
	}
}

图解:

(3)打印信息

//打印信息
void PrintContact(Node* phead)
{
	//注意打印格式,加-表示字符左对齐,数字表示最小字符数(不足补空格)
	printf("%-20s\t%-5s\t%-5s\t%-20s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
	while (phead != NULL)
	{
		printf("%-20s\t%-5d\t%-5s\t%-20s\t%-20s\n", 
			   phead->data.name,phead->data.age,
			   phead->data.sex,phead->data.tele,phead->data.addr);
		//迭代
		phead = phead->next;
	}
}

(4)查找 

注意:后续的删除和修改都需要用到查找,为增强代码复用性,查找函数返回结点的地址,如果未查找到就返回空。

//查找,第二个参数为名字字符串首地址
Node* FindContact(Node* phead,char* name)
{
	//遍历链表
	while (phead)
	{
		//strcmp()函数用于字符串比较,相同返回0,否则返回非0值
		if (strcmp(phead->data.name, name) == 0)
		{
			return phead;
		}
		phead = phead->next;
	}
	//没找到返回空
	return NULL;
}

(5)删除信息

先查找,找到待删除结点地址,如果待删除结点为头节点,需要更新头指针

否则就需要找到待删除结点的前一个结点,前一个结点指针域指向待删除结点的下一个结点,然后释放待删除结点。

//删除信息,可能要更改头指针,传二级指针修改一级指针
void DelContact(Node** pphead)
{
	//信息为空不能删除
	assert(*pphead != NULL);
	
	//查找
	char name[max_name];
	printf("请输入要删除联系人的名字:");
	scanf("%s", name);
	Node* pos = FindContact(*pphead,name);
	if (pos == NULL)
	{
		printf("查找失败\n");
		return;
	}

	//如果pos是第一个节点
	if (pos == *pphead)
	{
		//找到下一个
		Node* NEXT = pos->next;
		free(pos);
		*pphead = NEXT;
	}
	else
	{
		//找到前一个
		Node* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
	}
	printf("删除成功\n");
}

图解:

(6)修改信息

先查找到目标结点,然后进行修改。

//修改信息
void ModifyContact(Node* phead)
{
	//查找
	char name[max_name];
	printf("请输入要修改联系人的名字:");
	scanf("%s", name);
	Node* pos = FindContact(phead, name);
	if (pos == NULL)
	{
		printf("查找失败\n");
		return;
	}
	else
	{
		printf("找到了,进行修改\n");
		printf("请输入名字:");
		scanf("%s", pos->data.name);
		printf("请输入年龄:");
		scanf("%d", &pos->data.age);
		printf("请输入性别:");
		scanf("%s", pos->data.sex);
		printf("请输入电话:");
		scanf("%s", pos->data.tele);
		printf("请输入地址:");
		scanf("%s", pos->data.addr);
	}
}

(7)排序

这里给两种实现方式,第一种使用插入排序(方便理解)第二种使用快速排序(效率更快,有空间消耗,代码量更多)


 插入排序

插入排序的核心思想是:将一个未排序的元素插入到已排序的子序列中,使得插入后依然保持有序。具体实现时,我们可以从第二个元素开始,将其与前面已排序序列的元素比较,找到其正确的插入位置,然后插入即可。在插入过程中,需要将已排序序列中比该元素大的元素后移,为该元素腾出插入位置(看一遍就可以看代码和图解)。

//交换数据
void swap(Node* p1, Node* p2)
{
	struct PepInfo tmp = p1->data;
	p1->data = p2->data;
	p2->data = tmp;
}

//进行排序
void sort(Node* phead)
{
	//空不排序
	assert(phead != NULL);

	Node* begin = phead;
	Node* end = phead->next;
	while (end)
	{
		while (begin != end)
		{
			if (begin->data.age > end->data.age)
			{
				swap(begin,end);
			}
			begin = begin->next;
		}
        //更新
		end = end->next;
		begin = phead;
	}
}

图解:


快速排序

这里就不展开讲快速排序的思想了,也不建议用快速排序,一般通讯录这样的数据量用插入排序就足够了,感兴趣的同学可以看链接。

快速排序链接:https://blog.csdn.net/2301_76269963/article/details/130473353?spm=1001.2014.3001.5502

先将链表中的数据拷贝到数组中,对数组进行排序,再把数组数据拷贝回链表。

//交换数据
void swap(PepInfo* p1, PepInfo* p2)
{
	PepInfo tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

// 三数取中
int GetMidIndex(PepInfo* a, int left, int right)
{
	int midi = left + (right - left) / 2;

	if (a[midi].age > a[right].age)
	{
		if (a[midi].age < a[left].age)
			return midi;
		else if (a[right].age > a[left].age)
			return right;
		else
			return left;
	}
	else    //a[right] > a[mid]
	{
		if (a[midi].age > a[left].age)
			return midi;
		else if (a[left].age < a[right].age)
			return left;
		else
			return right;
	}
}

//前后指针法
int partion3(PepInfo* a, int left, int right)
{
	int midi = GetMidIndex(a, left, right);
	swap(&a[midi], &a[left]);

	int keyi = left;
	int prev = left;
	int cur = left + 1;
	while (cur <= right)
	{
		//++prev和cur相等,无效交换,不换
		if (a[cur].age < a[keyi].age && ++prev != cur)
		{
			swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	swap(&a[keyi], &a[prev]);
	return prev;
}

//快速排序
void QuickSort(PepInfo* a, int left, int right)
{
	//如果left>right,区间不存在
	//left==right,只有一个元素,可以看成是有序的
	if (left >= right)
	{
		return;
	}
	else
	{
		//单次排序
		int keyi = partion3(a, left, right);
		//分成左右区间,排序左右区间
		QuickSort(a, left, keyi - 1);
		QuickSort(a, keyi + 1, right);
	}
}

// 链表快速排序函数
void sortList(Node* phead) 
{
	// 处理空或单节点链表的情况
	if (!phead || !phead->next)
	{
		return;
	}

	//求链表长度
	int len = 0;
	Node* cur = phead;
	while (cur) 
	{
		len++;
		cur = cur->next;
	}

	// 将链表转换为数组
	PepInfo* arr = malloc(len * sizeof(PepInfo));
	if (arr == NULL)
	{
		printf("malloc error\n");
		exit(-1);
	}
	cur = phead;
	for (int i = 0; i < len; i++) 
	{
		arr[i] = cur->data;
		cur = cur->next;
	}

	// 快速排序
	QuickSort(arr, 0, len - 1);

	// 将数组转换为链表
	cur = phead;
	for (int i = 0; i < len; i++) 
	{
		cur->data = arr[i];
		cur = cur->next;
	}

	free(arr);
}

(8)已有数据读取

①生成一个新结点,将读取的数据存入新结点中。

②第一次读取时链表为空,修改头指针。

③后面的新结点链接到链表的尾部。

(注意:读模式打开文件需要文件夹中有这个文件,第一次需要手动创建一个data.txt文件)

//已有数据读取,可能要更改头指针,传二级指针修改一级指针
void StartFileEntry(Node** pphead)
{
	FILE* pf = fopen("data.txt", "rb");
	if (pf == NULL)
	{
		printf("打开文件失败\n");
		exit(-1);
	}

	//保存读取到的数据
	PepInfo* tmp = (PepInfo*)malloc(sizeof(PepInfo));
	if (tmp == NULL)
	{
		printf("malloc error\n");
		exit(-1);
	}
	//先读取一次
	fread(tmp, sizeof(PepInfo), 1, pf);
	//保存尾部节点地址
	Node* tail = NULL;
	
	//读取到结尾时feof()返回0,未读取到结尾返回非0值
	while (!feof(pf))
	{
		//生成新节点
		Node* NewNode = (Node*)malloc(sizeof(Node));
		if (NewNode == NULL)
		{
			printf("malloc error\n");
			exit(-1);
		}
		NewNode->data = *tmp;
		NewNode->next = NULL;
		//第一次头为空,更新
		if (*pphead == NULL)
		{
			*pphead = NewNode;
			tail = *pphead;
		}
		//头不为空,新节点链接到尾后面,尾指针更新
		else
		{
			tail->next = NewNode;
			tail = tail->next;
		}
		//继续读取
		fread(tmp, sizeof(PepInfo), 1, pf);
	}
	fclose(pf);
}

(9)更新数据录入

链表为空,直接返回;否则遍历链表,依次录入数据。

//新数据录入
void UpdateFileEntry(Node* phead)
{
	FILE* pf = fopen("data.txt", "wb");
	if (phead == NULL)
	{
		return;
	}
	else
	{
		while (phead)
		{
			fwrite(&(phead->data), sizeof(PepInfo), 1, pf);
			phead = phead->next;
		}
	}
	fclose(pf);
}

三:全部代码

contact.h(声明)

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
//方便后面更改这些信息的最大容量
#define max_name 20
#define max_tele 20
#define max_addr 20
#define max_sex 10

//个人信息
typedef struct PepInfo
{
	char name[max_name];
	int age;
	char sex[max_sex];
	char tele[max_tele];
	char addr[max_addr];
}PepInfo;

typedef struct Node
{
	//个人信息
	struct PepInfo data;
	//指针域
	struct Node* next;
}Node;


//生成一个新节点
Node* BuyNewNode();

//增加信息,可能要更改头指针,传二级指针修改一级指针
void AddContact(Node** pphead);

//打印信息
void PrintContact(Node* phead);

//查找
Node* FindContact(Node* phead, char* name);

//删除信息,可能要更改头指针,传二级指针修改一级指针
void DelContact(Node** pphead);

//修改信息
void ModifyContact(Node* phead);

//进行排序
// 链表快速排序函数
void sortList(Node* phead);
//插入排序
void sort(Node* phead);

//已有数据读取,可能要更改头指针,传二级指针修改一级指针
void StartFileEntry(Node** pphead);

//更新数据录入
void UpdateFileEntry(Node* phead);

contact.c(接口)

#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"

//生成一个新节点
Node* BuyNewNode()
{
	Node* NewNode = (Node*)malloc(sizeof(Node));
	if (NewNode == NULL)
	{
		printf("malloc error\n");
		exit(-1);
	}
	NewNode->next = NULL;

	printf("请输入名字:");
	scanf("%s", NewNode->data.name);
	printf("请输入年龄:");
	scanf("%d", &NewNode->data.age);
	printf("请输入性别:");
	scanf("%s", NewNode->data.sex);
	printf("请输入电话:");
	scanf("%s", NewNode->data.tele);
	printf("请输入地址:");
	scanf("%s", NewNode->data.addr);

	return NewNode;
}

//增加信息,可能要更改头指针,传二级指针修改一级指针
void AddContact(Node** pphead)
{
	//生成新节点
	Node* NewNode = BuyNewNode();
	//单独处理空的情况
	if (*pphead == NULL)
	{
		*pphead = NewNode;
		return;
	}
	else
	{
		NewNode->next = *pphead;
		*pphead = NewNode;
	}
}

//打印信息
void PrintContact(Node* phead)
{
	//注意打印格式,加-表示字符左对齐,数字表示最小字符数(不足补空格)
	printf("%-20s\t%-5s\t%-5s\t%-20s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
	while (phead != NULL)
	{
		printf("%-20s\t%-5d\t%-5s\t%-20s\t%-20s\n", 
			   phead->data.name,phead->data.age,
			   phead->data.sex,phead->data.tele,phead->data.addr);
		//迭代
		phead = phead->next;
	}
}

//查找,第二个参数为名字字符串首地址
Node* FindContact(Node* phead,char* name)
{
	//遍历链表
	while (phead)
	{
		//strcmp()函数用于字符串比较,相同返回0,否则返回非0值
		if (strcmp(phead->data.name, name) == 0)
		{
			return phead;
		}
		phead = phead->next;
	}
	//没找到返回空
	return NULL;
}

//删除信息,可能要更改头指针,传二级指针修改一级指针
void DelContact(Node** pphead)
{
	//断言,信息为空不能删除
	assert(*pphead != NULL);
	
	//查找
	char name[max_name];
	printf("请输入要删除联系人的名字:");
	scanf("%s", name);
	Node* pos = FindContact(*pphead,name);
	if (pos == NULL)
	{
		printf("查找失败\n");
		return;
	}

	//如果pos是第一个节点
	if (pos == *pphead)
	{
		//找到下一个
		Node* NEXT = pos->next;
		free(pos);
		*pphead = NEXT;
	}
	else
	{
		//找到前一个
		Node* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
	}
	printf("删除成功\n");
}

//修改信息
void ModifyContact(Node* phead)
{
	//查找
	char name[max_name];
	printf("请输入要修改联系人的名字:");
	scanf("%s", name);
	Node* pos = FindContact(phead, name);
	if (pos == NULL)
	{
		printf("查找失败\n");
		return;
	}
	else
	{
		printf("找到了,进行修改\n");
		printf("请输入名字:");
		scanf("%s", pos->data.name);
		printf("请输入年龄:");
		scanf("%d", &pos->data.age);
		printf("请输入性别:");
		scanf("%s", pos->data.sex);
		printf("请输入电话:");
		scanf("%s", pos->data.tele);
		printf("请输入地址:");
		scanf("%s", pos->data.addr);
	}
}

交换数据
//void swap(PepInfo* p1, PepInfo* p2)
//{
//	PepInfo tmp = *p1;
//	*p1 = *p2;
//	*p2 = tmp;
//}
//
 三数取中
//int GetMidIndex(PepInfo* a, int left, int right)
//{
//	int midi = left + (right - left) / 2;
//
//	if (a[midi].age > a[right].age)
//	{
//		if (a[midi].age < a[left].age)
//			return midi;
//		else if (a[right].age > a[left].age)
//			return right;
//		else
//			return left;
//	}
//	else // a[right] > a[mid]
//	{
//		if (a[midi].age > a[left].age)
//			return midi;
//		else if (a[left].age < a[right].age)
//			return left;
//		else
//			return right;
//	}
//}
//
前后指针法
//int partion3(PepInfo* a, int left, int right)
//{
//	int midi = GetMidIndex(a, left, right);
//	swap(&a[midi], &a[left]);
//
//	int keyi = left;
//	int prev = left;
//	int cur = left + 1;
//	while (cur <= right)
//	{
//		//++prev和cur相等,无效交换,不换
//		if (a[cur].age < a[keyi].age && ++prev != cur)
//		{
//			swap(&a[prev], &a[cur]);
//		}
//		cur++;
//	}
//	swap(&a[keyi], &a[prev]);
//	return prev;
//}
//
快速排序
//void QuickSort(PepInfo* a, int left, int right)
//{
//	//如果left>right,区间不存在
//	//left==right,只有一个元素,可以看成是有序的
//	if (left >= right)
//	{
//		return;
//	}
//	else
//	{
//		//单次排序
//		int keyi = partion3(a, left, right);
//		//分成左右区间,排序左右区间
//		QuickSort(a, left, keyi - 1);
//		QuickSort(a, keyi + 1, right);
//	}
//}
//
 链表快速排序函数
//void sortList(Node* phead) 
//{
//	// 处理空或单节点链表的情况
//	if (!phead || !phead->next)
//	{
//		return;
//	}
//
//	//求链表长度
//	int len = 0;
//	Node* cur = phead;
//	while (cur) 
//	{
//		len++;
//		cur = cur->next;
//	}
//
//	// 将链表转换为数组
//	PepInfo* arr = malloc(len * sizeof(PepInfo));
//	if (arr == NULL)
//	{
//		printf("malloc error\n");
//		exit(-1);
//	}
//	cur = phead;
//	for (int i = 0; i < len; i++) 
//	{
//		arr[i] = cur->data;
//		cur = cur->next;
//	}
//
//	// 快速排序
//	QuickSort(arr, 0, len - 1);
//
//	// 将数组转换为链表
//	cur = phead;
//	for (int i = 0; i < len; i++) 
//	{
//		cur->data = arr[i];
//		cur = cur->next;
//	}
//
//	free(arr);
//}

//交换数据
void swap(Node* p1, Node* p2)
{
	struct PepInfo tmp = p1->data;
	p1->data = p2->data;
	p2->data = tmp;
}

//进行排序
void sort(Node* phead)
{
	//空不排序
	assert(phead != NULL);

	Node* begin = phead;
	Node* end = phead->next;
	while (end)
	{
		while (begin != end)
		{
			if (begin->data.age > end->data.age)
			{
				swap(begin,end);
			}
			begin = begin->next;
		}
		end = end->next;
		begin = phead;
	}
}


//已有数据读取,可能要更改头指针,传二级指针修改一级指针
void StartFileEntry(Node** pphead)
{
	FILE* pf = fopen("data.txt", "rb");
	if (pf == NULL)
	{
		printf("打开文件失败\n");
		exit(-1);
	}

	//保存读取到的数据
	PepInfo* tmp = (PepInfo*)malloc(sizeof(PepInfo));
	if (tmp == NULL)
	{
		printf("malloc error\n");
		exit(-1);
	}
	//先读取一次
	fread(tmp, sizeof(PepInfo), 1, pf);
	//保存尾部节点地址
	Node* tail = NULL;
	
	//读取到结尾时feof()返回0,未读取到结尾返回非0值
	while (!feof(pf))
	{
		//生成新节点
		Node* NewNode = (Node*)malloc(sizeof(Node));
		if (NewNode == NULL)
		{
			printf("malloc error\n");
			exit(-1);
		}
		NewNode->data = *tmp;
		NewNode->next = NULL;
		//第一次头为空,更新
		if (*pphead == NULL)
		{
			*pphead = NewNode;
			tail = *pphead;
		}
		//头不为空,新节点链接到尾后面,尾指针更新
		else
		{
			tail->next = NewNode;
			tail = tail->next;
		}
		//继续读取
		fread(tmp, sizeof(PepInfo), 1, pf);
	}
	fclose(pf);
}


//新数据录入
void UpdateFileEntry(Node* phead)
{
	FILE* pf = fopen("data.txt", "wb");
	if (phead == NULL)
	{
		return;
	}
	else
	{
		while (phead)
		{
			fwrite(&(phead->data), sizeof(PepInfo), 1, pf);
			phead = phead->next;
		}
	}
	fclose(pf);
}

test.c(主逻辑)

#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"

void menu()
{
	printf("********************************\n");
	printf("***** 1.增加      2.删除  ******\n");
	printf("***** 3.查找      4.修改  ******\n");
	printf("***** 5.排序      6.打印  ******\n");
	printf("***** 0.退出              ******\n");
	printf("********************************\n");

}

int main()
{
	Node* head = NULL;
	int input = 0;

	//原信息录入,这里用读模式打开,第一次需要手动创建文件
	StartFileEntry(&head);
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			AddContact(&head);
			printf("增加成功\n");
			break;
		case 2:
			DelContact(&head);
			break;
		case 3:
		{
			char name[max_name];
			printf("请输入要查找联系人的名字:");
			scanf("%s", name);
			Node* pos = FindContact(head, name);
			if (pos == NULL)
			{
				printf("查找失败\n");
			}
			else
			{
				printf("找到了\n");
				printf("%-20s\t%-5d\t%-5s\t%-20s\t%-20s\n",
					pos->data.name, pos->data.age, pos->data.sex,
					pos->data.tele, pos->data.addr);
			}
			break;
		}
		case 4:
			ModifyContact(head);
			break;
		case 5:
			sort(head);
			printf("排序成功\n");
			break;
		case 6:
			PrintContact(head);
			break;
		case 0:
			printf("退出\n");
			break;
		default:
			printf("非法输入,重新输入\n");
			break;
		}
	} while (input);

	//新信息录入
	UpdateFileEntry(head);
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/552541.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言复习笔记3

1.标识符常量和宏函数&#xff08;宏函数是简单替换所以需要把括号加到位&#xff09; #include<stdio.h>#define MAX 1000//标识符常量 #define num 10 //#define SUM(X,Y) XY //不对 #define SUM(X,Y) ((X)(Y))int max(int a, int b) {return a>b?a:b; }int main(…

系列八、vue配置请求

一、vue2配置请求转发 config/index.js proxyTable配置后端的请求地址 proxyTable: {/: {target: "http://localhost:9000", // 后端服务器地址changeOrigin: true,pathRewrite: {^/: }} }, 注意事项&#xff1a;vue2中不像大多数教程里边讲的那样&#xff0c;直接…

Apache NiFi:实时数据流处理的可视化利器【上进小菜猪大数据系列】

上进小菜猪&#xff0c;沈工大软件工程专业&#xff0c;爱好敲代码&#xff0c;持续输出干货。欢迎订阅本专栏&#xff01; Apache NiFi是一个强大的、可扩展的开源数据流处理工具&#xff0c;广泛应用于大数据领域。本文将介绍Apache NiFi的核心概念和架构&#xff0c;并提供…

路由守卫的几种方式-M

vue的路由 Vue-router是Vue.js官方的路由插件。vue的单页面应用是基于路由和组件的&#xff0c;路由用于设定访问路径&#xff0c;并将路径和组件映射起来。传统的页面应用&#xff0c;是用一些超链接来实现页面切换和跳转的。在vue-router单页面应用中&#xff0c;则是路径之…

C# | KMeans聚类算法的实现,轻松将数据点分组成具有相似特征的簇

C# KMeans聚类算法的实现 文章目录 C# KMeans聚类算法的实现前言示例代码实现思路测试结果结束语 前言 本章分享一下如何使用C#实现KMeans算法。在讲解代码前先清晰两个小问题&#xff1a; 什么是聚类? 聚类是将数据点根据其相似性分组的过程&#xff0c;它有很多的应用场景&…

章节1:信息收集

章节1:信息收集 1 信息收集概览 01 为什么要做信息收集&#xff1f; 渗透测试的流程 确定目标 信息收集 漏洞扫描 漏洞利用 形成报告 信息收集包括的内容 域名信息、IP段、开放的端口、网站架构、文件目录结构、软件版本、WAF、旁站、C段… 分类 域名相关信息IP相关…

Redis缓存数据库(四)

目录 一、概述 1、Redis Sentinel 1.1、docker配置Redis Sentinel环境 2、Redis存储方案 2.1、哈希链 2.2、哈希环 3、Redis分区(Partitioning) 4、Redis面试题 一、概述 1、Redis Sentinel Redis Sentinel为Redis提供了高可用解决方案。实际上这意味着使用Sentinel…

Java 与排序算法(1):冒泡排序

一、冒泡排序 冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单的排序算法&#xff0c;它的基本思想是通过不断交换相邻两个元素的位置&#xff0c;使得较大的元素逐渐往后移动&#xff0c;直到最后一个元素为止。冒泡排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2)&…

《Kali渗透基础》02. 基本工具

kali渗透 1&#xff1a;基本工具1.1&#xff1a;NetCat1.1.1&#xff1a;命令参数1.1.2&#xff1a;示例 1.2&#xff1a;NCat1.2.1&#xff1a;命令参数1.2.2&#xff1a;示例 1.3&#xff1a;WireShark1.4&#xff1a;TCPdump1.4.1&#xff1a;命令参数1.4.2&#xff1a;示例…

C语言——函数

目录 1. 函数基本用法1.1 定义和三要素1.2 函数的声明和定义1.2.1 函数声明1.2.2 函数定义格式 1.3 函数调用1.4 函数传参1.4.1 值传递1.4.2 地址传递1.4.3 数组传递 1.5 函数和栈区 2. 开辟堆空间2.1 堆的概念2.2.malloc函数2.2.1 定义2.2.2 用法 2.3 free()函数定义注意&…

随机数发生器设计(一)

1 随机数发生器设计概述 密码行业的随机数发生器总体框架标准为GM/T 0103。随机数发生器可以分为硬件随机数发生器和软件随机数发生器。 硬件随机数发生器一般以组成部件的形式集成在安全芯片的内部&#xff0c;或者随机数发生器本身就是安全芯片。考虑到随机数发生器是密码产…

ChatGPT 能自己跑代码了!

公众号关注 “GitHubDaily” 设为 “星标”&#xff0c;每天带你逛 GitHub&#xff01; time leap, sci-fi, photorealistic, --niji 5 --ar 3:2 --s 1000 自 ChatGPT 发布以来&#xff0c;各行各业对其能力探索的举措一直没有停止。 很多大厂纷纷跟进&#xff0c;竞相推出自研…

Springboot +spring security,登录用户数据获取

一.简介 前面章节学习了登录表单的配置并且对源码进行了简单的分析&#xff0c;现在有个问题了&#xff0c;既然用户登录了&#xff0c;那么如何在接口中获取用户信息呢。这篇文章就来看下这个问题&#xff0c;代码中获取登录用户信息。 二.创建项目 如何创建一个SpringSecu…

笔记:BLIP源码之(1)数据集预处理【仅考虑Image-Text Retrieval on COCO】

BLIP&#xff1a;Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and Generat 论文的两个贡献如下&#xff1a; 从模型的角度&#xff1a;提出了 Encoder-Decoder (MED) 的多模态混合 An MED can operate either as a unimodal encode…

Js常识三

文章目录 作用域GCclosure变量和函数提升函数参数 作用域 GC Js Gc 算法 引用计数&#xff08;已淘汰&#xff09;标记清除 closure 一句话&#xff1a;内层函数 外层函数的变量 闭包作用&#xff1a;私有化数据&#xff0c;or 私有化状态 变量和函数提升 Js 祖传var变…

C语言结构体初级

目录 一、为什么要用结构体 二、使用结构体的具体形式 1.结构体类型的声明&#xff08;main函数外部&#xff09; 2.结构体变量的定义&#xff08;在main函数内或者外&#xff09; 3.结构体变量的初始化 4.结构体成员的访问 5.结构体的传参 跑了这么久&#xff0c;再坚…

分布式软件架构——单体架构

序言 当一项大工程需要大量人员共同开发&#xff0c;并保证它们分布在网络中的大量服务器节点能够同时运行&#xff0c;那么随着项目规模的增大、运行时间变长&#xff0c;它必然会受到墨菲定律的无情打击。 Murphy’s Law&#xff1a;Anything that can go wrong will go wro…

Qt文件系统源码分析—第四篇QLockFile

深度 本文主要分析Windows平台&#xff0c;Mac、Linux暂不涉及 本文只分析到Win32 API/Windows Com组件/STL库函数层次&#xff0c;再下层代码不做探究 本文QT版本5.15.2 类关系图 QTemporaryFile继承QFile QFile、QSaveFile继承QFileDevice QFileDevice继承QIODevice Q…

法规标准-ISO 17361标准解读

ISO 17361是做什么的&#xff1f; ISO 17361全称为智能交通系统-车道偏离警告系统性能要求和测试程序&#xff0c;其中主要描述了LDWS系统的功能要求及测试要求 系统功能 车道偏离警告系统的功能元件应符合图中的要求&#xff0c;抑制请求、车速检测、驾驶员偏好和其他附加功…

[CTF/网络安全] 攻防世界 simple_js 解题详析

[CTF/网络安全] 攻防世界 simple_js 解题详析 代码分析代码漏洞姿势String[fromCharCode]总结 题目描述&#xff1a;小宁发现了一个网页&#xff0c;但却一直输不对密码。(Flag格式为 Cyberpeace{xxxxxxxxx} ) 页面源代码&#xff1a; 代码分析 function dechiffre(pass_enc){…