不带头结点的单链表
1.实验目的
1、熟练掌握动态链表结构及有关算法的设计方法。
2、理解不带头结点的单链表的特点,掌握其基本操作。
3、熟练掌握运用不带头结点链表表示特定形式的数据的方法,并设计出相关算法。
2.实验内容
1、用尾插法建立带头指针的单链表
2、打印
3、删除列表中所有奇数
4、有序表插入
5、两降序表合并成升序表
3.设计思路
首先通过尾插法或头插法创建一个不带头结点的单链表,然后基于链表完成打印、删除、插入、合并等操作,每次都要通过打印函数输出。
4.实验代码
#include <stdio.h>
#include <stdlib.h>
#define newNode (LinkedList *)malloc(sizeof(LinkedList))
typedef struct k
{
int data;
struct k *next;
} LinkedList;
/**********************************************/
//1.建表:尾插法
//函数功能:用尾插法建立带头指针的单链表
//函数参数:空
//函数返回值:指向LinkedList类型变量的指针head
//函数名:TailInsert()
/**********************************************/
LinkedList *TailInsert()
{
int x;
LinkedList *head, *p, *tail;
printf("input the node number(end with -1):");
scanf("%d",&x);
if(x==-1)
return NULL; //头指针的值为-1,直接返回
//以下的操作与头节点型的单链表相同
head=(LinkedList *)malloc(sizeof(LinkedList)); //创建头指针,为头指针开辟内存空间
head->data=x; //focus here! different here!要对第一个元素单独处理
head->next=NULL; //对next的各个域初始化
//向链表中添加元素
tail=head;
//focus here! different here!
scanf("%d",&x);
while(x!=-1)
{
//1.创建新节点
p=newNode; //p=(LinkedList *)malloc(sizeof(LinkedList))
//2.对p的各个域赋初值
p->data=x;
p->next=NULL;
//3.将p插入链表尾部,并将tail修改成新的表尾
tail->next=p;
tail=p;
//4.继续读一个值x
scanf("%d",&x);
}
return head;
}
/***************************************/
//2.打印输出
//函数功能:打印带头节点的单链表head
//函数参数:指向LinkedList类型变量的指针head
//函数返回值:
//函数名:
/***************************************/
void show(LinkedList *head) //打印带头节点的单链表head
{
LinkedList *p;
for(p=head; p!=NULL; p=p->next)
printf("%d ",p->data);
}
/*********************************************************************/
//3.删除列表中所有奇数
//函数功能:删除列表中所有奇数
//函数参数:指向LinkedList类型变量的指针head
//函数返回值:空
//函数名:delete_special()
/*********************************************************************/
LinkedList *delete_special(LinkedList *head)
{
/**************************************************************************************************/
//【方法1】
// 1、空表返回NULL;
//2、while(head不空 && head->data是奇数) head=head->next; //即滑过头部的所有奇数--直至偶数或结束
//3、if( head==NULL )return NULL;
//4、pre=head; p=head->next; //以pre(必定是偶数)为头结点,删除后面的所有奇数
/*
while( p!=NULL ){
if( p->data是奇数 ){ q=p; p=p->next; pre->next=p; free(q); }
else{ p=p->next; pre=pre->next; }
}
*/
//5、return head;
/**************************************************************************************************/
LinkedList *p,*pre, *q;
//若头指针型的单链表为空表,直接返回
if(head==NULL)
{
printf("\n单链表为空表!不需要做删除奇数的操作!");
return head;
}
//行至此处,头指针型的单链表不为空表
//若头指针的data值为奇数,则将头指针后移
while(head!=NULL && head->data%2==1)
head=head->next; //将head后移
//如果head移动至单链表最后
if(head==NULL)
{
printf("\n头指针型单链表中所有data值均为奇数!全部删除!");
return NULL;
}
//行至此处,head->data一定是偶数
//下面的操作与带头节点的单链表一致
//以pre为头结点,pre->data一定是偶数,删除后面的所有奇数
pre=head;
p=head->next;
while(p!=NULL)
{
if(p->data%2==1)
{
//1.将q贴在p上
q=p;
//2.将p与pre向后移
p=p->next;
pre->next=p;
//3.释放q的内存空间
free(q);
}
else
{
p=p->next;
pre=pre->next;
}
}
return head;
}
//4.有序表插入
//函数功能:将x插入升序表
//函数参数:指向LinkedList类型变量的指针head int类型变量x
//函数返回值:指向LinkedList类型变量的指针head
//函数名:insert_posList()
/************************************************************************/
LinkedList *insert_posList(LinkedList *head, int x)
{
//插入有可能改变头节点,所以要有返回值
LinkedList *p,*pre,*q;
//不管单链表的情形如何,都要插入q节点,先创建q
//为要插入的节点创建内存空间及赋初值
q=newNode;
q->data=x;
q->next=NULL;
//若头指针型的单链表为空表,直接返回q
if(head==NULL)
return q;
//行至此处,头指针型的单链表不为空表
//将q插在头指针前,即x<head->data时
if(x<head->data)
{
q->next=head; //将q指向head,q为新的头指针
return q; //返回新的单链表
}
pre=head;
p=head->next;
//对单链表进行扫描
while(p!=NULL && x>p->data)
{
p=p->next;
pre=pre->next;
}
//出循环,找到x的位置
//实施插入q在p与pre之间的操作
q->next=p;
pre->next=q;
return head;
}
/************************************************************/
//5.两降序序表合并成升序表
//函数功能:两降序序表合并成升序表
//函数参数:指向LinkedList类型变量的指针head1, head2, head 3
//函数返回值:指向LinkedList类型变量的指针head
//函数名:merge_List
/************************************************************/
LinkedList *merge_List(LinkedList *head1, LinkedList *head2) //不需要第三个参数head3,因为有返回值,返回值作为新的表头
{
//focus here!
//插入有可能改变头节点,所以要有返回值
LinkedList *r;
//新表初始化为空表(头指针型单链表)
LinkedList *head3;
head3=NULL; //将存放结果的链表初始化为空表
while(head1!=NULL && head2!=NULL)
{
if(head1->data > head2->data)
{
r=head1; //把r贴在q上面,选中r
head1=head1->next; //将head1的头指针后移
//用头插法创建head3
r->next=head3;
head3=r;
}
else
{
//head2=head2->next; //将head2的头指针后移
r=head2; //把r贴在p上面,选中r
head2=head2->next; // 把p向后移动
//用头插法创建head3
r->next=head3;
head3=r;
}
}
//一定会有一个表先扫描至表尾,对剩下的元素进行处理
while(head1!=NULL)
{
r=head1; //把r贴在q上面,选中r
head1=head1->next; // 把p向后移动
//用头插法创建head3
r->next=head3;
head3=r;
}
while(head2!=NULL)
{
r=head2; //把r贴在p上面,选中r
head2=head2->next; // 把p向后移动
//用头插法创建head3
r->next=head3;
head3=r;
}
return head3;
}
int main()
{
/**/
LinkedList *head;
//1.建表:尾插法
printf("请按升序输入:\n");
head=TailInsert();
//2.打印输出
printf("head= ");
show(head);
/**/
//3.删除列表中所有奇数
head=delete_special(head);
printf("\n\n删除所有奇数后, head= ");
show(head);
//4.有序表插入
int x;
head=insert_posList(head,1);
printf("\n\n有序表插入1后,head=");
show(head);
head=insert_posList(head,2);
printf("\n有序表插入2后,head=");
show(head);
head=insert_posList(head,6);
printf("\n有序表插入6后,head=");
show(head);
//5.两降序序表合并成升序表
LinkedList *head1, *head2, *head3;
//用尾插法创建head1
printf("\n\n下面请按照【降序】输入两有序表:\n");
head1=TailInsert(); head2=TailInsert();
printf("head1="); show(head1);
printf("\nhead2="); show(head2);
head3=merge_List(head1, head2);
printf("\n两降序表合并成升序表,head3=");
show(head3);
5.运行结果
6.实验总结(含心得体会)
通过本次实验,深入理解了如何创建一个不带头结点的单链表、打印一个单链表、删除列表中所有奇数、有序表插入、两降序表合并成升序表等操作。
本次实验通过理解不同算法的思想,实现了诸多不带头结点的单链表的系列操作,尽管开头有些难以理解,但是通过不断的练习和老师的讲解,我逐渐理解了算法的思想,以后遇到类似的问题要有耐心,不断地提升自己。
代码的注释都写得比较完整,若有不正确之处,或者有其他不同看法的朋友,请在评论区给作者留言。