链表
定义
线性表链式存储的特点是:用一组任意的存储单位存储线性表的数据元素,这一组存储单位可以是连续的,也可以是不连续的。
链表中的每一项都包含着在何处能找到下一项的信息,这两部分信息组成数据元素的存储映像,称为结点;
它包括两个域:存储数据元素信息的叫做数据域;存储直接后续存储位置的称为指针域
n个结点链结成一个链表
链表是一种物理存储单位上的非连续、非顺序存储结构
根据链表结点所含的指针的个数,指针指向和指针连接方式,可将链表分为单链表、循环链表、双向链表、二叉链表等
循环链表和双向链表用于实现线性表的链式存储结构没其他形式用于实现树和图等非线性结构
单链表
整个链表的存储必须从头指针开始进行
用单链表表示线性表,数据元素之间的逻辑关系是由结点中的指针指示的;则逻辑上响铃的两个数据元素其存储的物理位置不要求紧邻;
电影的添加和显示
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TSIZE 45
struct film {
char title[TSIZE];
int rating;
struct film * next; //指针不仅指向数据域,也指向指针域
};
char * s_gets(char * st, int n);
int main(void){
struct film * head = NULL;
struct film * prev, *current;
char input[TSIZE];
/* 收集并存储信息 */
puts("Enter first movie title:");
while (s_gets(input, TSIZE) != NULL && input[0] != '\0'){
current = (struct film *) malloc(sizeof(struct film));
if (head == NULL) /* 第1个结构 */
head = current;
else /* 后续的结构 */
prev->next = current;
current->next = NULL;
strcpy(current->title, input);
puts("Enter your rating <0-10>:");
scanf("%d", ¤t->rating);
while (getchar() != '\n')
continue;
puts("Enter next movie title (empty line to stop):");
prev = current;
}
/* 显示电影列表 */
if (head == NULL)
printf("No data entered. ");
else
printf("Here is the movie list:\n");
current = head;
while (current != NULL){
printf("Movie: %s Rating: %d\n",
current->title, current->rating);
current = current->next;
}
/* 完成任务,释放已分配的内存 */
current = head;
while (current != NULL){
head = current->next;
free(current);
current = head;
}
printf("Bye!\n");
}
char * s_gets(char * st, int n){
char * ret_val;
char * find;
ret_val = fgets(st, n, stdin);
if (ret_val)
{
find = strchr(st, '\n'); // 查找换行符
if (find) // 如果地址不是 NULL,
*find = '\0'; // 在此处放置一个空字符
else
while (getchar() != '\n')
continue; // 处理剩余输入行
}
return ret_val;
}
特点
用一组任意的存储单位来存储线性表中的数据元素。在插入和删除元素中可以通过直接修改指针完成操作
链表操作
显示链表
- 从设置一个指向第一个结构的指针开始,由于头指针已经指向链表中的第一个结构
- 重新设置current指针指向链表中的下一个结构
- 当显示到链表最后一个项时,current将被设置为NULL,因为这是链表中最后一个结构中next成员
struct book* che(struct book*head){
puts("here is your list");
struct book*current=head;
while(current!=NULL){
printf("book:%s author:%s price:%d date:%d \n",current->book,current->author,current->price,current->date);
current=current->next;
}
printf(" There are all the book you have entered\n\n");
}
P.S. 不直接使用head的原因:会改变head的值,程序就找不到链表的开始处
创建链表
struct book{
char book[size] ;
char author[size];
int price;
int date;
struct book *next;
};
步骤
-
使用malloc为结构分配足够的空间;
如无必要不用创建一个结构,使用临时存储区(input数组)获取用户输入的电影名
//用户通过键盘模拟EOF或者输入一行空行,将退出下面的循环 while (s_gets(input, TSIZE) != NULL && input[0] != '\0') ; //用户进行输入,程序就分配一个结构的空间,并将其地址赋给指针变量current current = (struct book *) malloc(sizeof(struct book));
-
存储结构的地址;
程序分配一个结构的空间,并将其地址赋给指针变量current
链表是一种动态结构,所占用的大小和位置是不需要提前分配的,可以根据自身的需求即时生成
-
把当前信息拷贝到结构中
printf("please enter the first book's information\n"); printf("please enter the book's name: \n") ; scanf("%s",&head->book); fflush(stdin); printf("please enter the author's name: \n") ; scanf("%s",&head->author); fflush(stdin); printf("please enter the prices: \n") ; scanf("%d",&head->price); fflush(stdin); printf("please enter the published year: \n"); scanf("%d",&head->date); fflush(stdin); head->next=NULL;
尾插法
struct book * add(struct book*head){
struct book*current;
current=(struct book*)malloc(sizeof(struct book));
current->next=NULL;
struct book*pre=head;
while (pre->next!=NULL){ //遍历直到最后一项
pre=pre->next;
}
printf("please enter the book's name: \n") ;
scanf("%s",¤t->book);
fflush(stdin);
printf("please enter the author's name: \n") ;
scanf("%s",¤t->author);
fflush(stdin);
printf("please enter the prices: \n") ;
scanf("%d",¤t->price);
fflush(stdin);
printf("please enter the published year: \n");
scanf("%d",¤t->date);
fflush(stdin);
pre->next=current;
}
确定链表是否为空
如果链表表头不为空,证明链表不为空;因为头结点指针域存储的就是链表的第一个结构
bool EmptyList(LinkList L){
if(L->next)
return false;
else
return true;
}
返回链表元素个数(待补充功能)
链表没有定义表长,要使用工作指针后移,直到节点为空
int LengthList(LinkList L){
int i = 0;
LinkList p = L->next;
//L是头结点,L->next 代表链表的第一个节点。
//LinkList其实是 Node * ,所以p指向链表的链表的第一个节点。
while(p){
i++;
p = p->next;
}//指针一直后移,直到节点不存在为止。
return i;
}
清空链表
原理:工作指针后移
依次释放内存,直到最后一个节点为止
void ClearList(LinkList *L){
LinkList p,q;
p = (*L)->next;
while(p){
q = p->next; //指向下一个
free(p); //释放内存
p = q;
}
(*L)->next = NULL;
}
确定链表是否为满;
插入元素
找到要插入的位置,创造新节点,使得前一个节点的指针指向新节点,使新节点的指针指向原节点后面的节点
Status ListInsert(LinkList *L,int i,ElemType e){
LinkList p,s;
p = (*L); //声明一节点p 指向链表的头结点
int cnt = 1;
while(p && cnt < i){
cnt++;
p = p->next; //指向下一个节点
}
if(!p) //链表末尾p不存在,说明插入位置有误
return ERROR;
s = (LinkList)malloc(sizeof(Node));
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
检索项,返回顺序
int ser (struct book*head){
struct book*current=head;
char input[size];
puts("please enter the book's name");
scanf("%s",input);
fflush(stdin);
int n=1;
while(current){
if((strcmp(input, current->book))==0)
return n;
current=current->next;
n++;
}
return 0; //如果找不到。则return 0;
}
修改一项中的信息
struct book* mod(struct book*head){
struct book*current=head;
int choice;
char input[size];
printf("please enter the book's name:\n");
//此处采用名字检索,如若要增加其他方式检索,则使用switch语句让用户进行选择后面语句稍微替换即可
scanf("%s",input);
fflush(stdin);
while(current){
if((strcmp(input, current->book))==0){
printf("OPTIONS: 1.name 2.author 3.price 4.date\n");
scanf("%d",&choice);
fflush(stdin);
}
switch(choice){
case 1:
puts("please enter the information");
scanf("%s",¤t->book);
fflush(stdin);
break;
case 2:
puts("please enter the information");
scanf("%s",¤t->author);
fflush(stdin);
break;
case 3:
puts("please enter the information");
scanf("%d",¤t->price);
fflush(stdin);
break;
case 4:
puts("please enter the information");
scanf("%d",¤t->date);
fflush(stdin);
break;
}
current=current->next;
if(current==NULL){
puts("sorry, the book can't be found!");
return 0;
}
}
return head;
}