在C语言中,数据结构和算法是计算机科学的基础知识。我们将详细介绍链表、树、图、哈希表、排序和搜索算法的理论,并用C语言实现这些数据结构和算法。每个数据结构和算法都有其特定的应用场景和实现方法。
链表是一种线性数据结构,其中每个元素都是一个独立的节点,这些节点通过指针连接在一起。链表的每个节点包含两个部分:数据部分和指针部分(指向下一个节点)。
以下是单链表的C语言实现,包括基本的操作:插入、删除和遍历。
#include <stdio.h>
#include <stdlib.h>
// 定义节点结构
struct Node {
int data;
struct Node* next;
};
// 插入新节点到链表头部
void insertAtHead(struct Node** head_ref, int new_data) {
// 分配新节点
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
// 将数据放入节点
new_node->data = new_data;
// 将新节点的 next 指针指向当前头节点
new_node->next = (*head_ref);
// 将头指针指向新节点
(*head_ref) = new_node;
}
// 删除指定值的节点
void deleteNode(struct Node** head_ref, int key) {
struct Node* temp = *head_ref, *prev;
// 检查头节点是否为要删除的节点
if (temp != NULL && temp->data == key) {
*head_ref = temp->next; // 改变头节点
free(temp); // 释放内存
return;
}
// 搜索要删除的节点
while (temp != NULL && temp->data != key) {
prev = temp;
temp = temp->next;
}
// 如果未找到该节点
if (temp == NULL) return;
// 解除链表中的节点
prev->next = temp->next;
free(temp); // 释放内存
}
// 遍历链表
void printList(struct Node* node) {
while (node != NULL) {
printf("%d -> ", node->data);
node = node->next;
}
printf("NULL\n");
}
int main() {
struct Node* head = NULL;
insertAtHead(&head, 1);
insertAtHead(&head, 2);
insertAtHead(&head, 3);
insertAtHead(&head, 4);
printf("初始链表: ");
printList(head);
deleteNode(&head, 3);
printf("删除 3 后的链表: ");
printList(head);
return 0;
}
双链表允许双向遍历,每个节点包含前驱和后继指针。
#include <stdio.h>
#include <stdlib.h>
// 定义节点结构
struct Node {
int data;
struct Node* next;
struct Node* prev;
};
// 插入新节点到链表头部
void insertAtHead(struct Node** head_ref, int new_data) {
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
new_node->data = new_data;
new_node->next = (*head_ref);
new_node->prev = NULL;
if ((*head_ref) != NULL)
(*head_ref)->prev = new_node;
(*head_ref) = new_node;
}
// 删除指定值的节点
void deleteNode(struct Node** head_ref, struct Node* del) {
if (*head_ref == NULL || del == NULL)
return;
if (*head_ref == del)
*head_ref = del->next;
if (del->next != NULL)
del->next->prev = del->prev;
if (del->prev != NULL)
del->prev->next = del->next;
free(del);
}
// 遍历链表
void printList(struct Node* node) {
struct Node* last;
printf("正向遍历:\n");
while (node != NULL) {
printf("%d -> ", node->data);
last = node;
node = node->next;
}
printf("NULL\n");
printf("反向遍历:\n");
while (last != NULL) {
printf("%d -> ", last->data);
last = last->prev;
}
printf("NULL\n");
}
int main() {
struct Node* head = NULL;
insertAtHead(&head, 1);
insertAtHead(&head, 2);
insertAtHead(&head, 3);
insertAtHead(&head, 4);
printf("初始双向链表: ");
printList(head);
deleteNode(&head, head->next); // 删除第二个节点
printf("删除第二个节点后的链表: ");
printList(head);
return 0;
}
树是一种层次化的数据结构,由节点组成,其中每个节点都有一个父节点和零个或多个子节点。树的根节点没有父节点,树中的其他节点都有且仅有一个父节点。
以下是二叉搜索树的C语言实现,包括插入、搜索和删除操作。
#include <stdio.h>
#include <stdlib.h>
// 定义节点结构
struct TreeNode {
int data;
struct TreeNode* left;
struct TreeNode* right;
};
// 创建新节点
struct TreeNode* createNode(int data) {
struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
newNode->data = data;
newNode->left = newNode->right = NULL;
return newNode;
}
// 插入新节点
struct TreeNode* insert(struct TreeNode* node, int data) {
if (node == NULL)
return createNode(data);
if (data < node->data)
node->left = insert(node->left, data);
else if (data > node->data)
node->right = insert(node->right, data);
return node;
}
// 搜索节点
struct TreeNode* search(struct TreeNode* root, int data) {
if (root == NULL || root->data == data)
return root;
if (data < root->data)
return search(root->left, data);
return search(root->right, data);
}
// 找到最小值节点
struct TreeNode* findMin(struct TreeNode* node) {
struct TreeNode* current = node;
while (current && current->left != NULL)
current = current->left;
return current;
}
// 删除节点
struct TreeNode* deleteNode(struct TreeNode* root, int data) {
if (root == NULL)
return root;
if (data < root->data)
root->left = deleteNode(root->left, data);
else if (data > root->data)
root->right = deleteNode(root->right, data);
else {
if (root->left == NULL) {
struct TreeNode* temp = root->right;
free(root);
return temp;
} else if (root->right == NULL) {
struct TreeNode* temp = root->left;
free(root);
return temp;
}
struct TreeNode* temp = findMin(root->right);
root->data = temp->data;
root->right = deleteNode(root->right, temp->data);
}
return root;
}
// 中序遍历
void inorderTraversal(struct TreeNode* root) {
if (root != NULL) {
inorderTraversal(root->left);
printf("%d ", root->data);
inorderTraversal(root->right);
}
}
int main() {
struct TreeNode* root = NULL;
root = insert(root, 5);
insert(root, 3);
insert(root, 7);
insert(root, 2);
insert(root, 4);
insert(root, 6);
insert(root, 8);
printf("中序遍历: ");
inorderTraversal(root); // 输出: 2 3 4 5 6 7 8
printf("\n");
printf("搜索 4: %p\n", search(root, 4));
// 输出: 非NULL
printf("搜索 9: %p\n", search(root, 9)); // 输出: NULL
root = deleteNode(root, 4);
printf("删除 4 后中序遍历: ");
inorderTraversal(root); // 输出: 2 3 5 6 7 8
printf("\n");
return 0;
}
图是一种非线性数据结构,由顶点(节点)和边组成。图可以是有向的或无向的,也可以是加权的或无权的。
图可以使用邻接矩阵或邻接表表示。
邻接矩阵是一个二维数组,其中行列表示顶点,数组中的值表示边的存在(及其权重)。
#include <stdio.h>
#include <stdlib.h>
#define MAX_VERTICES 100
// 邻接矩阵表示图
struct Graph {
int numVertices;
int adjMatrix[MAX_VERTICES][MAX_VERTICES];
};
// 创建图
struct Graph* createGraph(int vertices) {
struct Graph* graph = (struct Graph*)malloc(sizeof(struct Graph));
graph->numVertices = vertices;
for (int i = 0; i < vertices; i++)
for (int j = 0; j < vertices; j++)
graph->adjMatrix[i][j] = 0;
return graph;
}
// 添加边
void addEdge(struct Graph* graph, int src, int dest) {
graph->adjMatrix[src][dest] = 1;
graph->adjMatrix[dest][src] = 1; // 无向图
}
// 打印图
void printGraph(struct Graph* graph) {
for (int i = 0; i < graph->numVertices; i++) {
printf("顶点 %d: ", i);
for (int j = 0; j < graph->numVertices; j++) {
if (graph->adjMatrix[i][j])
printf("%d ", j);
}
printf("\n");
}
}
int main() {
struct Graph* graph = createGraph(5);
addEdge(graph, 0, 1);
addEdge(graph, 0, 4);
addEdge(graph, 1, 2);
addEdge(graph, 1, 3);
addEdge(graph, 1, 4);
addEdge(graph, 2, 3);
addEdge(graph, 3, 4);
printGraph(graph);
return 0;
}
邻接表是一种更为高效的图表示方法,适用于稀疏图。每个顶点有一个链表,用于存储与该顶点相连的所有其他顶点。
#include <stdio.h>
#include <stdlib.h>
// 链表节点
struct Node {
int vertex;
struct Node* next;
};
// 图结构
struct Graph {
int numVertices;
struct Node** adjLists;
};
// 创建节点
struct Node* createNode(int v) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->vertex = v;
newNode->next = NULL;
return newNode;
}
// 创建图
struct Graph* createGraph(int vertices) {
struct Graph* graph = (struct Graph*)malloc(sizeof(struct Graph));
graph->numVertices = vertices;
graph->adjLists = (struct Node**)malloc(vertices * sizeof(struct Node*));
for (int i = 0; i < vertices; i++)
graph->adjLists[i] = NULL;
return graph;
}
// 添加边
void addEdge(struct Graph* graph, int src, int dest) {
struct Node* newNode = createNode(dest);
newNode->next = graph->adjLists[src];
graph->adjLists[src] = newNode;
newNode = createNode(src);
newNode->next = graph->adjLists[dest];
graph->adjLists[dest] = newNode;
}
// 打印图
void printGraph(struct Graph* graph) {
for (int v = 0; v < graph->numVertices; v++) {
struct Node* temp = graph->adjLists[v];
printf("\n 顶点 %d\n: ", v);
while (temp) {
printf("%d -> ", temp->vertex);
temp = temp->next;
}
printf("\n");
}
}
int main() {
struct Graph* graph = createGraph(5);
addEdge(graph, 0, 1);
addEdge(graph, 0, 4);
addEdge(graph, 1, 2);
addEdge(graph, 1, 3);
addEdge(graph, 1, 4);
addEdge(graph, 2, 3);
addEdge(graph, 3, 4);
printGraph(graph);
return 0;
}
哈希表是一种用于存储键值对的数据结构,通过哈希函数将键映射到值所在的位置。哈希表支持高效的插入、删除和查找操作。
以下是一个简单的哈希表实现,使用链地址法解决冲突。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TABLE_SIZE 10
// 链表节点
struct Node {
int key;
int value;
struct Node* next;
};
// 哈希表
struct HashTable {
struct Node** table;
};
// 创建节点
struct Node* createNode(int key, int value) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->key = key;
newNode->value = value;
newNode->next = NULL;
return newNode;
}
// 创建哈希表
struct HashTable* createTable() {
struct HashTable* hashTable = (struct HashTable*)malloc(sizeof(struct HashTable));
hashTable->table = (struct Node**)malloc(TABLE_SIZE * sizeof(struct Node*));
for (int i = 0; i < TABLE_SIZE; i++)
hashTable->table[i] = NULL;
return hashTable;
}
// 哈希函数
int hashFunction(int key) {
return key % TABLE_SIZE;
}
// 插入键值对
void insert(struct HashTable* hashTable, int key, int value) {
int hashIndex = hashFunction(key);
struct Node* newNode = createNode(key, value);
if (hashTable->table[hashIndex] == NULL) {
hashTable->table[hashIndex] = newNode;
} else {
struct Node* temp = hashTable->table[hashIndex];
while (temp->next != NULL) {
temp = temp->next;
}
temp->next = newNode;
}
}
// 搜索键
int search(struct HashTable* hashTable, int key) {
int hashIndex = hashFunction(key);
struct Node* temp = hashTable->table[hashIndex];
while (temp != NULL) {
if (temp->key == key)
return temp->value;
temp = temp->next;
}
return -1;
}
// 删除键
void delete(struct HashTable* hashTable, int key) {
int hashIndex = hashFunction(key);
struct Node* temp = hashTable->table[hashIndex];
struct Node* prev = NULL;
if (temp != NULL && temp->key == key) {
hashTable->table[hashIndex] = temp->next;
free(temp);
return;
}
while (temp != NULL && temp->key != key) {
prev = temp;
temp = temp->next;
}
if (temp == NULL)
return;
prev->next = temp->next;
free(temp);
}
int main() {
struct HashTable* hashTable = createTable();
insert(hashTable, 1, 10);
insert(hashTable, 2, 20);
insert(hashTable, 3, 30);
insert(hashTable, 4, 40);
printf("搜索键 2: %d\n", search(hashTable, 2)); // 输出: 20
delete(hashTable, 2);
printf("删除键 2 后搜索键 2: %d\n", search(hashTable, 2)); // 输出: -1
return 0;
}
排序算法用于将元素按照特定顺序进行排列。常见的排序算法包括冒泡排序、选择排序、插入排序、快速排序和归并排序。
冒泡排序是一种简单的排序算法,逐步比较相邻元素,并根据需要交换元素。
#include <stdio.h>
// 冒泡排序
void bubbleSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
for
(int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
// 打印数组
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++)
printf("%d ", arr[i]);
printf("\n");
}
int main() {
int arr[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(arr) / sizeof(arr[0]);
bubbleSort(arr, n);
printf("排序后的数组: ");
printArray(arr, n);
return 0;
}
快速排序是一种分治排序算法,通过选择基准元素将数组划分为两个子数组,然后递归地排序子数组。
#include <stdio.h>
// 交换两个元素
void swap(int* a, int* b) {
int t = *a;
*a = *b;
*b = t;
}
// 划分数组
int partition(int arr[], int low, int high) {
int pivot = arr[high];
int i = (low - 1);
for (int j = low; j <= high - 1; j++) {
if (arr[j] < pivot) {
i++;
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i + 1], &arr[high]);
return (i + 1);
}
// 快速排序
void quickSort(int arr[], int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
// 打印数组
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++)
printf("%d ", arr[i]);
printf("\n");
}
int main() {
int arr[] = {10, 7, 8, 9, 1, 5};
int n = sizeof(arr) / sizeof(arr[0]);
quickSort(arr, 0, n - 1);
printf("排序后的数组: ");
printArray(arr, n);
return 0;
}
搜索算法用于在数据结构中查找特定元素。常见的搜索算法包括线性搜索和二分搜索。
线性搜索是最简单的搜索算法,从数组的起始位置开始逐个比较元素。
#include <stdio.h>
// 线性搜索
int linearSearch(int arr[], int n, int x) {
for (int i = 0; i < n; i++) {
if (arr[i] == x)
return i;
}
return -1;
}
int main() {
int arr[] = {2, 3, 4, 10, 40};
int n = sizeof(arr) / sizeof(arr[0]);
int x = 10;
int result = linearSearch(arr, n, x);
if (result == -1)
printf("元素不在数组中\n");
else
printf("元素在数组中的位置: %d\n", result);
return 0;
}
二分搜索是一种高效的搜索算法,适用于有序数组。它通过不断将搜索范围减半来查找元素。
#include <stdio.h>
// 二分搜索
int binarySearch(int arr[], int l, int r, int x) {
while (l <= r) {
int m = l + (r - l) / 2;
if (arr[m] == x)
return m;
if (arr[m] < x)
l = m + 1;
else
r = m - 1;
}
return -1;
}
int main() {
int arr[] = {2, 3, 4, 10, 40};
int n = sizeof(arr) / sizeof(arr[0]);
int x = 10;
int result = binarySearch(arr, 0, n - 1, x);
if (result == -1)
printf("元素不在数组中\n");
else
printf("元素在数组中的位置: %d\n", result);
return 0;
}
这些代码和概念提供了C语言中数据结构和算法的基本实现。对于每种数据结构和算法,理解其应用场景和特性是关键。希望这些示例能帮助你更好地理解数据结构和算法的实现。如果你还有其他问题或需要更详细的解释,随时告诉我!