链表 —— 由一连串的结构(称为结点node)组成,每个结点都包含指向下一个结点的指针,最后一个为空指针。
一、声明结点类型
struct node{
int value;
struct node *next;
};
struct node *first = NULL;
- node 为结构标记,通常可使用结构标记或 typedef 来定义一种特殊的结构类型。但若在结构中有一个指向相同结构类型的指针成员时,要求使用结构标记。
二、创建结点
创建结点的步骤: ① 为新结点分配内存单元; ② 将数据存储到新结点中; ③ 将新节点插入到链表中。
struct node *new_node;
new_node = (struct node*)malloc(sizeof(struct node));
new_node->value = 10;
三、-> 运算符
-> —— 右箭头选择 (right arrow selection)
- 需要通过一个指向结构的指针访问该结构的某个成员时,可使用
-> 运算符。 - 运算对象必须为指向结构的指针。
new_node->value = 10;
(*new_node).value = 10;
-> 运算符产生左值,可以在任何允许使用普通变量的地方使用它,例如:
scanf("%d", &new_node->value);
四、在链表中插入结点
链表的优势在于可以在链表的任何位置添加结点。
我们在链表中插入一个新结点之前,需要先确定插入位置,由于插入后需要重新拼接,为此需要确定两个位置,分别为插入位置,和其前一个结点的位置。
- 插入结点之前需要前确定插入位置是否为链表开头
- 插入步骤:
- 创建新结点 new_node (分配内存,存入数据);
- 确定插入位置 cur,和插入位置的前一个结点位置 prev;
- 链接:将 cur 、new_node 和 prev 链接起来。
struct node *new_node = (struct node*)malloc(sizeof(struct node));
new_node->value = 10;
struct node *cur = fisrt, *prev = NULL;
...
new_node->next = cur;
if(prev == NULL)
first = new_node;
else
prev->next = new_node;
往链表开头中插入结点:
- 参数1:指向旧链表首结点的指针;
- 参数2:需要存储在新结点的数据。
- 返回:新链表的首地址
struct node *add_to_list(struct node *list, int n)
{
struct node *new_node = (struct node*)malloc(sizeof(struct node));
if(new_node == NULL){
printf("Error: maloc failed in add_to_list.\n");
exit(EXIT_FAILURE);
}
new_node->value = n;
new_node->next = list;
return new_node;
}
函数的使用:
first = add_to_list(first, 10);
first = add_to_list(first, 20);
五、链表的搜索
惯用法:
for(p = first; p != NULL; p = p->next){
...
}
链表搜索函数:
struct node *search_list(struct node *list, int n)
{
struct node *p = NULL;
for(p = list ; p != NULL; p = p->next){
if(p->value == n) break;
}
return p;
}
struct node *search_list(struct node *list, int n)
{
for( ; list != NULL; list = list->next){
if(list->value == n) break;
}
return list;
}
- list 是原始链表指针的副本,也没有对其进行间接寻址,因此在函数内修改 list 的值不会造成任何影响。若要在函数内修改原始指针,需要传入指向该指针变量的指针,通过间接寻址的方式修改才能有效。
struct node *search_list(struct node *list, int n)
{
for( ; list != NULL && list->value != n; list=list->next)
;
return list;
}
struct node *search_list(struct node *list, int n)
{
while(list != NULL && list->value != n)
list = list->next;
return list;
}
六、从链表中删除结点
链表的优势之一就是可以轻松地删除链表中不需要的结点。
删除结点的步骤: ① 定位需删除的结点; ② 改变前一个结点,使它跳过删除结点,链接到后一个结点上; ③ 释放回收删除结点的内存空间。
由于需要对前一个结点进行修改,因此在定位待删除结点时,也需要存储前一个结点的位置。
共有3种情况: - 常规情况:目标结点存在且不是链表的首结点 - 特殊情况1:没找到目标结点(不需要删除) - 特殊情况2:链表首结点就是目标结点
struct node *delete_from_list(struct node *list, int n)
{
struct node *prev, *cur;
for(prev=NULL,cur=list; cur!=NULL,cur->value!=n; prev=cur,cur=cur->next)
;
if(cur == NULL) return list;
if(prev == NULL)
list = list->next;
else
prev->next = cur->next;
free(cur);
return list;
}
七、有序链表
有序链表的结点是有序的,各个结点按该节点中的数据排序。 有序链表的插入更加困难,但搜索会更快。
内容详见下一节。
八、 综合举例:维护零件数据库 (插入/删除/修改/搜索)
要求实现的功能:
- 添加新零件编号、名称和初始的现货数量;若零件已在数据库中,或数据库已满,则显示出错信息
- 给定零件编号,删除该零件的数据;
- 给定零件编号,显示零件信息;
- 给定零件编号,改变现有的零件数量;
- 显示列出数据库中全部信息的表格;
- 终止程序的执行。
各个功能中,若给定的零件编号非法(不是单个有效整数),或零件不再数据库中,需要显示出错信息。
由于零件名不一定是连续的字符串,例如 Disk Drive ,而 scanf 遇到空白字符会停止读取,因而必须使用逐字符读取字符串的方法。 需要读取单个数字时,也需要考虑到用户输出非法的问题,也需要创建一个单独的函数来读取。
使用操作符:i (插入)、d (删除)、s (搜索查看)、u (更新数据)、p (全显示)、q (退出)
程序完整代码:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define NAME_LEN 25
struct part{
int number;
char name[NAME_LEN + 1];
int on_hand;
struct part *next;
};
struct part *inventory = NULL;
int read_a_number(void);
int readline(char *str, int n);
struct part *find_part(int number);
void insert(void);
void delete(void);
void update(void);
void search(void);
void print(void);
int main(){
char code;
for(;;){
printf("---- i-insert, d-delete, s-search, u-update, p-print, q-qiut ----\n");
printf("Enter operation code: ");
scanf(" %c", &code);
while(getchar() != '\n')
;
switch (code){
case 'i' : insert(); break;
case 'd' : delete(); break;
case 's' : search(); break;
case 'u' : update(); break;
case 'p' : print(); break;
case 'q' : return 0;
default : printf("Illegal code! Please enter correct code.\n");
}
printf("\n\n\n");
}
return 0;
}
struct part *find_part(int number)
{
struct part *p;
for(p = inventory; p != NULL && p->number < number; p = p->next)
;
if(p != NULL && p->number == number) return p;
return NULL;
}
int read_a_number(void)
{
int number;
while( !(scanf("%d", &number) && getchar() == '\n') ){
while( getchar() != '\n')
;
printf("Invalid input, please re-enter a number: ");
}
return number;
}
int readline(char *str, int n)
{
int ch, i = 0;
while( isspace(ch = getchar()) )
;
while( ch != '\n' && ch != EOF){
if(i < n) str[i++] = ch;
ch = getchar();
}
str[i] = 0;
return i;
}
void insert(void)
{
struct part *prev, *cur, *new_node;
new_node = (struct part*)malloc(sizeof(struct part));
if(new_node == NULL){
printf("Datebase is full, cannot add more parts.\n");
return;
}
printf("Enter part number: ");
new_node->number = read_a_number();
for(prev = NULL, cur = inventory; cur != NULL && cur->number < new_node->number; prev = cur, cur = cur->next)
;
if(cur != NULL && cur->number == new_node->number){
printf("Part already exist.\n");
free(new_node);
return;
}
printf("Enter part name: ");
readline(new_node->name, NAME_LEN);
printf("Enter quantity on hand: ");
new_node->on_hand = read_a_number();
new_node->next = cur;
if(prev == NULL)
inventory = new_node;
else
prev->next = new_node;
}
void delete(void)
{
int number;
printf("Enter part number: ");
number = read_a_number();
struct part *prev, *cur;
for(prev = NULL, cur = inventory; cur != NULL && cur->number < number; prev = cur, cur = cur->next)
;
if(cur != NULL && cur->number == number){
if(prev == NULL)
inventory = cur->next;
else
prev->next = cur->next;
free(cur);
printf("Part has been deleted.\n");
}
else
printf("Part not exist, no need to delete.\n");
}
void update(void)
{
int number, change_option, change;
printf("Enter part number: ");
number = read_a_number();
struct part *p;
p = find_part(number);
if(p != NULL){
printf("Change option(1-part name, 2-quantity on hand): ");
for(;;){
change_option = read_a_number();
if(change_option == 1){
printf("Enter a new name: ");
readline(p->name, NAME_LEN);
break;
}
else if(change_option == 2){
printf("Enter change in quantity on hand: ");
change = read_a_number();
p->on_hand += change;
break;
}
else
printf("Invalid input, please enter correct number(1 or 2): ");
}
}
else
printf("Part not found.\n");
}
void search(void)
{
int number;
printf("Enter part number: ");
number = read_a_number();
struct part *p;
p = find_part(number);
if(p != NULL){
printf("Part name: %s\n", p->name);
printf("Quantity on hand: %d\n", p->on_hand);
}
else
printf("Part not found.\n");
}
void print(void)
{
struct part *p;
printf("Part Number Part Name Quantity on hand\n");
for(p = inventory; p != NULL; p = p->next){
printf("%7d %-25s%11d\n", p->number, p->name, p->on_hand);
}
}
|