通讯录实现

实现通讯录可以采用顺序表,单链表,双链表等数据结构实现,所以我们需要先写出顺序表,单链表,双链表的代码实现。

顺序表:

定义:顺序表(Sequence List)是一种线性表的存储结构,它采用一组地址连续的存储单元依次存储线性表的数据元素。顺序表中的元素在内存中是连续存储的,通过元素的下标可以直接访问到对应的元素,因此支持随机访问。常见的顺序表实现包括数组。

个人理解:

当你想象一张书架时,你其实已经在思考顺序表了!想象一下,书架上排列着一排排的书籍,每本书都有一个唯一的编号(索引),你可以通过这个编号快速找到并拿起任何一本书。这种布局让你能够轻松地查找特定的书籍,就像顺序表中通过索引能够快速访问特定位置的元素一样。

在这个比喻中:

  • 书架 就相当于顺序表的存储空间,它是由一系列连续的存储单元(书架上的位置)组成的。
  • 每本书 对应于顺序表中的一个元素,它们依次排列在书架上。
  • 书的编号 就相当于顺序表中元素的索引,它能够唯一标识每一本书(或元素)的位置。

顺序表通常包含以下几个要素:

  1. 数据存储区域(数组): 顺序表的主体是一个一维数组,用于存储线性表的元素。数组的大小通常是固定的,但某些情况下也可以动态扩展。

  2. 长度(Length): 顺序表中元素的个数,即表的长度。长度通常作为一个属性存储在顺序表的控制信息中。

  3. 容量(Capacity): 数组的大小,即数组能够容纳的元素个数。当元素数量达到容量上限时,如果需要插入更多的元素,可能需要进行扩容操作。

  4. 控制信息: 包括表的长度和数组的容量等信息,用于管理顺序表的操作。

顺序表的优点包括:

  • 支持随机访问: 由于元素在内存中是连续存储的,可以通过下标直接访问到指定位置的元素,因此支持高效的随机访问。
  • 空间利用率高: 顺序表只需要额外存储控制信息,数据元素本身在内存中是连续存储的,不需要额外的指针来建立关联关系,因此空间利用率相对较高。
  • 操作简单: 插入和删除操作可能需要移动元素,但是在大多数情况下,这些操作的时间复杂度为 O(n),操作相对简单。

但是顺序表也存在一些缺点:

  • 插入和删除操作的效率低: 当需要在中间位置插入或删除元素时,需要移动大量元素,导致时间复杂度为 O(n)。
  • 容量固定: 顺序表的容量通常是固定的,如果元素数量超过了容量上限,可能需要进行扩容操作,这会带来额外的开销。

总的来说,顺序表适用于元素数量相对固定,需要频繁进行随机访问的场景,但不适用于频繁插入和删除元素的场景。

对于使用 C 语言实现顺序表,以下是一种基本的思路:

  1. 定义结构体:首先,你需要定义一个结构体来表示顺序表。这个结构体应该包含一个数组用于存储数据,以及一个变量来表示当前数组中元素的数量。

  2. 初始化:编写一个初始化函数,用于创建一个空的顺序表。在初始化函数中,将顺序表的长度设置为0。

  3. 插入元素:实现一个插入函数,用于向顺序表中插入元素。插入元素时,需要考虑插入位置的合法性,如果位置合法,则将插入位置后的所有元素依次向后移动一位,然后将新元素插入到指定位置。

  4. 删除元素:编写一个删除函数,用于从顺序表中删除指定位置的元素。删除元素时,同样需要考虑删除位置的合法性,如果位置合法,则将删除位置后的所有元素向前移动一位。

  5. 访问元素:如果需要,可以编写一个函数来访问顺序表中指定位置的元素。

  6. 打印顺序表:编写一个函数,用于将顺序表中的所有元素打印出来,方便查看顺序表的内容。

  7. 释放资源:如果需要动态分配内存,则需要编写一个函数来释放顺序表占用的内存。

  8. 主函数:在主函数中调用上述函数来测试顺序表的各种操作,确保它们能够正常工作。

代码实现:

#include <stdio.h>
#include <stdlib.h>#define MAX_SIZE 100 // 定义顺序表的最大容量// 定义顺序表结构体
typedef struct {int data[MAX_SIZE]; // 存储元素的数组int length;         // 当前顺序表的长度
} SeqList;// 初始化顺序表
void init(SeqList *list) {list->length = 0; // 初始长度为0
}// 在指定位置插入元素
int insert(SeqList *list, int index, int element) {// 判断插入位置的合法性if (index < 0 || index > list->length || list->length == MAX_SIZE) {return 0; // 插入失败}// 将插入位置后的所有元素后移一位for (int i = list->length; i > index; i--) {list->data[i] = list->data[i - 1];}// 在插入位置处放入新元素list->data[index] = element;list->length++; // 长度加1return 1; // 插入成功
}// 删除指定位置的元素
int delete(SeqList *list, int index) {// 判断删除位置的合法性if (index < 0 || index >= list->length) {return 0; // 删除失败}// 将删除位置后的所有元素前移一位for (int i = index; i < list->length - 1; i++) {list->data[i] = list->data[i + 1];}list->length--; // 长度减1return 1; // 删除成功
}// 输出顺序表中的所有元素
void display(SeqList *list) {printf("顺序表中的元素为:");for (int i = 0; i < list->length; i++) {printf("%d ", list->data[i]);}printf("\n");
}int main() {SeqList list;init(&list); // 初始化顺序表// 插入元素insert(&list, 0, 1);insert(&list, 1, 2);insert(&list, 2, 3);// 输出顺序表中的元素display(&list);// 删除元素delete(&list, 1);// 再次输出顺序表中的元素display(&list);return 0;
}

 

单链表:

单链表(Singly Linked List)是一种基本的链式数据结构,由一系列节点组成,每个节点包含两部分:数据域和指针域。数据域用于存储节点的数据,指针域用于指向下一个节点,从而将各个节点连接在一起形成链表。下面详细解释单链表的定义、性质、优缺点等:

定义:

单链表由节点组成,每个节点包含两个字段:数据域和指针域。数据域用于存储节点的数据,指针域用于指向下一个节点。单链表的最后一个节点的指针域通常指向空(NULL),表示链表的结束。

个人理解:

用一个简单的图示来比喻单链表:

假设你有一本书,每一页都是一个节点,每一页的最后都有一个箭头指向下一页。这本书的第一页就是链表的头节点,而最后一页则指向空白的页面(表示链表结束)。

Book Pages --> Node 1 --> Node 2 --> Node 3 --> ... --> Node n --> NULL

其中,箭头表示指针,指向下一个节点,最后一个节点指向 NULL,表示链表结束。

这个比喻可以让你更直观地理解单链表的结构:每个节点都包含一些信息(就像书页上的内容),并且通过指针与下一个节点相连,形成一个链条。

性质:

  1. 动态大小:单链表的大小可以动态地增加或减少,根据需要灵活分配内存。

  2. 插入和删除高效:在单链表中,插入和删除节点的时间复杂度为 O(1),只需修改指针即可,不需要移动其他节点。

  3. 不连续存储:单链表中的节点在内存中不必连续存储,因此可以灵活地利用内存空间。

  4. 顺序访问:单链表只能顺序访问,不能像数组一样随机访问,需要从头节点开始逐个访问直到目标节点。

优点:

  1. 动态内存分配:单链表的大小可以动态调整,根据需要灵活地分配和释放内存,节约内存空间。

  2. 高效的插入和删除操作:在单链表中,插入和删除节点的时间复杂度为 O(1),只需修改指针即可,效率高。

  3. 不需要预先分配内存空间:与数组不同,单链表不需要预先分配内存空间,可以根据需要动态地分配内存。

缺点:

  1. 无法随机访问:单链表只能顺序访问,不能像数组一样根据索引随机访问元素,查找效率较低。

  2. 占用额外空间:每个节点都需要额外的指针域来指向下一个节点,占用了额外的存储空间。

  3. 查找节点效率低:查找特定节点需要从头节点开始逐个访问,效率较低,尤其是对于大型链表。

  4. 不利于缓存性能:单链表的节点在内存中存储不连续,可能导致缓存性能较差,增加了访问时间。

总的来说,单链表适用于需要频繁执行插入和删除操作,且对内存动态分配要求较高的情况。它的灵活性和高效的插入、删除操作使其在某些场景下优于数组。

思路:

  1. 节点结构体定义:首先定义了一个节点结构体 struct Node,每个节点包含一个整数数据 data 和一个指向下一个节点的指针 next

  2. 节点插入功能

    • append() 函数用于在链表末尾插入新节点。
    • prepend() 函数用于在链表头部插入新节点。
  3. 节点删除功能deleteNode() 函数用于删除指定数据的节点。

  4. 节点查找功能search() 函数用于查找包含指定数据的节点。

  5. 节点数据修改功能modifyNode() 函数用于修改节点中的数据。

  6. 链表打印功能printList() 函数用于打印链表的所有节点数据。

  7. 主函数:在 main() 函数中,初始化了一个空链表,然后依次进行了如下操作:

    • 在链表末尾插入了数据 1、2、3;
    • 在链表头部插入了数据 0;
    • 删除了数据为 2 的节点;
    • 修改了数据为 3 的节点的数据为 4;
    • 最后打印了链表的所有节点数据。

代码实现:

#include <stdio.h>
#include <stdlib.h>// 定义节点结构体
struct Node {int data;           // 节点数据struct Node* next;  // 指向下一个节点的指针
};// 在链表末尾插入新节点
void append(struct Node** head_ref, int new_data) {// 分配新节点的内存struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));if (new_node == NULL) {printf("内存分配失败\n");return;}// 将数据赋值给新节点new_node->data = new_data;new_node->next = NULL;// 如果链表为空,则将新节点设为头节点if (*head_ref == NULL) {*head_ref = new_node;return;}// 找到链表的最后一个节点struct Node* last = *head_ref;while (last->next != NULL) {last = last->next;}// 将新节点连接到最后一个节点last->next = new_node;
}// 在链表头部插入新节点
void prepend(struct Node** head_ref, int new_data) {// 分配新节点的内存struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));if (new_node == NULL) {printf("内存分配失败\n");return;}// 将数据赋值给新节点new_node->data = new_data;// 将新节点插入链表头部new_node->next = *head_ref;*head_ref = new_node;
}// 删除指定数据的节点
void deleteNode(struct Node** head_ref, int key) {// 如果链表为空,直接返回if (*head_ref == NULL) {return;}// 如果要删除的节点是头节点if ((*head_ref)->data == key) {struct Node* temp = *head_ref;*head_ref = (*head_ref)->next;free(temp);return;}// 找到要删除节点的前一个节点struct Node* prev = *head_ref;while (prev->next != NULL && prev->next->data != key) {prev = prev->next;}// 如果找不到要删除的节点,则直接返回if (prev->next == NULL) {return;}// 将要删除的节点从链表中移除struct Node* temp = prev->next;prev->next = prev->next->next;free(temp);
}// 查找节点
struct Node* search(struct Node* head, int key) {struct Node* current = head;while (current != NULL) {if (current->data == key) {return current;}current = current->next;}return NULL;
}// 修改节点数据
void modifyNode(struct Node* head, int old_data, int new_data) {struct Node* node = search(head, old_data);if (node != NULL) {node->data = new_data;}
}// 打印链表
void printList(struct Node* node) {while (node != NULL) {printf("%d ", node->data);node = node->next;}printf("\n");
}int main() {// 初始化空链表struct Node* head = NULL;// 在链表末尾插入节点append(&head, 1);append(&head, 2);append(&head, 3);// 在链表头部插入节点prepend(&head, 0);// 删除节点deleteNode(&head, 2);// 修改节点数据modifyNode(head, 3, 4);// 打印链表printf("链表内容:");printList(head);return 0;
}

 

双链表:

双链表是一种链表数据结构,与单链表不同的是,每个节点除了包含指向下一个节点的指针外,还包含指向前一个节点的指针。这使得双链表可以从头到尾或者从尾到头遍历链表,而不需要像单链表那样从头开始遍历。以下是双链表的结构及其操作:

结构定义:

struct Node { int data; struct Node* next; struct Node* prev; };

基本操作:

  1. 节点插入

    • 头部插入:将新节点插入到链表头部。
    • 尾部插入:将新节点插入到链表尾部。
    • 任意位置插入:在指定位置后面插入新节点。
  2. 节点删除

    • 按值删除:删除具有特定值的节点。
    • 按位置删除:删除链表中指定位置的节点。
  3. 节点查找

    • 按值查找:查找具有特定值的节点。
    • 按位置查找:查找链表中指定位置的节点。
  4. 遍历

    • 正向遍历:从头到尾遍历链表。
    • 反向遍历:从尾到头遍历链表。
  5. 修改节点数据:修改链表中指定节点的数据。

双链表优点:

  • 双向遍历:可以从头到尾或者从尾到头遍历链表,而单链表只能从头到尾遍历。
  • 节点删除效率高:删除节点时,可以直接通过节点的前一个指针找到前一个节点,不需要从头开始遍历链表找到前一个节点。

双链表缺点:

  • 占用更多空间:每个节点需要额外存储指向前一个节点的指针,相比单链表会占用更多的内存空间。
  • 操作复杂度高:双链表的操作相对复杂,需要维护额外的指针。

实现双链表的关键在于正确维护节点之间的指针关系,在插入和删除操作时要注意更新前后节点的指针,以确保链表结构的正确性。

思路:

  1. 初始化链表:使用 initializeList() 函数来创建一个空链表,返回指向链表头部的指针。

  2. 在链表头部插入新节点:使用 insertAtBeginning() 函数在链表的开头插入新节点。这个函数会创建一个新节点,并将其链接到当前头部节点之前,然后更新头部指针。

  3. 在链表尾部插入新节点:使用 insertAtEnd() 函数在链表的末尾插入新节点。这个函数会遍历链表直到找到最后一个节点,然后在其后插入新节点。

  4. 查找节点:使用 searchNode() 函数根据给定的关键字在链表中查找节点。这个函数会遍历链表,找到匹配的节点并返回指向该节点的指针。

  5. 修改节点值:使用 modifyNode() 函数根据给定的旧数据值修改节点的值为新数据值。这个函数会先调用 searchNode() 找到要修改的节点,然后更新其数据值。

  6. 删除节点:使用 deleteNode() 函数删除指定的节点。这个函数会先判断链表是否为空或者要删除的节点是否为 NULL,然后分情况处理要删除的节点,最后释放其内存。

  7. 打印链表:使用 printList() 函数遍历链表并打印其中的节点数据值。

  8. 释放链表内存:使用 freeList() 函数释放链表所占用的内存,防止内存泄漏。

代码:

#include <stdio.h>
#include <stdlib.h>// 定义双链表节点结构体
struct Node {int data;struct Node* next;struct Node* prev;
};// 初始化双链表为空链表
struct Node* initializeList() {return NULL;
}// 在双链表头部插入新节点
struct Node* insertAtBeginning(struct Node* head, int data) {struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));newNode->data = data;newNode->prev = NULL;newNode->next = head;if (head != NULL) {head->prev = newNode;}return newNode;
}// 在双链表尾部插入新节点
struct Node* insertAtEnd(struct Node* head, int data) {struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));struct Node* temp = head;newNode->data = data;newNode->next = NULL;if (head == NULL) {newNode->prev = NULL;return newNode;}while (temp->next != NULL) {temp = temp->next;}temp->next = newNode;newNode->prev = temp;return head;
}// 查找节点
struct Node* searchNode(struct Node* head, int key) {struct Node* current = head;while (current != NULL) {if (current->data == key) {return current;}current = current->next;}return NULL; // 没有找到
}// 修改节点值
void modifyNode(struct Node* head, int oldData, int newData) {struct Node* nodeToModify = searchNode(head, oldData);if (nodeToModify != NULL) {nodeToModify->data = newData;} else {printf("Node with value %d not found\n", oldData);}
}// 删除指定节点
struct Node* deleteNode(struct Node* head, struct Node* delNode) {if (head == NULL || delNode == NULL) {return head;}if (head == delNode) {head = delNode->next;}if (delNode->next != NULL) {delNode->next->prev = delNode->prev;}if (delNode->prev != NULL) {delNode->prev->next = delNode->next;}free(delNode);return head;
}// 打印双链表
void printList(struct Node* head) {struct Node* temp = head;while (temp != NULL) {printf("%d ", temp->data);temp = temp->next;}printf("\n");
}// 释放双链表内存
void freeList(struct Node* head) {struct Node* temp;while (head != NULL) {temp = head;head = head->next;free(temp);}
}int main() {struct Node* head = initializeList();head = insertAtEnd(head, 1);head = insertAtEnd(head, 2);head = insertAtEnd(head, 3);printf("Original List: ");printList(head);modifyNode(head, 2, 5);printf("List after modifying node with value 2 to 5: ");printList(head);struct Node* searchResult = searchNode(head, 3);if (searchResult != NULL) {printf("Node with value 3 found\n");} else {printf("Node with value 3 not found\n");}head = deleteNode(head, head->next);printf("List after deleting second node: ");printList(head);freeList(head); // 释放链表内存return 0;
}

 

在这里我想再添加一些文件操作来实现设置密码和通讯录数据储存的功能,既能增加安全性也能提高实用性。

文件操作:

文件操作是指在计算机中对文件进行读取、写入和修改等操作的一系列行为。在 C 语言中,文件操作主要通过标准库中的 <stdio.h> 头文件中提供的函数来完成。

以下是一些常见的文件操作函数及其作用:

  1. fopen()

    • 作用:打开文件。
    • 原型:FILE *fopen(const char *filename, const char *mode);
    • 参数:
      • filename:要打开的文件的路径。
      • mode:打开文件的模式,包括读取("r")、写入("w")、追加("a")等。详细模式可参考文档。
    • 返回值:成功时返回文件指针,失败时返回 NULL
  2. fclose()

    • 作用:关闭文件。
    • 原型:int fclose(FILE *stream);
    • 参数:要关闭的文件指针。
    • 返回值:成功时返回 0,失败时返回 EOF
  3. fprintf()

    • 作用:向文件中写入格式化数据。
    • 原型:int fprintf(FILE *stream, const char *format, ...);
    • 参数:文件指针、格式化字符串及对应的数据。
    • 返回值:成功写入的字符数。
  4. fscanf()

    • 作用:从文件中读取格式化数据。
    • 原型:int fscanf(FILE *stream, const char *format, ...);
    • 参数:文件指针、格式化字符串及对应的变量地址。
    • 返回值:成功读取的数据项数。
  5. fgets()

    • 作用:从文件中读取一行数据。
    • 原型:char *fgets(char *str, int n, FILE *stream);
    • 参数:字符数组、最大读取字符数(包括空字符)、文件指针。
    • 返回值:成功时返回 str,失败时返回 NULL
  6. fputs()

    • 作用:向文件中写入字符串。
    • 原型:int fputs(const char *str, FILE *stream);
    • 参数:要写入的字符串、文件指针。
    • 返回值:成功时返回非负数,失败时返回 EOF
  7. fread()fwrite()

    • 作用:分别用于二进制文件的读取和写入。
    • 原型:
      • size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
      • size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
    • 参数:数据缓冲区指针、每个数据项的大小、数据项的数量、文件指针。
    • 返回值:成功读取或写入的数据项数量。
  8. feof()

    • 作用:检查文件是否到达文件末尾。
    • 原型:int feof(FILE *stream);
    • 参数:文件指针。
    • 返回值:如果到达文件末尾则返回非零值,否则返回 0。

设置密码:

要通过文件操作来实现设置密码的功能,可以将密码存储在一个文件中,并提供相应的功能来读取和更新密码。以下是一个简单的示例代码,演示了如何实现这一功能:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define MAX_LENGTH 50// 读取密码文件中的密码
char* readPasswordFromFile(const char* filename) {FILE* file = fopen(filename, "r");if (file == NULL) {perror("Error opening file");exit(EXIT_FAILURE);}char* password = (char*)malloc(MAX_LENGTH * sizeof(char));if (fgets(password, MAX_LENGTH, file) == NULL) {perror("Error reading password");exit(EXIT_FAILURE);}fclose(file);return password;
}// 更新密码文件中的密码
void updatePasswordInFile(const char* filename, const char* newPassword) {FILE* file = fopen(filename, "w");if (file == NULL) {perror("Error opening file");exit(EXIT_FAILURE);}fprintf(file, "%s", newPassword);fclose(file);
}int main() {const char* filename = "password.txt";// 读取密码char* password = readPasswordFromFile(filename);printf("Current Password: %s", password);// 设置新密码char newPassword[MAX_LENGTH];printf("Enter new password: ");fgets(newPassword, MAX_LENGTH, stdin);newPassword[strcspn(newPassword, "\n")] = '\0'; // 移除换行符// 更新密码文件中的密码updatePasswordInFile(filename, newPassword);printf("Password updated successfully.\n");// 释放内存free(password);return 0;
}

数据储存:

要实现内容存储的功能,你可以通过文件操作来创建、读取、写入和修改文件中的内容。以下是一个简单的示例,演示了如何使用文件操作来实现内容存储的功能:

#include <stdio.h>#define MAX_LEN 100 // 假设内容的最大长度为 100// 函数声明
void saveContent(const char *filename, const char *content);
void readContent(const char *filename);int main() {const char *filename = "content.txt"; // 文件名const char *content = "这是要保存的内容。"; // 要保存的内容// 保存内容到文件saveContent(filename, content);// 从文件中读取内容并显示readContent(filename);return 0;
}// 保存内容到文件
void saveContent(const char *filename, const char *content) {FILE *file = fopen(filename, "w"); // 以写入模式打开文件if (file == NULL) {printf("无法打开文件 %s\n", filename);return;}// 写入内容到文件fputs(content, file);// 关闭文件fclose(file);printf("内容已保存到文件 %s\n", filename);
}// 从文件中读取内容并显示
void readContent(const char *filename) {FILE *file = fopen(filename, "r"); // 以读取模式打开文件if (file == NULL) {printf("无法打开文件 %s\n", filename);return;}char buffer[MAX_LEN]; // 缓冲区// 从文件中读取内容,并逐行显示while (fgets(buffer, MAX_LEN, file) != NULL) {printf("%s", buffer);}// 关闭文件fclose(file);
}

通讯录的代码实现:

接下是我以顺序表为例写出的通讯录代码:

头文件1:

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include"con.h"
#include<string.h>typedef pn seq;typedef struct sequce
{seq* arr;int size;int cap;
}sl;void Sequce_set(sl* s);//初始化顺序表
void Sequce_front(sl* s, seq x);//前插
void Print(sl s);//打印顺序表
void En(sl* s);//动态内存实现
void Sequce_back(sl* s, seq x);//尾插
void Sequce_popback(sl* s);//尾删
void Sequce_popfront(sl* s);//首删
void N_ull(sl* s);//判断空指针
void Zd_set(sl* s, int n, seq x);//指定位置插入
void Zd_del(sl* s, int n);//指定位置删除
void des(sl* s);//内存销毁

头文件2:

#pragma once#define NAME_MAX 20
#define SAX_MAX 10
#define PH_MAX 20
#define AD_MAX 20
#define PASSWORD_MAX 20//姓名 性别 年龄 电话 地址
typedef struct contact {char name[NAME_MAX];char sex[SAX_MAX];int age;char ph[PH_MAX];char ad[AD_MAX];
}pn;typedef struct sequce Contact;
void Con_set(Contact* con);//初始化
void Con_des(Contact* con);//销毁
void Con_fset(Contact* con, pn x);//前插
void Con_bset(Contact* con, pn x);//尾插
//void Con_zset(Contact* con, seq x, int n);//指定位置插入
//void Con_fdel(Contact* con);//前删
//void Con_bdel(Contact* con);//尾删
void Con_zdel(Contact* con);//删除数据
void Con_zchange(Contact* con);//修改数据
int Con_namefind(Contact con);//按名字查找通讯人
void Print_con(Contact con);//打印通讯录
void Con_xset(Contact* con);//添加数据
void Print_zd(Contact con, int n);//打印指定通讯人信息
void Sort_age(Contact* con);//根据年龄大小排序

源文件1:

#define _CRT_SECURE_NO_WARNINGS 1#include "seq.h"//初始化顺序表
void Sequce_set(sl* s)
{s->arr = (seq*)malloc(sizeof(seq));s->size = 0;s->cap = 1;
}//表首插入
void Sequce_front(sl* s, seq x)
{N_ull(s);En(s);for (int i = s->size; i > 0; i--){s->arr[i] = s->arr[i - 1];}s->arr[0] = x;s->size++;
}//表尾插入
void Sequce_back(sl* s, seq x)
{N_ull(s);En(s);s->arr[s->size] = x;s->size++;
}//表首删除
void Sequce_popfront(sl* s)
{N_ull(s);if (s->size == 0){return;}for (int i = 1; i < s->size; i++){s->arr[i - 1] = s->arr[i];}s->size--;
}//表尾删除
void Sequce_popback(sl* s)
{N_ull(s);if (s->size == 0){return;}s->size--;
}//指定位置添加
void Zd_set(sl* s, int n, seq x)
{N_ull(s);En(s);if (s->size < n){return;}for (int i = s->size; i > n; i--){s->arr[i] = s->arr[i - 1];}s->arr[n] = x;s->size++;
}//指定位置删除
void Zd_del(sl* s, int n)
{if (s->size == 0){return;}N_ull(s);for (int i = n; i < s->size - 1; i++){s->arr[i] = s->arr[i + 1];}s->size--;
}//打印顺序表
//void Print(sl s)
//{
//	for (int i = 0; i < s.size; i++)
//	{
//		printf("%d ", s.arr[i]);
//	}
//	printf("\n");
//}//判断容量
void En(sl* s)
{N_ull(s);while (s->cap <= s->size){int newcap = 2 * s->cap;seq* ps = (seq*)realloc(s->arr, sizeof(seq) * newcap);if (!ps){exit(1);}s->arr = ps;s->cap = newcap;}
}//判断空指针
void N_ull(sl* s)
{if (!s){exit(1);}
}//顺序表销毁
void des(sl* s)
{free(s->arr);s->arr = NULL;
}

源文件2:

#define _CRT_SECURE_NO_WARNINGS 1
#include"seq.h"//初始化数据
void Con_set(Contact* con)
{Sequce_set(con);
}//数据销毁
void Con_des(Contact* con)
{des(con);
}void Con_fset(Contact* con, seq x)//前插
{Sequce_front(con, x);
}//添加数据
void Con_xset(Contact* con)
{pn l;printf("请输入通讯人姓名:\n");scanf("%s", l.name);printf("请输入通讯人性别:\n");scanf("%s", l.sex);printf("请输入通讯人年龄:\n");scanf("%d", &l.age);printf("请输入通讯人电话:\n");scanf("%s", l.ph);printf("请输入通讯人地址:\n");scanf("%s", l.ad);Sequce_back(con, l);Print_con(*con);
}//按名字删除数据
void Con_zdel(Contact* con)
{//char name[NAME_MAX];//scanf("%s", name);int k = Con_namefind(*con);if (k >= 0){Zd_del(con, k);printf("删除成功!\n");Print_con(*con);}else{printf("未找到你要删除的通讯人!\n");}
}//根据名字查找通讯人
int Con_namefind(Contact con)
{char name[NAME_MAX];printf("请输入你要操作的通讯人姓名:");scanf("%s", name);for (int i = 0; i < con.size; i++){if (!strcmp(con.arr[i].name, name)){printf("找到了!\n");return i;}}return -1;
}void Print_con(Contact con)//打印通讯录
{printf("姓名:");for (int i = 0; i < con.size; i++){printf("%-20s", con.arr[i].name);}printf("\n");printf("性别:");for (int i = 0; i < con.size; i++){printf("%-20s", con.arr[i].sex);}printf("\n");printf("年龄:");for (int i = 0; i < con.size; i++){printf("%-20d", con.arr[i].age);}printf("\n");printf("电话:");for (int i = 0; i < con.size; i++){printf("%-20s", con.arr[i].ph);}printf("\n");printf("地址:");for (int i = 0; i < con.size; i++){printf("%-20s", con.arr[i].ad);}printf("\n");
}//打印查找到的通讯人
void Print_zd(Contact con, int n)
{if (n >= 0){printf("姓名:");printf("%s\n", con.arr[n].name);printf("性别:");printf("%s\n", con.arr[n].sex);printf("年龄:");printf("%d\n", con.arr[n].age);printf("电话:");printf("%s\n", con.arr[n].ph);printf("地址:");printf("%s\n", con.arr[n].ad);}else{printf("没有找到该通讯人!\n");}
}void Sort_age(Contact* con)//根据年龄大小排序
{printf("****************1.升序****************\n");printf("****************2.降序****************\n");int select;scanf("%d", &select);if (select == 1){for (int i = 0; i < con->size; i++){for (int j = 0; j < con->size - 1 - i; j++){if (con->arr[j].age > con->arr[j + 1].age){seq t = con->arr[j];con->arr[j] = con->arr[j + 1];con->arr[j + 1] = t;}}}printf("******************升序结果:*******************\n");}else if (select == 2){for (int i = 0; i < con->size; i++){for (int j = 0; j < con->size - 1 - i; j++){if (con->arr[j].age < con->arr[j + 1].age){seq t = con->arr[j];con->arr[j] = con->arr[j + 1];con->arr[j + 1] = t;}}}printf("******************降序结果:*******************\n");}Print_con(*con);//打印通讯录
}//修改通讯人信息
void Con_zchange(Contact* con)
{int k = Con_namefind(*con);pn l;if (k >= 0){printf("请输入通讯人姓名:\n");scanf("%s", l.name);printf("请输入通讯人性别:\n");scanf("%s", l.sex);printf("请输入通讯人年龄:\n");scanf("%d", &l.age);printf("请输入通讯人电话:\n");scanf("%s", l.ph);printf("请输入通讯人地址:\n");scanf("%s", l.ad);con->arr[k] = l;printf("修改成功!\n");Print_con(*con);}else{printf("未找到你查找的通讯人!\n");}
}

源文件3:

#define _CRT_SECURE_NO_WARNINGS 1
#include"seq.h"void Memu1()
{printf("********************通讯录********************\n");printf("*****************1.进入通讯录*****************\n");printf("*****************2.退出通讯录*****************\n");printf("**********************************************\n");
}void Memu2()
{printf("********************通讯录功能列表********************\n");printf("********************1.添加通讯人**********************\n");printf("********************2.删除通讯人**********************\n");printf("********************3.查找通讯人**********************\n");printf("********************4.根据通讯人年龄排序**************\n");printf("********************5.修改通讯人信息******************\n");printf("********************通讯录功能列表********************\n");
}void Memu3()
{printf("请选择:");
}int main()
{char word[PASSWORD_MAX];FILE* ol = fopen("password.txt", "r");if (ol == NULL){perror("fopen");return 1;}int u;if (fscanf(ol, "password: %s\n", word) != 1){u = 0;}else{printf("请输入密码:\n");char guess[PASSWORD_MAX];scanf("%s", guess);u = strcmp(guess, word);}fclose(ol);if (!u){Contact con;Con_set(&con);FILE* p = fopen("con.txt", "r");if (p == NULL){perror("fopen");return 1;}// 使用循环读取文件中的每条记录int count = 0;fscanf(p, "Size: %d\nCap: %d\n", &con.size, &con.cap);con.cap = 1;En(&con);if (con.size != 0){while (fscanf(p, "Name: %s\nSex: %s\nAge: %d\nph: %s\nad: %s\n", con.arr[count].name, con.arr[count].sex, \& con.arr[count].age, con.arr[count].ph, con.arr[count].ad) == 5) {count++;if (count >= con.size) {break; // 如果达到数组容量上限,则停止读取}}}fclose(p);Print_con(con);//Con_set(&con);do {//system("cls");Memu1();int a = 0;Memu3();scanf("%d", &a);if (a == 1){Memu2();Memu3();scanf("%d", &a);switch (a) {case 1:printf("********************添加通讯人**********************\n");Con_xset(&con);break;case 2:printf("********************删除通讯人**********************\n");Con_zdel(&con);break;case 3:printf("********************查找通讯人**********************\n");Print_zd(con, Con_namefind(con));break;case 4:printf("********************根据通讯人年龄排序**************\n");Sort_age(&con);//根据年龄大小排序break;case 5:printf("********************修改通讯人信息**********************\n");Con_zchange(&con);break;default:printf("输入错误,请重新选择:\n");}}else if (a == 2){break;}else{printf("输入错误,请重新选择!\n");}} while (1);FILE* f = fopen("con.txt", "w");if (f == NULL){perror("fopen");return 1;}fprintf(f, "Size: %d\nCap:  %d\n", con.size, con.cap);for (int i = 0; i < con.size; i++){fprintf(f, "Name: %s\nSex: %s\nAge: %d\nph: %s\nad: %s\n", con.arr[i].name, con.arr[i].sex, \con.arr[i].age, con.arr[i].ph, con.arr[i].ad);}Con_des(&con);//销毁fclose(f);printf("重新设置密码请按1,退出可按任意数字!\n");int df;scanf("%d", &df);if (df == 1){mm:printf("新密码:");FILE* gh = fopen("password.txt", "w");if (gh == NULL){perror("fopen");return 1;}char newword[PASSWORD_MAX];char newword2[PASSWORD_MAX];scanf("%s", newword);printf("请再输入一次新密码:");scanf("%s", newword2);if (strcmp(newword, newword2)){printf("第二次输入错误, 请重新设置!\n");goto mm;}fprintf(gh, "password: %s\n", newword);fclose(gh);}#if 0Con_set(&con);//添加数据:Con_xset(&con);Con_xset(&con);Con_xset(&con);//按名字删除数据Con_zdel(&con);//按姓名查找通讯人:Print_zd(con, Con_namefind(con));Con_des(&con);//销毁
#endif}else{printf("密码错误,直接退出程序!\n");}return 0;
}

 

运行演示:

第一次进入运行

 

 

 第二次进入程序:

从中可以看出数据能够成功地储存在文件中。

 

 如果感兴趣可以自己用单链表和双链表都实现一遍。

完。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

anaconda创建了虚拟python环境,且安装了pytorch,但是pycharm中import torch运行时报错

报错如下&#xff1a; C:\Users\tashi\.conda\envs\test1\python.exe D:\project\python\transformer\main.py C:\Users\tashi\.conda\envs\test1\lib\site-packages\numpy\__init__.py:127: UserWarning: mkl-service package failed to import, therefore Intel(R) MKL init…

【计算机网络】ip子网划分--超详细例题解析

Hello!这一篇主要是计算机网络中的ip地址子网划分的例题&#xff0c;这里例举了四个题型。保证即便从0也可以掌握&#xff01;(前面是一些预备知识&#xff0c;不熟悉的小伙伴一定要看下学习下哦&#xff5e;) 这也是博主的学习过程&#xff0c;做题中仅仅我的理解哦。若文章中…

langchain 文本向量化存储,并检索相似 topK

目录 chroma 检索 faiss 检索 检索器 相似性 最大相关性mmr 相似数阈值 txt 有多行&#xff0c;我的这份数据有 67 行&#xff0c;样例如下&#xff1a; 字段1\t值1\n 字段2\t值2\n ... chroma 检索 pip install langchain-chroma 在本地下载了 embedding 模型&…

FastAPI - uvicorn设置 logger 日志格式

怎么将日志打印到文件 在main.py加入log_config“./uvicorn_config.json” import uvicornif __name__ "__main__":uvicorn.run("app:app", host"0.0.0.0", port8000, log_config"./uvicorn_config.json")uvicorn_config.json {&qu…

吴恩达深度学习 (week3,4)

文章目录 一、神经网络概述二、神经网络的表示三、神经网络的输出四、多个例子的向量化五、向量化实现的解释六、深度学习激活函数七、激活函数导数八、神经网络的梯度下降法九、深度学习随机初始化十、上述学习总结1、第一题2、第二题3、第三题4、第四题5、第五题6、第六题7、…

langchain LCEL,prompt模块,outputparse输出模块

目录 基本代码 prompt模块 prompt模版控制长度 outputparse格式化输出 并行使用调用链 LangChain表达式语言&#xff0c;或者LCEL&#xff0c;是一种声明式的方式&#xff0c;可以轻松地将链条组合在一起 langchian 可以使用 通义千问&#xff0c;我们用通义千问&#x…

STM32H7的MPU学习和应用示例

STM32H7的MPU学习记录 什么是MPU&#xff1f;MPU的三种内存类型内存映射MPU保护区域以及优先级 MPU的寄存器XN位AP位TEX、C、B、S位SRD 位SIZE 位CTRL 寄存器的各个位 示例总结 什么是MPU&#xff1f; MPU&#xff08;Memory Protection Unit&#xff0c;内存保护单元&#xf…

Pytorch Windows EOFError: Ran out of input when num_workers>0

关于深度学习的一些学习框架,我使用过pytorch,caffe,caffe2,openchatkit,oneflow等,最近我将长达几十万字的报错手册重新进行了整理,制作出一个新的专栏,主要记录这几种常见的开发框架在安装和使用过程中常见的报错,以及我是如何解决掉的,以此来帮助更多的深度学习开…

GB/T 28181标准中的错误码,国标28181中可能出现的SIP协议相关的错误码及其含义

目录 一、GB/T 28181标准介绍 &#xff08;一&#xff09;概述 &#xff08;二&#xff09;关键内容和特点 1. 系统架构&#xff1a; 2. 设备接入&#xff1a; 3. 网络通信&#xff1a; 4. 业务功能&#xff1a; 5. 安全保护&#xff1a; 6. 平台管理&#xff1a; &a…

二维数组---刷题

一维数组不想更了&#xff0c;弄点二维数组&#xff01; 1.新矩阵 思路 题目简单&#xff0c;6*636&#xff0c;可以得知有36个元素。数组就定义成a[7][7]&#xff0c;难点在与如何找出对角线上的元素。可以画图分析&#xff1a; 通过观察不难发现&#xff0c;元素1&#xff…

spring boot admin搭建,监控springboot程序运行状况

新建一个spring boot web项目&#xff0c;添加以下依赖 <dependency><groupId>de.codecentric</groupId><artifactId>spring-boot-admin-starter-server</artifactId><version>2.3.0</version></dependency> <dependency&…

uni-app实现分页--(2)分页加载,首页下拉触底加载更多

业务逻辑如下&#xff1a; api函数升级 定义分页参数类型 组件调用api传参

[论文翻译]GLU Variants Improve Transformer

引言 今天带来一篇短小精悍的论文GLU Variants Improve Transformer笔记&#xff0c;作者提出了GLU1的一种变体。 GLU(Gated Linear Units,门控线性单元)由两个线性投影的逐元素乘积组成&#xff0c;其中一个首先经过sigmoid函数。GLU的变体是可能生效的&#xff0c;可以使用…

c语言多功能计算软件170

定制魏&#xff1a;QTWZPW&#xff0c;获取更多源码等 目录 题目 要求 主要代码片段 题目 设计一个计算器软件&#xff0c;具备如下功能提示界面。 要求 设计出界面&#xff0c;注意界面名称最后为自己的姓名&#xff1b;&#xff08;20分&#xff09;能够实现加、减、乘、…

Java 面试宝典:Redis 的线程模型是怎么样的?

大家好&#xff0c;我是大明哥&#xff0c;一个专注「死磕 Java」系列创作的硬核程序员。 本文已收录到我的技术网站&#xff1a;https://www.skjava.com。有全网最优质的系列文章、Java 全栈技术文档以及大厂完整面经 Redis 的线程模型其实是分两块的&#xff1a; Redis 6.0 …

(学习日记)2024.04.15:UCOSIII第四十三节:任务消息队列

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

文件上传【2】--靶场通关

1.前端禁用js绕过 上传文件&#xff0c;进行抓包&#xff0c;没有抓到&#xff0c;说明这里的验证是前端js验证跳出的弹窗 禁用js后&#xff0c;php文件上传成功。 2.文件上传.htaccess 上传png木马后连接不上 代码中存在.htaccess&#xff0c;判断此时应该就是需要用到.htac…

1111111111

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

21 标准错误

标准输出重定向关闭无数据 下面的代码&#xff1a; #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main() {close(1);i…

超级详细的JDBC和数据库连接池讲解

文章目录 JDBC简介概念本质好处 JDBC快速入门JDBC中API详解DriverManager驱动管理类作用注册驱动获取连接 Connection数据库连接对象作用获取执行SQL的对象事务管理 Statement作用执行SQL语句 ResultSet原理使用步骤 PreparedStatementSQL注入获取对象操作步骤 原理好处 JDBC工…