一.链表
链表节点分为数据域和指针域
数据域进行数据的存储
指针域用于存放下一个节点的首地址
链表就是把结构体连接起来
二.单向链表的操作
2.1头插法
head 表示链表的头
new表示新插入的文件
头插法的目的就是把new插入在head前面
使用一段代码进行讲解
struct student *touChaFa(struct student *head)
{
struct student *phead;
phead=head;
struct student *new=NULL;
if(head==NULL){
head=new;
return head;
}else{
new->next=head;//让新节点的下一个节点指向头节点
head=new; //把新节点变成新节点
}
}
头插法就是讲的把
使用头插法的一段代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student
{
int score;
char *name;
int shuxue;
struct student *next;
};
void xianshi(struct student *head)
{
int i=4;
while(head!=NULL){
i--;
printf("%d学生的成绩%d 名字%d mingzi %s\n",i,head->score,head->shuxue,head->name);
head=head->next;
}
}
struct student *touChaFa(struct student *head)
{
int i=0;
int j;
printf("请输入你想创建的节点\n");
scanf("%d",&j);
while(i<j){
i++;
struct student *newNode;
newNode=(struct student *)malloc(sizeof(struct student));
newNode->next=NULL;
printf("请输入第%d个节点的内容\n",i);
scanf("%d",&(newNode->score));
printf("shuxue :");
scanf("%d",&newNode->shuxue);
printf("name :");
newNode->name=(char *)malloc(128);
scanf("%s",newNode->name);
if(head==NULL){
head=newNode;
}else{
newNode->next=head;
head=newNode;
}
}
return head;
}
int main()
{
struct student *head=NULL;
head=touChaFa(head);
xianshi(head);
return 0;
}
容易犯错的地方是在输入字符串的时候要对结构体的字符串进行重新空间赋值
newNode->name =(char *)malloc(128);
开辟出来128字节的空间以后才能进行操作
scanf("%s",newNode->name);
2.2尾插法
核心部分:*********************
struct student *phead;
......
if(head==NULL){
head=newNode;
}else{
phead=head;
while(phead!=NULL){
phead=phead->next;
}
phead->next=newNode;
}
2.3链表的释放
定义一个新的节点q指向p,p节点指向下一个节点,然后free掉q,以此类推,直到p的下一个节点为NULL
free函数释放的是malloc或者calloc开辟的空间,不是两者开辟的空间无法进行释放
struct student* link_free(struct student *head)
{
struct student *qhead=NULL;
while(head!=NULL){
//qhead指向head头节点
qhead=head;
//指向他的下一个节点
head=head->next;
//释放该节点
free(qhead);
//防止出现野指针,定义为NULL
qhead=NULL;
}
return head;
}
2.4链表的查找
struct student* link_find(struct student *head)
{
int numshuxue;
struct student *phead=head;
printf("请输入查找的数学:");
scanf("%d",&numshuxue);
while(phead!=NULL){
if(phead->shuxue==numshuxue){
return phead;
}else{
phead=phead->next;
}
}
}
2.5查找链表中的数字 实现在前面插入和在后面插入
2.5.1首先在后面插入(比较简单)
p -> p1 -> p2 -> p3
1 2 3 4
在3后面插入 我们现在知道 的是3这个点 因此判断 p当前的值是不是等于3
如果等于三 那么 让
new->next=p->next;
p->next=new->next;
具体代码
int findNode(struct student *head,int data,struct student *findNode)
{
struct student *phead=head;
while(phead!=NULL){
if(phead->score==data){
findNode->next=head->next;
head->next=findNode;
return 1;
}
}
return 0;
}
在主函数调用函数
struct student new={4,NULL};
int i=findNode(head,4,&new);
2.5.2在前面插入 (类似)
p -> p1 -> p2 -> p3 -> p4
1 2 3 4 5
假设要在p4前面插入
核心代码
struct student *findNodes(struct student *head,int data,struct student *newNode)
{
struct student *phead=head;
if(head->score==data){
new->next=head->next;
head=new;
return head;
}
while(phead->next != NULL){
if(phead->next->score==data){
newNode->next=phead->next;
phead->next=newNode;
return head;
}
phead=phead->next;
}
return NULL;
}
2.6删除节点
核心代码
struct student *delate(struct student *head,int data)
{
struct student *phead;
phead=head;
if(head->score==data){
head=head->next;
return head;
}
while(phead->next!=NULL){
if(phead->next->score==data){
phead->next=phead->next->next;
return head;
}
phead=phead->next;
}
return head;
}
2.7链表的排序
struct student * paixu(struct student *head)
{
struct student *phead =head;
struct student *pheadnext=NULL;
struct student temp;/* 如果使用的是指针需要进行开辟空间,不然会产生段错误 */
if(phead->next==NULL){
return phead;
}else{
while(phead->next != NULL){
/*pf head pb phead*/
pheadnext=phead->next;
while(pheadnext!=NULL)
{
/* 若后一个节点大于前一个的大小 */
if(pheadnext->score < phead->score)
{
/* 进行数据的变换 */
temp = *pheadnext;
*pheadnext = *phead;
*phead = temp;
/*当前temp相当于是phead的地址 */
/*进行指针域的变化*/
temp.next = pheadnext->next;//等于头节点的下一个结点
pheadnext->next = phead->next;
phead->next = temp.next;
}
pheadnext=pheadnext->next;
}
phead=phead->next;
}
}
return head;
}
2.8链表逆序
【5】 -> 【3】 -> 【2】 ->【4】->NULL
phead pheadnext
逆序
【3】 -> 【5】 【2】 -> 【4】->NULL
phead pheadnext
temp
【2】 -> 【3】 ->【5】 【4】 ->NULL
phead pheadnext
temp
【4】 -> 【2】 ->【3】 -> 【5】 ->NULL
phead pheadnext
temp
思路首先将3移动到5的前面,然后将2移动到上次移动的3的前面,以此类推
phead =head;
pheadnext =head->next;
while(phead!=NULL){
temp=pheadnext->next;
pheadnext->next=phead;//指向头部
phead=pheadnext; //始终保持头部是转移过来的
pheadnext=temp; //相当于执行一次之后后移一个
}
struct student * link_backword(struct student *head)
{
struct student *phead=head;
printf("phead 地址%p\n",phead);
struct student *pheadnext=NULL;
struct student *temp =NULL;
pheadnext=phead->next;
if(phead==NULL){
printf("链表为空\n");
return NULL;
}
if(phead->next==NULL){
printf("链表只有一个成员,不需要进行逆序\n");
return phead;
}
while(pheadnext!=NULL){
/*5 2 3 4 NULL*/
/* 让temp指向下下节点 3 */
temp=pheadnext->next;
/*2的下一个节点 指向 5 */
pheadnext->next=phead;
/*指向新的 2*/
phead=pheadnext;
/* 变成3 */
pheadnext =temp;
}
printf("phead 地址%p\n",head);
printf("phead 地址%p\n",phead);
head->next=NULL;
head = phead;
return head;
}
struct student
{
int score; // 4 0
char *name; // 8 4+4
int shuxue; // 4 16
struct student *next; //8 20 28 不是8的最小倍数 因此是32
};
结构体的大小是32个字节
phead 地址0x559dc3a9cc00
phead 地址0x559dc3a9cc00
phead 地址0x559dc3a9ca80
地址相差384
384/32=12
三.双向链表
双向链表的指针一个指向前一个节点的地址,另一个指向后一个链表的地址
3.1头插法
struct student *newNode;
newNode=(struct student *)malloc(sizeof(struct student));
newNode->next=NULL;
newNode->front=NULL;
printf("请输入第%d个节点的内容\n",i);
scanf("%d",&(newNode->score));
printf("shuxue :");
scanf("%d",&newNode->shuxue);
printf("name :");
newNode->name=(char *)malloc(128);
getchar();
fgets(newNode->name,10,stdin);
if(head==NULL){
head=newNode; /*加入第一个节点为NULL,直接将新节点插入在该点*/
}else{
head->front=newNode;/*head ->front = new head->next=NULL*/
newNode->next=head;/*new ->next=head new->front=NULL*/
head=newNode;
}
}
return head;
}
3.2尾插法
if(phead==NULL){
phead=newNode;
head=phead;
}else{
phead=head;
while(phead->next!=NULL)
{
phead=phead->next;
}
phead->next=newNode;
newNode->front=phead;
}
3.3 链表的双向显示
if(head==NULL){
printf("head=NULL;\n");
}
while(head!=NULL){
i--;
printf("%d学生的成绩%d 数学%d 名字 %s\n",i,head->score,head->shuxue,head->name);
if(head->next==NULL){
phead=head;
}
head=head->next;
}
printf("head =%p\n",head);
printf("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
while(phead!=NULL){
printf("学生的成绩%d 数学%d 名字 %s\n",phead->score,phead->shuxue,phead->name);
phead=phead->front;
}
3.4在双向链表的某一个节点后插入节点
int charu(struct student*head,int i)
{
struct student *newNode=NULL;
newNode=(struct student*)malloc(sizeof(struct student));
printf("请输入第%d个节点的内容\n",i);
scanf("%d",&(newNode->score));
printf("shuxue :");
scanf("%d",&newNode->shuxue);
printf("name :");
newNode->name=(char *)malloc(128);
getchar();
fgets(newNode->name,10,stdin);
while(head!=NULL)
{
if(head->shuxue==i)
{
if(head->next!=NULL){
newNode->next=head->next;
newNode->front=head;
head->next->front=newNode;
head->next=newNode;
return 1;
}else{
head->next=newNode;
newNode->front=head;
return 1;
}
}else{
head=head->next;
}
}
return 0;
}
删除一个节点后面的节点
struct student * delete_node(struct student *head,int x)
{
struct student *phead=head;
struct student *delete_nodex=NULL;
if(phead->shuxue==x) //删除头节点
{
head=head->next;
head->front =NULL;
free(head->front);
return head;
}
while(phead->next!=NULL)
{
if(phead->next->shuxue==x)
{
if(phead->next->next==NULL)
{
free(phead->next);//释放free的下一个节点
phead->next=NULL;//指向NULL
return head;
}
else
{
delete_nodex=phead->next;
phead->next->next->front=phead;//
phead->next=phead->next->next;
free(delete_nodex);
return head;
}
}
phead=phead->next;
}
return NULL;
}
四.循环单向链表
链表的最后一个节点指向链表的第一个节点的指针
遍历一个链表,直到达到同一节点,循环单链表类似于链表,但是他没有开始也没有结束,任何节点的下一个节点都不能是NULL
4.1循环单向链表
struct student *touChaFa(struct student *head)
{
int i=0;
int j;
printf("请输入你想创建的节点\n");
scanf("%d",&j);
struct student *phead=NULL;
while(i<j){
i++;
struct student *newNode=NULL;
newNode=(struct student *)malloc(sizeof(struct student));
newNode->next=NULL;
printf("请输入第%d个节点的内容\n",i);
scanf("%d",&(newNode->score));
printf("shuxue :");
scanf("%d",&newNode->shuxue);
printf("name :");
newNode->name=(char *)malloc(128);
getchar();
fgets(newNode->name,10,stdin);
if(head==NULL){
phead=newNode;//尾巴
head=newNode;
}else{
newNode->next=head;
head=newNode;
}
}
phead->next=head;//现在表示的是尾节点 指向的是NULL
return head;
}
4.2循环单向链表的显示
void xianshi(struct student *head)
{
int i=4;
struct student *phead = head;
printf("head =%p\n",head);
if(head==NULL){
printf("head=NULL;\n");
}
if(head->next==phead) //只有一个节点的时候
{
printf("%d学生的成绩%d 数学%d 名字 %s\n",1,head->score,head->shuxue,head->name);
}
else
{
while(phead->next!=head){//多个节点的时候
i--;
printf("%d学生的成绩%d 数学%d 名字 %s\n",i,phead->score,phead->shuxue,phead->name);
phead=phead->next;
}
printf("%d学生的成绩%d 数学%d 名字 %s\n",i,phead->score,phead->shuxue,phead->name);
}
printf("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
}
4.3在开头插入节点
struct student *front_insertPoint(struct student *head)
{
//判断是不是只有一个节点
struct student *insertPoint=NULL;
struct student *phead=head;
insertPoint = (struct student *)malloc(sizeof(struct student));
insertPoint->next=NULL;
printf("请输入节点的内容\n");
scanf("%d",&(insertPoint->score));
printf("shuxue :");
scanf("%d",&insertPoint->shuxue);
printf("name :");
insertPoint->name=(char *)malloc(128);
getchar();
fgets(insertPoint->name,10,stdin);
if(head->next == head)
{
insertPoint->next = head;
head->next=insertPoint;
head=insertPoint;
return head;
}
while(phead->next != head)
{
phead=phead->next;
}
phead->next = insertPoint;
insertPoint->next = head;
head=insertPoint;
return head;
}
4.4在末尾插入节点
if(head->next == head)
{
insertPoint->next = head;
head->next=insertPoint;
return head;
}
while(phead->next != head)
{
phead=phead->next;
}
phead->next = insertPoint;
insertPoint->next = head;
return head;
}
4.5删除头节点或者尾节点
//x 等于1的时候表示删除头节点 等于0的时候 表示删除尾节点
struct student *delete_point(struct student *head,int x)
{
struct student *phead = head;
struct student *pheadxxx=NULL;
if(head==NULL)
{
printf("没有节点\n");
return NULL;
}
if(head->next==head)
{
printf("只有一个节点\n");
head=NULL;
free(head);
return head;
}
while(phead->next!=head)
{
if(phead->next->next==head || x==0)
{
pheadxxx=phead;
}
phead=phead->next;
}
if(x==1){
printf("删除的是头部节点\n");
phead->next = head->next;
free(head);
head=phead->next;
}
else
{
printf("删除的是尾部节点\n");
free(pheadxxx->next);
pheadxxx->next=head;
}
return head;
}
五.循环双向链表
5.1双向循环链表的创建
struct student *touChaFa(struct student *head)
{
int i=0;
int j;
printf("请输入你想创建的节点\n");
scanf("%d",&j);
struct student *phead=NULL;
while(i<j){
i++;
struct student *newNode=NULL;
newNode=(struct student *)malloc(sizeof(struct student));
newNode->next=NULL;
newNode->front=NULL;
printf("请输入第%d个节点的内容\n",i);
scanf("%d",&(newNode->score));
printf("shuxue :");
scanf("%d",&newNode->shuxue);
printf("name :");
newNode->name=(char *)malloc(128);
getchar();
fgets(newNode->name,10,stdin);
if(head==NULL){
phead=newNode; //尾巴
head=newNode;
}else{
newNode->next=head;
head->front = newNode;
head=newNode;
}
}
head->front = phead; //最后让头的上一个节点指向尾巴
phead->next = head; //phead表示的是尾节点 现在让尾节点的下一个节点指向头节点
return head;
}
5.2循环双向链表的显示
void xianshi(struct student *head)
{
int i=4;
struct student *phead = head;
struct student *phead2 = head;
printf("head =%p\n",head);
if(head==NULL){
printf("head=NULL;\n");
}
if(head->next==phead) //只有一个节点的时候
{
printf("%d学生的成绩%d 数学%d 名字 %s\n",1,head->score,head->shuxue,head->name);
}
else
{
while(phead->next!=head){//多个节点的时候
i--;
printf("%d学生的成绩%d 数学%d 名字 %s\n",i,phead->score,phead->shuxue,phead->name);
phead=phead->next;
}
printf("%d学生的成绩%d 数学%d 名字 %s\n",i,phead->score,phead->shuxue,phead->name);
}
struct student *tailPoint =phead;//当前tailpoint表示的尾节点
printf("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
if(head->front == phead2)
{
printf("%d学生的成绩%d 数学%d 名字 %s\n",1,head->score,head->shuxue,head->name);
}
else
{
while(phead2->front!=head)
{
phead2=phead2->front;
printf("%d学生的成绩%d 数学%d 名字 %s\n",2,phead2->score,phead2->shuxue,phead2->name);
}
phead2=phead2->front;
printf("xxxxxxx学生的成绩%d 数学%d 名字 %s\n",phead2->score,phead2->shuxue,phead2->name);
}
printf("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
if(tailPoint->front == phead)//检测是不是同一个节点
{
printf("%d学生的成绩%d 数学%d 名字 %s\n",3,head->score,head->shuxue,head->name);
}
else
{
while(tailPoint->front != phead)
{
printf("%d学生的成绩%d 数学%d 名字 %s\n",4,tailPoint->score,tailPoint->shuxue,tailPoint->name);
tailPoint=tailPoint->front;
}
printf("jj学生的成绩%d 数学%d 名字 %s\n",tailPoint->score,tailPoint->shuxue,tailPoint->name);
}
printf("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
}
5.3双向链表的头节点插入 和尾节点插入
//i==1的时候表示添加头节点,i==0的时候表示添加尾节点
struct student *next_insertPoint(struct student *head,int i)
{
//判断是不是只有一个节点
struct student *insertPoint=NULL;
struct student *phead=head;
insertPoint = (struct student *)malloc(sizeof(struct student));
insertPoint->next=NULL;
insertPoint->front=NULL;
printf("请输入节点的内容\n");
scanf("%d",&(insertPoint->score));
printf("shuxue :");
scanf("%d",&insertPoint->shuxue);
printf("name :");
insertPoint->name=(char *)malloc(128);
getchar();
fgets(insertPoint->name,10,stdin);
if(head->next == head)
{
insertPoint->next = head;
insertPoint->front = head;
head->front = insertPoint;
head->next=insertPoint;
if(i==1) head = insertPoint;
return head;
}
while(phead->next != head)
{
phead=phead->next;
}
phead->next = insertPoint;
head->front = insertPoint;
insertPoint->next = head;
insertPoint->front = phead;
if(i==1) head=insertPoint;
return head;
}
5.4循环双向链表头节点删除或者尾节点删除
//x 等于1的时候表示删除头节点 等于0的时候 表示删除尾节点
struct student *delete_point(struct student *head,int x)
{
struct student *phead = head;
struct student *pheadxxx=NULL;
if(head==NULL)
{
printf("没有节点\n");
return NULL;
}
if(head->next==head)
{
printf("只有一个节点\n");
head=NULL;
free(head);
return head;
}
while(phead->next!=head)
{
if(phead->next->next==head || x==0)//尾巴的上一个节点 pheadxxx
{
pheadxxx=phead;
}
phead=phead->next;
}
if(x==1){
printf("删除的是头部节点\n");
phead->next = head->next; //尾节点的上一个节点等于头节点的下一个节点
head->next->front = phead;//头节点的上一个节点等于尾节点
free(head);
head=phead->next;
}
else
{
printf("删除的是尾部节点\n");
free(pheadxxx->next);
pheadxxx->next=head;
head->front=pheadxxx;
}
return head;
}