本文章主要是记述我对单向链表的理解:1.有头节点和无头结点链表的理解 2.有头结点链表的创建 ,显示,插入,删除,冒泡排序,逆置。
我对链表的理解就是将一个个结构体串联起来。一个个结构体在一个链表中就是一个节点,节点之间是由结构体指针联系起来的,也就是他们结构体中会包含一个同类型的*next指针,用来指向下一个节点的地址,这个就是节点的指针域,里面其他类型的数据就是节点的数据域。 头结点就是数据域为空(0),指针域是指向一个有效数据的一个结构体,它是一个节点,由malloc跟它分配了内存。
下面我用代码来分析:
首先创建一个结构体
typedef struct list
{
int data;//数据域
struct list *next;//指针域
}node;
下面就是各个功能函数
1.创建一个带头结点的链表:
node *creat_link_list(int n)
{
node *head,*p,*pre;
int i;
head=(node*)malloc(sizeof(node));//创建一个头结点
head->next=NULL;
pre=head;//用一个指针来指向这个头结点,这个指针是各个结点连接起来的关键
for(i=0;i<n;i++)
{
p=(node*)malloc(sizeof(node));//创建我们需要的数据结点
printf("input the %d data\n",i+1);
scanf("%d",&p->data);
pre->next=p;//将pre指向的结点和数据结点连接起来
pre=p;//再把第一个数据结点的位置给pre指针
}//如此循环,直到创建完你需要的结点
p->next=NULL; //然后将最后一个数据结点指向null,结束此单向链表
return head;//返回头结点的地址
}
2.显示链表的数据
void printlist(node *head)
{
node *p;
p=head->next;//因为我们是有头结点的链表,所以head头结点的数据域为0,我们不需要,所以得把它的next地址给它
int i=1;
while(p)
{
printf("the (%d)data= %d\n",i,p->data);
i++;
p=p->next;
}
}
按照上面的程序,一直打印出各个结点的信息(除了头结点的),直到我们的链表结束,null。
3.链表插入结点(这个函数的功能是插在某个结点的后面)
void insertlist(node *head,int i,int is_data,int n)
{
node *p,*q;
int j=0;
p=head;
while(j<i)
{
p=p->next; //遍历这个链表,找到你需要插的那个结点,第一个数据结点的下标我给的1,所以才是j=0,j<i这样设置遍历条件,大家慢慢体会,也可以自己画图,如果是想在头结点后面插一个数据,则i就输入0。
j++;
}
q=(node *)malloc(sizeof(node));//创建我们要插入的结点
q->data=is_data;
q->next=p->next;//找到位置后将这个结点的next给我们创建的结点,然后再将这个位置结点的next指向我们创建的结点
p->next=q;
}
3.链表删除结点:
void delietelist(node *head,int i)
{
node *p,*q;
int j=0;
p=head;
while(j<i-1)//我们还是进行遍历,找到我们需要删除的结点的前一个结点位置,当然这里我们不能删除头结点,跟插入一样第一个数据结点我给的下标还是1,所以你想删除第一个数据结点的时候就只需要输入i=1
{
p=p->next;
j++;
}
q=p->next;
p->next=q->next;
free(q);
}
这里为什么要找到删除结点的前一个位子呢?因为我们删了这个结点,但是还需要把这个结点的前后连接起来,组成一个链表,不然这里就断了,而且这个链表是单向的。
结点的删除很简单,只不过需要注意的是我们必须得把要删除的结点free掉,不然会造成内存泄露
4.链表的冒泡排序(按从小到大)
node * sort(node *head)
{
node *p,*q;
int temp;
p=head->next;
for(;p->next!=NULL;p=p->next)
{
for(q=p->next;q!=NULL;q=q->next)
{
if(p->data>q->data)
{
temp=q->data;
q->data=p->data;
p->data=temp;
}
}
}
return head;
}
这个没什么说的,思路就是冒泡的思想,实现的时候只需要将数据换乘链表的形式即可。
思路:就是将所有数据进行比较,小的放前面,第一次是所有数据比,最小的放第1个,然后是除了第一个数据的后面的再比较,最小的放第2个,以此类推。n个数据比较,
最多是比较n(n-1)/2,最少比较0次 ps:10个数据比较,最多就是9+8+7+.....+1,通项公式是前面那个式子。
5.链表的逆置(逆置后我仍然把它做成一个带头结点的链表,所以给创建了一个新的头结点new_h,把以前的那个free掉了)
node * reverse_list(node *head)
{
node *p,*q,*r,*new_h;
r=NULL;
q=NULL;
new_h=(node*)malloc(sizeof(node));
p=head->next;
while(p)
{
q=p->next;
p->next=r;
r=p;
p=q;
}
head=NULL;
new_h->next=r;
return new_h;
}
q=p->next;
p->next=r;
r=p;
p=q;
第一次循环结束;
接下来继续循环
q=p->next;
p->next=r;
r=p;
p=q;
到最后逆置完所有数据
别忘记了我们还要插入头结点和将以前的那个头结点设置为null,这里有一点我改过的就是我以前将head结点free掉,现在是将其设置为null,如果是前者他们这个链表就没有null结束了,但是运行结果还是正确的,这里是我不懂的,反正2种方法最后运行还是可以的。
下面是我的main函数
int main(int argc,char *argv[])
{
node * head;
int i,data,n;
printf("please input the number of the data list: \n");
scanf("%d",&n);
head=creat_link_list(n);
printf("list elements:\n");
printlist(head);
printf("input the position of insert elements:\n");
scanf("%d",&i);
if(i>n)
{
printf("lager than the ranger\n");
exit(0);
}
else
printf("input data :\n");
scanf("%d",&data);
insertlist(head,i,data,n);
printf("list elements\n");
printlist(head);
printf("input the position of delete elements:\n");
scanf("%d",&i);
delietelist(head,i);
printlist(head);
printf("sort the list\n");
head=sort(head);
printlist(head);
printf("list the reverse list:\n");
head=reverse_list(head);
printlist(head);
return 0;
}
下面是运行结果:
程序比较简单,应该会有bug,只是为了简单的学习一点链表。