/*
单链表: 访问时,只能通过表头遍历进行访问,遍历结束的条件是最后一个节点的为NULL。单链表中可以分为数据域和指针域。数据域为用户存储数据的变量。指针域则指向下一个节点。 一般单链表操作可以分为头节结方式和头指针方式(struct node *root=NULL)方式。 由于单链表访问节点只有一条路径,因此再进行有序追加数据、删除数据和查找中间节点时,需要用到快慢指针。
*/
#include
#include
#define NR(x) sizeof(x)/sizeof(x[0])
typedef int DataType;
typedef struct node {
DataType data;
struct node *next;
}linkList;
/*头部插入法*/
void top_append(struct node *head,DataType data);
/*尾部插入法*/
void tail_append(struct node *head,DataType data);
/*删除节点*/
void del_node(struct node *head,int key);
/*打印单链表*/
void dispaly_list(struct node *head);
/*链表倒置*/
void invert_list(struct node *head);
/*查找中间节点*/
struct node *find_midList(struct node *head);
/*链表顺序插入*/
void insert_sortList(struct node *head,DataType data);
int main(void){
linkList head={.next=NULL};
DataType num[]={101,2,3,4,5,56,12,34,57,89,100,0};
int i;
/*头部插入*/
for(i=0;idata);
else
printf("this is linklist is empty\n");
return 0;
}
/*
将data数据插入到head后面,即每次将新的数据往头部添加
*/
void top_append(struct node *head,DataType data){ //先插入的数据,最后打印
struct node *new;
new=malloc(sizeof(struct node));
if(new==NULL)
{
printf("分配新空间失败\n");
return ;
}
new->data=data; //将数据赋值给新的节点
new->next=head->next; //将头节点指向的下一个节点的地址给新节点
head->next=new; //将新节点的地址给头结点
}
void dispaly_list(struct node *head){
struct node *tmp;
for(tmp=head->next;tmp!=NULL;tmp=tmp->next) //头结点的data不存在数据,因此tmp=head->next
printf("%d ",tmp->data);
}
/*
将新数据添加到单链表的尾部
*/
void tail_append(struct node *head,DataType data){
struct node *new,*tmp;
new=malloc(sizeof(struct node));
if(new==NULL)
{
printf("新节点分配空间失败\n");
return ;
}
new->data=data;
for(tmp=head;tmp->next!=NULL;tmp=tmp->next); //遍历单链表直到链表为空,即链表元素的next为NULL
tmp->next=new; //将新节点给链表的最后一个节点的next
new->next=NULL;
}
/*
采用快慢指针方式进行插入
由于单链表指针只能从头部开始遍历,因此采用快慢指针的方式进行遍历,保留上一个节点的指针,进行插入操作
思路分析(**link *cur):cur=*link cur为快指针,*link为慢的指针域
1、链表为空时,插入第一个数据, 只有head头节点.则*link为NULL,cur=NULL;则不进入while循环。直接将new->next=NULL,*link=new(head->next=new).
2、链表不为空时,插入的数据都比原来链表中的要小,进入循环将cur->next的地址给link,则*link等于cur->next指向的下一个节点的地址直到cur为即前一次的cur->next指向的下一节点为NULL
3、链表不为空,插入的数据比链表中的其中一个要小,则会在中途break掉.此时cur->data比新的data大。则需要切断cur节点跟前一个节点的链。然后将cur节点接到new->next中。*link为cur前一个节点的next
*/
void insert_sortList(struct node *head,DataType data){
struct node *new,*cur,**link=&(head->next); //将头结点的next地址给二级指针**link, link解引用为head的next的地址,*link为head->next指向的节点(第一个节点)的地址
new=malloc(sizeof(struct node));
if(new==NULL)
{
printf("新节点分配空间失败]n");
return ;
}
new->data=data; //将data赋值给新节点的数据域
#if 1
struct node *slow=head,*fast; //快慢指针
for(fast=head->next;fast!=NULL;fast=fast->next)
{
if(fast->data>=data)
break;
slow=fast;
}
new->next=fast;
slow->next=new;
#else
while(cur=*link){ //link为装的节点的next地址 *link为解引用为next
if((cur->data)>=data) //假设当前值小于当前数据域,则跳出循环
break ;
link=&(cur->next); //将当前的cur->next的地址给link指针 即*link为cur->next指向的节点的地址
}
new->next=cur;
*link=new;
#endif
}
/*
思路:链表逆置
将将原来的链表的表头处砍断,然后将砍断部分按头部插入法插入
*/
void invert_list(struct node *head){
struct node *tmp,*tmp_head;
tmp_head=malloc(sizeof(struct node));
if(tmp_head==NULL)
{
printf("分配空间失败\n");
return ;
}
//在头部砍断链表,表头的next指向的下一个节点给tmp_head临时表头
tmp_head->next=head->next;
head->next=NULL;
for(tmp=tmp_head->next;tmp!=NULL;tmp=tmp->next)
top_append(head,tmp->data);
free(tmp_head);
}
/*
描述:删除数据域=key的节点
思路:这里仍然采用快慢指针,快指针进行比较,慢指针保留后面一个节点
*/
void del_node(struct node *head,int key){
struct node *fast,*slow;
if(head->next==NULL){
printf("This linklist is not entry\n");
return ;
}
#if 0
for(fast=head->next;fast!=NULL;fast=fast->next){
if(fast->data==key)
break;
slow=fast;
}
slow->next=fast->next;
free(fast);
#else
struct node **link=&head->next,*cur;
while(cur=*link){
if(cur->data==key)
break;
link=&cur->next;
}
*link=cur->next;
free(cur);
#endif
}
/*
描述:查找中间节点
思路:仍然是用快慢指针,快指针的速度是慢指针的两倍,当快指针走完链表时,慢指针刚好是链表的一半
*/
struct node *find_midList(struct node *head){
struct node *slow=head,*fast=head;
if(NULL==head->next){
printf("This linklist is not entry\n");
return NULL;
}
while(fast->next){
slow=slow->next;
fast=fast->next;
if(fast->next!=NULL) //快指针每走完一个节点都要进行判断其指向的下一个节点是否为空,不为空,则进行下一步遍历
fast=fast->next;
}
return slow;
}