单链表的定义就不说了, 很简单, 请自行百度; 那么从今天的主题<单链表的应用>入手; 利用单链表实现电话本的模拟程序:定义单链表的数据类型,将头插法和尾插法、插入、逆置、删除、查找、修改、计数、输出等操作都定义成子函数的形式,最后在主函数中调用它( 兄弟们调用就不弄了, 自行测试, 增加动手能力 )
整个程序最主要的步骤还是插入, 查找, 删除操作, 其他操作都可以围绕这三个操作来进行, 其实单链表无论是什么操作都可以总结为指针的操作, 无非是指针的指向, 通过指针的指向就能完成很多步骤, 但是在单链表中由于指针是单向的, 不像双链表那样可以双向操作, 所以操作起来有点不太方便(其实还是很简单的, 只是说了吓唬人的)
此处做注释: 链表的head节点数据域不存数据, head所在的位置是-1, 链表中元素是从0号开始
那么我就先从insert开始:
insert操作
void insert(int index,LNode *L1,LNode *L2);
insert就是在特定的位置(index)把L2节点连接到L1上面, 所以就有如下实现方式:
void insert(int index,LNode *L1,LNode *L2){
for(int i=0;(inext);
L2->next=L1->next;L1->next=L2;
}
L1与L2需要判断是否为空(避免引用非法指针), 上面的for操作主要是将指针L1移动到需要插入的位置的前一个位置, 首先将L2的next指针存入原本L1的后一个位置的地址, 然后将L1的next指针存入L2的地址, 这两步操作不能颠倒, 颠倒, 原本链表中L1后面部分的链表结构将会丢失; (请细细理解)
在创建链表的过程中我们可以选择头插法或者尾插法进行创建链表;
所谓头插法简单的来说就是每次插入操作总是把插入节点的位置定位0号, 头插法的意义就在于我们可以很容易完成逆置的操作, 头插法代码如下:
void insertHead(LNode *L1,LNode *L2){
insert(0,L1,L2);
}
头插法就是在0号位置把L2插入到L1的后面, 如果将头插法应用到创建链表, 那么此处的L1便是head
尾插法就是每次在尾部插入节点, 插入的位置总是末尾; 代码如下:
void insertTail(LNode *L1,LNode *L2,int t){
insert(t,L1,L2);
}
此处L1, L2的含义与头插法中的含义一致, t表示末尾的位置, 末尾需要传入参数来确定, 待会在main函数中测试再进行说明, 两个插入的区别就在于位置的变化, 由两种插入方面所形成的链表顺序是相反的
逆置操作
上面提到逆置操作需要用到头插法, 因为每次节点传入的insertHead中, 我总是把节点插入到0号位置, 也就是head后面, 一个正序的链表, 我把它的节点按顺序不断拆下来, 同时将每个拆下来的节点连接到head的0号位上面, 而不是又按照原有的顺序进行顺序连接(表面上看好像连和没连一样, 这里你需要画图好好思考), 举个例子: head 0 1 2 3的顺序表, 0拆下连接到head上面, 1拆下连接到head上面, 此时0就连接到1号上面, 顺序为head 1 0, 同理, 2拆下连接到head上面, 1又连接到2上面, 顺序为head 2 1 0 ,最后整个过程重复, 得到的顺序就是head 3 2 1 0, 然后就可以做到把一个正序的链表变为逆序的
话不多说, 上代码:
void convert(LNode *L){//此处逆置是对整个链表逆置, L传入的是head
LNode* p=L->next;
L->next=NULL;
LNode* q;
int t_size=size;
while(t_size>0){
q=p->next;
p->next=NULL;
insert(0,L,p);
p=q;t_size--;
}
}
LNode* p=L->next;L->next=NULL;LNode* q;是将head分离, while内部的操作主要是通过不断迭代的方式, 做到将第一个节点与第二个节点进行分离, 以达到将在0号位插入的效果
删除, 查找, 修改
这三步操作是围绕查找来, 由于单链表结构的特点, 所以只能做到顺序查找(可采用平衡二叉搜索树, 实现链表的O(logn)的复杂度)
上代码:
LNode *find(LNode *L,char *e){
while(L!=NULL){//避免引用非法指针
if(strcmp(L->data.name,e)==0)
return L;
L=L->next;
}
return head;
}
传入head节点, 以及所要查找的元素e(使用指针, 其效果与char e[]一致), 不断比较, 成功则返回所指向的节点, 失败则返回head
删除: 删除我做了修改, 让它具有两个功能–删除特定元素, 删除整个链表(其实还是为了偷懒, 少写代码)
void remove(LNode *L,LNode *LTarget_value){
while(L->next!=NULL){
if((L->next==LTarget_value)&&(LTarget_value!=NULL)){//用于删除特定元素
LNode* p=L->next;L->next=p->next;//将所要删除的节点进行分离操作
size--;//删除一个人就需要总人数减一次
delete p;return;//若删除p立马退出
}else if(LTarget_value==NULL){//用于删除整个链表
LNode* p=L->next;L->next=p->next;
delete p;//分离一个节点进行一次删除,直到删除到只剩下head节点才停止
}
if(LTarget_value!=NULL) L=L->next;//只有在删除特定元素的时候才进行L的移动
}
if(LTarget_value==NULL) delete L;//只有在删除整个链表才删除head
}
修改操作: 修改可针对特定元素, 此处只做简单说明
上代码:
void revise(LNode *L,char *e,char *Ae,int i){
LNode *p=find(L,e);
if(p->data.name==NULL && p->data.num=NULL )
return;//表示没有找到要修改的元素,p现在是head,head里面没有存数据, 直接跳出程序
if(i==1) strcpy(p->data.name,Ae);
else if(i==2) strcpy(p->data.num,Ae);
}
L表示head, e表示要查找的目标元素, Ae表示替换e, i表示选择替换name, 还是替换num
输出
输出就很简单了, 不做叙述
void display(LNode *L){
while(L!=NULL){
printf("%s %s\n",L->data.name,L->data.num);
L=L->next;
}
}
struct DataType{
char name[10];
char num[12];
};
struct LNode{
LNode *next;
DataType data;
};
LNode *head=new LNode();//head节点
int size=0;//用作计数, 计算存入多少联系人
int main(){
int t_size;
int sum=0,i;
char na[10],nu[12];
printf("输入需要创建的电话本大小以及1.头插OR2.尾插\n");
scanf("%d%d",&t_size,&i);
while(sumnext=NULL;
strcpy(s->data.name,na);
strcpy(s->data.num,nu);
size++; //每输入一次,size进行一次增加, 只有在输入才有人数的增加
if(i==1){//输入时选择头插法,还是尾插法
insertHead(head,s);
}else if(i==2){
insertTail(head,s,sum);
}++sum;
}
display(head);
// printf("输入要查找的元素\n");fflush(stdin);
// scanf("%s",na);
// printf("%s\n",find(head,na)->data.name);
// printf("删除链表中元素\n");fflush(stdin);
// scanf("%s",na);
// if(i==1){
// puts("输入要删除的元素");scanf("%s",na);
// remove(head,find(head,na));
// }
// display(head);
// puts("若号码以及名字都要修改那么你可选择直接删除");
// puts("需要修改的元素,修改后的值,1.修改名字,2.修改号码");
// scanf("%s%s%d",na,nu,&i);
// revise(head,na,nu,i);
puts("输入在几号位置进行插入操作,插入元素为,0号是首元素");
scanf("%d",&i);
printf("输入名字,号码\n");fflush(stdin);
scanf("%s%s",na,nu);
LNode *s=new LNode();s->next=NULL;
strcpy(s->data.name,na);
strcpy(s->data.num,nu);
insert(i,head,s);
// convert(head);
display(head);
remove(head,NULL);
return 0;
}
代码进行拼凑就可使用, 可能存在异常, 但是主要还是阐述思想, 上面注释部分为测试每个功能的代码, 最后还需做修改, 写的不好, 还有很多地方需要改进, 就不做修改了, 界面做的很简陋, 整个程序最核心的还是insert, find, remove, 理解头插法与尾插法的区别, 有问题还请在下方留言