UNIX编程中的常用算法与函数详解
在UNIX编程中,有许多常用的算法和函数可以帮助我们更高效地处理数据和完成各种任务。下面将详细介绍一些常见的算法和函数,包括线性搜索、二分搜索、哈希表、二叉树、队列操作、排序、环境变量处理以及密码加密等。
1. 线性搜索示例
线性搜索是一种简单的搜索方法,通过逐个比较元素来查找目标。以下是一个简单的线性搜索示例:
int compare(const void *a, const void *b)
{
return(strcmp((char *) a, (char *) b));
}
在这个示例中,我们可以输入10个字符串,然后进行搜索操作。例如:
Enter 10 strings, not all unique.
1> abcdef
2> ghijkl
3> mnopqr
4> stuvwx
5> yz
6> abcdef
7> ghijkl
8> mnopqr
9> stuvwx
10> yz
Contents of the table:
abcdef
ghijkl
mnopqr
stuvwx
yz
Search for: abc
String not found.
Search for: abcdef
Found at location 1.
Search for: ghijkl
Found at location 2.
Search for: mn
String not found.
Search for: yz
Found at location 5.
2. 二分搜索
二分搜索是一种高效的搜索方法,适用于大型有序表。其基本思想是每次将搜索范围缩小一半,直到找到目标元素或确定目标元素不存在。二分搜索的时间复杂度为 $O(log n)$。
二分搜索通过
bsearch
函数实现:
#include <stdlib.h>
void *bsearch(const void *key, const void *base, size_t nel,
size_t size, int (*compar)(const void *, const void *));
-
key:要查找的元素。 -
base:指向要搜索的表的起始位置。 -
nel:表中元素的数量。 -
size:每个元素的大小(以字节为单位)。 -
compar:指向一个比较函数的指针,用于比较两个元素。
以下是一个使用
bsearch
函数搜索系统拼写字典的示例:
#include <search.h>
#include <string.h>
#include <stdio.h>
#define TABLESIZE 32768 /* max. size of the table */
#define ELEMENTSIZE 32 /* max. size of a table element */
int compare(const void *, const void *);
int
main(void)
{
char *p;
FILE *fp;
size_t nel;
char line[ELEMENTSIZE];
char table[TABLESIZE][ELEMENTSIZE];
/*
* Open the file.
*/
if ((fp = fopen("/usr/dict/words", "r")) == NULL) {
perror("/usr/dict/words");
exit(1);
}
printf("Reading the table... ");
fflush(stdout);
/*
* Read in the file.
*/
for (nel = 0; nel < TABLESIZE; nel++) {
/*
* Read a line.
*/
if (fgets(table[nel], ELEMENTSIZE, fp) == NULL)
break;
/*
* Strip the newline.
*/
table[nel][strlen(table[nel]) - 1] = '\0';
}
printf("done.\n");
fclose(fp);
/*
* Let the user search for things.
*/
for (;;) {
/*
* Prompt for a search string.
*/
printf("\nSearch for: ");
/*
* Read the search string.
*/
if (fgets(line, sizeof(line), stdin) == NULL) {
putchar('\n');
exit(0);
}
/*
* Strip the newline.
*/
line[strlen(line) - 1] = '\0';
/*
* Do a binary search for the string.
*/
p = (char *) bsearch(line, table, nel, ELEMENTSIZE, compare);
/*
* Print the search results.
*/
if (p == NULL) {
printf("String not found.\n");
}
else {
printf("Found at location %d.\n",
((int) p - (int) table) / ELEMENTSIZE);
}
}
}
/*
* compare - compare two strings, return 0 if equal, non-zero if not.
*/
int
compare(const void *a, const void *b)
{
return(strcasecmp((char *) a, (char *) b));
}
该程序的执行流程如下:
1. 打开系统拼写字典文件
/usr/dict/words
。
2. 读取文件内容到数组中,并去除每行的换行符。
3. 提示用户输入要搜索的字符串。
4. 使用
bsearch
函数进行二分搜索。
5. 输出搜索结果。
3. 哈希表
哈希表是一种常用的数据结构,用于管理符号表等。它通过哈希函数将键映射到一个桶中,从而实现快速查找。哈希表的优点是插入和查找操作的平均时间复杂度为 $O(1)$,但需要合理估计表的大小,否则可能会导致效率低下。
哈希表通过
hsearch
、
hcreate
和
hdestroy
函数实现:
#include <search.h>
typedef struct {
char *key;
char *data;
} ENTRY;
typedef enum { FIND, ENTER } ACTION;
ENTRY *hsearch(ENTRY item, ACTION action);
int hcreate(size_t nel);
void hdestroy(void);
-
hcreate:创建一个哈希表,nel是对表中最大条目数的估计。 -
hsearch:在哈希表中搜索或插入条目。 -
hdestroy:销毁哈希表。
以下是一个使用哈希表管理人员信息的示例:
#include <search.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
struct data {
int age;
int height;
int weight;
};
int
main(void)
{
char *p;
ENTRY item;
ENTRY *result;
struct data *d;
char buf[BUFSIZ];
/*
* Create the hash table.
*/
hcreate(100);
printf("Enter Name/age/height/weight; terminate with blank line.\n\n");
/*
* Read information until a blank line.
*/
while (fgets(buf, sizeof(buf), stdin) != NULL) {
/*
* Blank line, all done.
*/
if (*buf == '\n')
break;
/*
* Allocate a data structure (we should check for
* errors here).
*/
d = (struct data *) malloc(sizeof(struct data));
item.data = (char *) d;
/*
* Split up the data (we should check for errors
* here).
*/
p = strtok(buf, "/");
item.key = strdup(p);
p = strtok(NULL, "/");
d->age = atoi(p);
p = strtok(NULL, "/");
d->height = atoi(p);
p = strtok(NULL, "/");
d->weight = atoi(p);
/*
* Add the item to the table.
*/
(void) hsearch(item, ENTER);
}
/*
* Let the user search for things.
*/
for (;;) {
/*
* Prompt for a search string.
*/
printf("\nSearch for: ");
/*
* Read the search string.
*/
if (fgets(buf, sizeof(buf), stdin) == NULL) {
putchar('\n');
hdestroy();
exit(0);
}
/*
* Strip the newline.
*/
buf[strlen(buf) - 1] = '\0';
/*
* Look in the table for the item.
*/
item.key = buf;
result = hsearch(item, FIND);
/*
* Print the search results.
*/
if (result == NULL) {
printf("Entry not found.\n");
}
else {
d = (struct data *) result->data;
printf("Name: %s\nAge: %d\nHeight: %d\nWeight: %d\n",
result->key, d->age, d->height, d->weight);
}
}
}
该程序的执行流程如下:
1. 创建一个哈希表,估计最大条目数为100。
2. 提示用户输入人员信息,格式为
Name/age/height/weight
,以空行结束输入。
3. 将输入的信息存储到哈希表中。
4. 提示用户输入要搜索的姓名。
5. 使用
hsearch
函数进行搜索。
6. 输出搜索结果。
4. 二叉树
二叉树是一种高效的数据结构,用于维护有序列表。在二叉树中,每个节点的左子树中的所有节点都小于该节点,右子树中的所有节点都大于该节点。二叉树的搜索操作的平均时间复杂度为 $O(log n)$。
二叉树通过
tsearch
、
tfind
、
tdelete
和
twalk
函数实现:
#include <search.h>
typedef enum { preorder, postorder, endorder, leaf } VISIT;
void *tsearch(const void *key, void **rootp,
int (*compar)(const void *, const void *));
void *tfind(const void *key, const void **rootp,
int (*compar)(const void *, const void *));
void *tdelete(const void *key, void **rootp,
int (*compar)(const void *, const void *));
void twalk(void *rootp, void(*action)(void **, VISIT, int));
-
tsearch:构建和搜索二叉树。 -
tfind:查找二叉树中的元素。 -
tdelete:从二叉树中删除元素。 -
twalk:遍历二叉树。
以下是一个使用二叉树存储字符串并按字母顺序输出的示例:
#include <search.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
struct node {
char *string;
int length;
};
int compareNode(const void *, const void *);
void printNode(void **, VISIT, int);
int
main(void)
{
void *root;
struct node *n;
char buf[BUFSIZ];
root = NULL;
/*
* Read strings until end of file.
*/
while (fgets(buf, sizeof(buf), stdin) != NULL) {
/*
* Strip the newline.
*/
buf[strlen(buf) - 1] = '\0';
/*
* Allocate a node structure.
*/
n = (struct node *) malloc(sizeof(struct node));
if (n == NULL) {
fprintf(stderr, "out of memory.\n");
exit(1);
}
/*
* Save the information in the node.
*/
n->string = strdup(buf);
n->length = strlen(buf);
/*
* Add the item to the tree.
*/
(void) tsearch((void *) n, &root, compareNode);
}
/*
* Print out the tree in alphabetical order.
*/
twalk(root, printNode);
exit(0);
}
/*
* compareNode - compare the strings in two nodes.
*/
int
compareNode(const void *a, const void *b)
{
struct node *aa, *bb;
aa = (struct node *) a;
bb = (struct node *) b;
return(strcmp(aa->string, bb->string));
}
/*
* printNode - print a node - we only print if this is the postorder (inorder)
* visit or a leaf; this results in alphabetical order.
*/
void
printNode(void **node, VISIT order, int level)
{
struct node *n;
n = *(struct node **) node;
if (order == postorder || order == leaf)
printf("level=%d, length=%d, string=%s\n", level, n->length, n->string);
}
该程序的执行流程如下:
1. 初始化二叉树的根节点为
NULL
。
2. 从标准输入读取字符串,直到文件结束。
3. 为每个字符串分配一个节点,并将其插入到二叉树中。
4. 使用
twalk
函数按字母顺序遍历二叉树并输出节点信息。
5. 队列操作
队列是一种先进先出(FIFO)的数据结构,可以通过双向链表实现。在UNIX编程中,提供了
insque
和
remque
函数来操作队列:
#include <search.h>
void insque(struct qelem *elem, struct qelem *pred);
void remque(struct qelem *elem);
struct qelem {
struct qelem *q_forw;
struct qelem *q_back;
char *q_data;
};
-
insque:将元素elem插入到队列中pred元素的后面。 -
remque:从队列中移除元素elem。
需要注意的是,HP - UX 10.x 不提供
struct qelem
数据类型,
insque
和
remque
的参数类型为
void *
。
6. 排序
在UNIX编程中,
qsort
函数用于对数据进行原地排序,它实现了快速排序算法,平均时间复杂度为 $O(n log n)$。
#include <stdlib.h>
void qsort(void *base, size_t nel, size_t width,
int (*compar)(const void *, const void *));
-
base:指向要排序的表的起始位置。 -
nel:表中元素的数量。 -
width:每个元素的大小(以字节为单位)。 -
compar:指向一个比较函数的指针,用于比较两个元素。
以下是一个使用
qsort
函数对数组进行排序的示例:
#include <stdlib.h>
#define NELEM 10
int compare(const void *, const void *);
int
main(void)
{
int i;
int array[NELEM];
/*
* Fill the array with numbers.
*/
for (i = 0; i < NELEM; i++)
array[NELEM - i - 1] = (i * i) & 0xf;
/*
* Print it.
*/
printf("Before sorting:\n\t");
for (i = 0; i < NELEM; i++)
printf("%d ", array[i]);
putchar('\n');
/*
* Sort it.
*/
qsort(array, NELEM, sizeof(int), compare);
/*
* Print it again.
*/
printf("After sorting:\n\t");
for (i = 0; i < NELEM; i++)
printf("%d ", array[i]);
putchar('\n');
exit(0);
}
/*
* compare - compare two integers.
*/
int
compare(const void *a, const void *b)
{
int *aa, *bb;
aa = (int *) a;
bb = (int *) b;
return(*aa - *bb);
}
该程序的执行流程如下:
1. 初始化一个包含10个元素的数组。
2. 输出排序前的数组元素。
3. 使用
qsort
函数对数组进行排序。
4. 输出排序后的数组元素。
7. 环境变量
每个进程都有一组与之关联的环境变量,包括搜索路径、终端类型、用户登录名等。在UNIX编程中,可以通过以下函数来处理环境变量:
-
getenv
:获取指定环境变量的值。
#include <stdlib.h>
char *getenv(char *name);
-
putenv:设置新的环境变量。
#include <stdlib.h>
int putenv(char *string);
以下是一个打印环境变量的示例:
#include <stdio.h>
int
main(int argc, char **argv, char **envp)
{
while (*envp != NULL)
printf("%s\n", *envp++);
exit(0);
}
该程序会遍历环境变量数组并打印每个环境变量。
8. 密码加密
UNIX密码加密基于修改版的数据加密标准(DES)。当用户选择密码时,
passwd
程序会随机选择两个字符作为盐(salt),然后将密码和盐传递给
crypt
函数进行加密:
#include <crypt.h>
char *crypt(const char *key, const char *salt);
密码加密的过程如下:
1. 从密码的每个字符中提取7位,形成56位的DES密钥。
2. 根据盐的值对DES算法中的一个内部表进行4096种不同的排列。
3. 对一个全零块执行25次DES算法。
4. 将加密结果转换为64字符的字母表(A - Z、a - z、0 - 9、
.
和
/
)。
加密后的字符串包含两个字符的盐和11个字符的加密结果,且加密过程是单向的,无法解密。
当程序提示用户输入密码时,通常会使用
getpass
函数。
综上所述,这些算法和函数在UNIX编程中非常实用,可以帮助我们更高效地处理各种任务。在实际应用中,需要根据具体需求选择合适的算法和函数。
UNIX编程中的常用算法与函数详解(续)
9. 函数总结与对比
为了更清晰地了解这些算法和函数的特点和适用场景,我们可以对它们进行一个总结和对比。以下是一个简单的表格:
| 数据结构/算法 | 函数 | 时间复杂度 | 适用场景 |
| — | — | — | — |
| 线性搜索 |
compare
| $O(n)$ | 小规模无序数据搜索 |
| 二分搜索 |
bsearch
| $O(log n)$ | 大规模有序数据搜索 |
| 哈希表 |
hsearch
、
hcreate
、
hdestroy
| 平均 $O(1)$ | 快速查找和插入操作,需合理估计表大小 |
| 二叉树 |
tsearch
、
tfind
、
tdelete
、
twalk
| 平均 $O(log n)$ | 维护有序列表,需要频繁插入、删除和查找操作 |
| 队列 |
insque
、
remque
| $O(1)$ | 先进先出的数据处理 |
| 排序 |
qsort
| $O(n log n)$ | 对数据进行原地排序 |
| 环境变量 |
getenv
、
putenv
| - | 获取和设置进程的环境变量 |
| 密码加密 |
crypt
| - | 对用户密码进行加密处理 |
通过这个表格,我们可以根据具体的需求和数据特点,选择最合适的算法和函数。
10. 实际应用案例分析
下面我们来看一些实际应用案例,进一步说明这些算法和函数在实际编程中的使用。
10.1 文本搜索工具
假设我们要开发一个简单的文本搜索工具,用于在一个大文件中查找特定的字符串。我们可以使用二分搜索算法来提高搜索效率。以下是一个简化的实现思路:
1.
读取文件
:将文件内容读取到一个数组中,并对数组进行排序。
2.
用户输入
:提示用户输入要搜索的字符串。
3.
二分搜索
:使用
bsearch
函数在排序后的数组中进行搜索。
4.
输出结果
:根据搜索结果输出相应的信息。
以下是一个简单的代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINES 1000
#define MAX_LENGTH 100
int compare(const void *a, const void *b) {
return strcmp((const char *)a, (const char *)b);
}
int main() {
FILE *fp;
char lines[MAX_LINES][MAX_LENGTH];
int line_count = 0;
char search_str[MAX_LENGTH];
// 打开文件
fp = fopen("large_file.txt", "r");
if (fp == NULL) {
perror("Failed to open file");
return 1;
}
// 读取文件内容
while (fgets(lines[line_count], MAX_LENGTH, fp) != NULL && line_count < MAX_LINES) {
lines[line_count][strcspn(lines[line_count], "\n")] = 0; // 去除换行符
line_count++;
}
fclose(fp);
// 对数组进行排序
qsort(lines, line_count, sizeof(lines[0]), compare);
// 提示用户输入搜索字符串
printf("Enter the string to search: ");
fgets(search_str, MAX_LENGTH, stdin);
search_str[strcspn(search_str, "\n")] = 0; // 去除换行符
// 二分搜索
char *result = (char *)bsearch(search_str, lines, line_count, sizeof(lines[0]), compare);
// 输出结果
if (result != NULL) {
printf("String found: %s\n", result);
} else {
printf("String not found.\n");
}
return 0;
}
10.2 人员信息管理系统
我们可以使用哈希表来实现一个简单的人员信息管理系统。该系统可以让用户输入人员信息,并进行查询操作。以下是一个简化的实现思路:
1.
创建哈希表
:使用
hcreate
函数创建一个哈希表。
2.
用户输入
:提示用户输入人员信息,格式为
Name/age/height/weight
,以空行结束输入。
3.
插入信息
:将用户输入的信息插入到哈希表中。
4.
查询信息
:提示用户输入要查询的姓名,使用
hsearch
函数进行查询。
5.
输出结果
:根据查询结果输出相应的信息。
以下是一个简单的代码示例:
#include <search.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
struct data {
int age;
int height;
int weight;
};
int main() {
char *p;
ENTRY item;
ENTRY *result;
struct data *d;
char buf[BUFSIZ];
// 创建哈希表
hcreate(100);
printf("Enter Name/age/height/weight; terminate with blank line.\n\n");
// 读取信息
while (fgets(buf, sizeof(buf), stdin) != NULL) {
if (*buf == '\n')
break;
d = (struct data *)malloc(sizeof(struct data));
item.data = (char *)d;
p = strtok(buf, "/");
item.key = strdup(p);
p = strtok(NULL, "/");
d->age = atoi(p);
p = strtok(NULL, "/");
d->height = atoi(p);
p = strtok(NULL, "/");
d->weight = atoi(p);
(void)hsearch(item, ENTER);
}
// 查询信息
for (;;) {
printf("\nSearch for: ");
if (fgets(buf, sizeof(buf), stdin) == NULL) {
putchar('\n');
hdestroy();
exit(0);
}
buf[strlen(buf) - 1] = '\0';
item.key = buf;
result = hsearch(item, FIND);
if (result == NULL) {
printf("Entry not found.\n");
} else {
d = (struct data *)result->data;
printf("Name: %s\nAge: %d\nHeight: %d\nWeight: %d\n",
result->key, d->age, d->height, d->weight);
}
}
return 0;
}
11. 注意事项和优化建议
在使用这些算法和函数时,需要注意以下几点:
-
内存管理
:在使用动态内存分配函数(如
malloc
)时,要确保及时释放内存,避免内存泄漏。
-
错误处理
:在调用函数时,要检查函数的返回值,进行适当的错误处理,以提高程序的健壮性。
-
性能优化
:根据具体的应用场景,选择合适的算法和数据结构,以提高程序的性能。例如,对于大规模数据的搜索,优先选择二分搜索或哈希表;对于排序操作,使用
qsort
函数。
以下是一些优化建议:
-
哈希表大小
:在使用哈希表时,要合理估计表的大小,避免哈希冲突过多导致性能下降。
-
二叉树平衡
:在使用二叉树时,要注意保持树的平衡,避免出现极端情况导致搜索效率降低。
-
环境变量管理
:在设置环境变量时,要注意变量名的规范和内存管理,避免出现意外错误。
12. 总结
本文详细介绍了UNIX编程中常用的算法和函数,包括线性搜索、二分搜索、哈希表、二叉树、队列操作、排序、环境变量处理以及密码加密等。通过实际的代码示例和应用案例,展示了这些算法和函数的使用方法和适用场景。同时,还提供了一些注意事项和优化建议,帮助读者更好地使用这些算法和函数。
在实际编程中,我们可以根据具体的需求和数据特点,选择最合适的算法和函数,以提高程序的效率和性能。希望本文对读者在UNIX编程中有所帮助。
下面是一个简单的流程图,展示了一个通用的搜索程序的执行流程:
graph TD;
A[开始] --> B[读取数据];
B --> C[数据预处理];
C --> D[用户输入搜索关键字];
D --> E[选择搜索算法];
E --> F[执行搜索];
F --> G{是否找到};
G -- 是 --> H[输出结果];
G -- 否 --> I[输出未找到信息];
H --> J[结束];
I --> J;
通过这个流程图,我们可以更清晰地了解搜索程序的执行过程。
希望这些内容能够帮助你在UNIX编程中更好地运用这些算法和函数,提高编程效率和质量。如果你有任何疑问或建议,欢迎留言讨论。
超级会员免费看
780

被折叠的 条评论
为什么被折叠?



