最近几天学习了一下数据结构中的链表,其实大二的时候老师就教过数据结构这门课的,但我不敢说自己学过数据结构,呵呵!为什么呢?那时候老师讲链表的时候讲的那叫一个深奥啊!又不举个例子演示一番,刚开始兴趣还是有的,可后面听着听着就整个人都不好了。蓝瘦,香菇!......,后来就再也没有听过了,只有期末考试时刷刷往年的题目,应付了过去。所以当时数据结构学的怎么样可想而知!
其实学习链表主要是得掌握指针的操作以及结构体的用法,当然这里边我觉得链表的操作在数据结构我觉得算是比较难的了。不过通过自己几天的挣扎,算是把单链表的基本操作搞清楚了,想想还是蛮开心的。下面把自己写的代码记录下来,便于日后复习。
/*********************************************************************************
* Copyright: (C) 2017 zoulei
* All rights reserved.
*
* Filename: list.c
* Description: This file
*
* Version: 1.0.0(2017年07月22日)
* Author: zoulei <zoulei121@gmail.com>
* ChangeLog: 1, Release initial version on "2017年07月22日 15时41分56秒"
*
********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
struct student
{
int number ; /*学生学号 */
int score; /*学生分数 */
struct student *next;
};
int n;
/* 创建不带头结点的链表, */
struct student *Createlist()
{
struct student *head; /*这里的head是指带数据的第一个节点。*/
struct student *new; /*new保存创建的新节点的地址 */
struct student *tail; /*tail保存原链表最后一个节点的地址 */
n=1; /*创建前链表的节点总数为0:空链表 */
new=(struct student*)malloc(sizeof(struct student)); /* 分配的内存给第一个节点 */
if(new==NULL)
{
printf("distrubute memory failed!\n");
return NULL;
}
else
{
printf("向第%d个节点输入数据:",n );
scanf("%d,%d",&(new->number),&(new->score)); /* 给第一个节点输入数据 */
}
while(new->number!=0)
{
if(1==n) /*创建第一个节点链表*/
{
head=new;
tail=head;
tail->next=NULL;
}
else /* 创建第二个节点,第三个节点...的链表 */
{
new->next=NULL;
tail->next=new;
tail=new;
}
n++;
new=(struct student*)malloc(sizeof(struct student));/* 分配的内存给第二个,第三个节点... */
printf("向第%d个节点输入数据:",n);
scanf("%d,%d",&(new->number),&(new->score)); /* 给第二个,第三个节点...输入数据 */
}
return head;
}
/*输出链表中的所有数据*/
void print(struct student*head)
{
struct student *temp;
int i=0;
temp=head;
while(NULL!=temp)
{
i++;
printf("第%d个节点的数据是:%d,%d\n",i,temp->number,temp->score);
temp=temp->next;
}
}
/* 链表逆序 */
struct student *inverse_list(struct student* phead)
{
struct student *current=phead; /*指向当前节点需要反转的指针 */
struct student *pnext; /*指向下一个需要反转的节点的指针 */
struct student *plast=NULL; /* 临时指针,它的作用是保存指向当前结点的指针 */
printf("逆序后的链表数据输出为:\n");
while(current->next !=NULL)
{
pnext=current->next; /*当前节点指向下一个节点的指针赋值给pnext */
current->next=plast;
plast=current;
current=pnext;
}
/* 这里的循环可以这么理解:假设是第一轮逆序,current->next指向的是第二个节点的地址,将其赋值
* 给pnext,这时再将临时指针plast赋值给指向第二个节点地址的指针(第一轮plast为初始化NULL),前面
* 这两段代码的操作作用是将第一个节点与第二个节点断开,第一个节点的next为NULL,这样就将第一个节
* 点逆序为最后一个节点,下面两段代码是将指向第一个节点地址的指针给plast,将指向第二个节点地址的
* 指针给current,这样就来到了第二轮逆序,第二轮逆序链表中的第二个节点就好比之前第一个节点,第三个
* 节点就好比之前的第二个节点,如此循环,直到最后一个节点。
* */
current->next=plast; /*这里的plast指向最后一个节点的前一个节点*/
return current;
}
/*销毁链表*/
void DestroyList(struct student *head)
{
struct student *ptr;
ptr=head;
printf("销毁链表后各个节点的数据输出:\n");
while(NULL !=ptr)
{
free(ptr);
printf("%d,%d\n",ptr->number,ptr->score);
ptr=ptr->next;
}
}
/*求链表长度 */
int ListLength(struct student *head)
{
int i=0;
while(NULL !=head->next)
{
i++;
head=head->next;
}
printf("the length of the linked list is:%d\n",i+1);
return(i+1);
}
/* 在单链表的指定位置插入新的节点 */
int Insert_Node(struct student *head)
{
int i=0;
int j=1;
struct student *p1=head;
struct student *new;
printf("please input the position which inserted:");
scanf("%d",&i);
while((j<i) && (p1 !=NULL)) /*当前节点不为NULL,且输入插入节点的位置存在,然后遍历到该位置执行下面else里面的语句 */
{
j++;
p1=p1->next;
}
if(p1 == NULL)
{
return 0;
}
else
{
new=(struct student *)malloc(sizeof(struct student));
printf("the data which inserted are:");
scanf("%d,%d",&new->number,&new->score);
new->next=p1->next; /*new->next指向要插入那个位置的下一节点,这样就将要插入的节点的前一节点后一节点断开了。*/
p1->next= new; /*要插入的的节点的前一节点的p1->next指向要插入的节点的地址new.*/
return 0;
}
printf("向链表插入一个节点后的数据输出为:");
}
/* 删除指定的某一个节点 */
int DeleteNode(struct student *head)
{
int i;
int j=1;
struct student *p1;
struct student *p2;
p2=head;
printf("please input the node that you will delete :");
scanf("%d",&i);
printf("删除原链表中的一个节点后,新链表各个节点的数据输出为:\n");
while((j<i-1) && (p2 != NULL)) /* 找到要删除的节点*/
{
j++;
p2=p2->next;
}
if(p2==NULL)
{
return 0;
}
else
{
p1=p2->next; /* p1指向了要删除的节点 */
p2->next=p1->next; /* 删除节点 */
free(p1); /*释放删除节点的内存*/
return 0;
}
}
int main(int argc,char**argv)
{
struct student *head;
struct student *phead;
head= Createlist() ;
ListLength(head);
DeleteNode(head);
print(head);
Insert_Node(head);
print(head);
phead=inverse_list(head);
print(phead);
DestroyList(phead);
return 0;
}
运行结果:
更新:
下面是循环链表的创建,输出数据,插入节点,删除节点基本操作。其实循环链表就是将最后一个节点指向的指针指向了头结点,而不是NULL,所以
循环链表的操作和普通的单链表操作差不多,不同的是循环语句判断改变了。看实例:
1.链表创建函数
struct student *Createlist()
{
struct student *head; /*这里的head是指带数据的第一个节点。*/
struct student *new; /*new保存创建的新节点的地址 */
struct student *tail; /*tail保存原链表最后一个节点的地址 */
n=1; /*创建前链表的节点总数为0:空链表 */
new=(struct student*)malloc(sizeof(struct student)); /* 分配的内存给第一个节点 */
if(new==NULL)
{
printf("distrubute memory failed!\n");
return NULL;
}
else
{
printf("向第%d个节点输入数据:",n );
scanf("%d,%d",&(new->number),&(new->score)); /* 给第一个节点输入数据 */
}
while(new->number!=0)
{
if(1==n) /*创建第一个节点链表*/
{
head=new;
tail=head;
tail->next=head;
}
else /* 创建第二个节点,第三个节点...的链表 */
{
new->next=head;
tail->next=new;
tail=new;
}
n++;
new=(struct student*)malloc(sizeof(struct student));/* 分配的内存给第二个,第三个节点... */
printf("向第%d个节点输入数据:",n);
scanf("%d,%d",&(new->number),&(new->score)); /* 给第二个,第三个节点...输入数据 */
}
return head;
}
与之前不同的是tail->next指向了head,而不是NULL,新的节点的指针new->next指向了head,不是NULL。
2.链表插入一个节点
int Insert_Node(struct student *head)
{
int i=0;
int j=1;
struct student *p1=head;
struct student *new;
printf("please input the position which inserted:");
scanf("%d",&i);
while((j<i) && (p1->next !=head))
{
j++;
p1=p1->next;
}
new=(struct student *)malloc(sizeof(struct student));
printf("the data which inserted are:");
scanf("%d,%d",&new->number,&new->score);
new->next=p1->next;
p1->next= new;
return 0;
printf("向链表插入一个节点后的数据输出为:");
}
与单链表操作不同之处在于while()循环语句有p1!=NULL变为p1->next!=head,且没有了if...else的判断语句。
3.链表删除一个节点
int DeleteNode(struct student *head)
{
int i;
int j=1;
struct student *p1;
struct student *p2;
p2=head;
printf("please input the node that you will delete :");
scanf("%d",&i);
printf("删除原链表中的一个节点后,新链表各个节点的数据输出为:\n");
while((j<i-1) && (p2->next != head)) /* 找到要删除的节点*/
{
j++;
p2=p2->next;
}
if(p2->next==head)
{
return 0;
}
else
{
p1=p2->next; /* p1指向了要删除的节点 */
p2->next=p1->next; /* 删除节点 */
free(p1);
return 0;
}
}
循环语句while()里面由之前的p!=NULL变为p2->next!=head,if()语句里面由p2==NULL变为p2->next==head。
不过这里头结点是不能删除的。只能删除头结点后面的节点。准确来说循环链表中是没有头节点的说法,但是这里为了叙述方便,
才用头结点来表达。
4.输出各个节点中的数据
void print(struct student*head)
{
struct student *temp;
int i=1;
temp=head->next;
printf("第%d个节点的数据是:%d,%d\n",i,head->number,head->score);
while(temp !=head)
{
i++;
printf("第%d个节点的数据是:%d,%d\n",i,temp->number,temp->score);
temp=temp->next;
}
}
循环语句中指针由指向NULL变为了指向head。
5.销毁链表
void DestroyList(struct student *head)
{
struct student *ptr;
ptr=head->next;
printf("销毁链表后各个节点的数据输出:\n");
while(head !=ptr)
{
free(ptr);
printf("%d,%d\n",ptr->number,ptr->score);
ptr=ptr->next;
}
free(head);
}