链表基本操作

复习一次链表的基本操作
网上找到一位大佬的代码
重新学习链表
跟着他的代码重新打了一遍
受益匪浅

最后在测试阶段采用文件重定向的方式
避免多次输入数据

#include <bits/stdc++.h>
using namespace std;

struct Student
{
	int num;//学号 
	double score;//分数,其他信息可以继续在下面增加 
	struct Student *next;//指向下一节点的指针 
};

int n;//节点总数

/*
========================= 
功能:创建n个节点的链表
返回:指向链表表头的指针 
========================= 
*/ 
struct Student * Create()
{
	struct Student *head;//头节点 
	struct Student *p1=NULL;//p1保存创建的新节点的地址 
	struct Student *p2=NULL;//p2保存原链表最后一个节点的地址 
	n=0;//创建前为空链表 
	p1=( struct Student *)malloc( sizeof(struct Student) );//开辟一个新节点 
	p2=p1; //如果开辟成功,p2把它的指针保存下来以备后用 
	if(p1==NULL)//节点开辟失败 
	{
		cout << "\nCann't create it, try it again in a moment!\n";
		return NULL;
	}
	else//节点开辟成功 
	{
		head=NULL;//开始head指向NULL 
		cout << "Please input " << n+1 << " node -- num,score: \n";
		cin >> p1->num >> p1->score ;
	} 
	while(p1->num !=0)//只要学号不为0,就继续录入下一节点 
	{
		n++;//节点总数增加一个 
		if(n==1)//如果节点总数是1,则head指向刚创建的节点p1 
		{
			head=p1;
			p2->next =NULL;//此时p2和p1相同,也就是让p1-next指向NULL 
		}
		else
		{
			p2->next =p1;//指向刚刚开辟的新节点 
		}
		p2=p1;//把p1的地址给p2保留,然后p1开辟新节点 
		p1=(struct Student *)malloc( sizeof(struct Student) );
		cout << "Please input " << n+1 << " node -- num,score: \n";
		cin >> p1->num >> p1->score ;
	} 
	p2->next =NULL;//单向链表最后一个节点指向NULL 
	free(p1);//释放p1 
	p1->next =NULL;//将清空的变量置NULL 
	return head;//返回创建链表的头指针 
} 

/*
=========================
功能:输出节点
返回:void 
========================= 
*/ 
void Print(struct Student *head)
{
	struct Student *p;
	cout << "\nNow , These " << n << " records are:\n";
	p=head;
	if(head!=NULL)//只要链表非空,就输出链表中所有节点 
	{
		cout << "head is " << head << "\n";//输出头节点指向地址 
		do
		{
			cout << p << " " << p->num << " "<< p->score << " "<< p->next << endl;
			//依次输出当前节点地址  字段值  下一节点地址 
			p=p->next ;//移动到下一节点 
		}
		while(p!=NULL);
	}
}

/*
=========================
功能:删除指定节点(此例中是删除指定学号的节点)
返回:指向链表表头的指针 
=========================
*/
struct Student *Del(struct Student *head,int num)
{
	struct Student *p1;//p1保存当前需要检查的节点地址 
	struct Student *p2;//p2保存当前检查过的节点地址 
	if(head==NULL)//若是空链表 
	{
		cout << "\nList is NULL!\n" ;
		return head;
	}
	//定位要删除的节点 
	p1=head;
	while(p1->num != num && p1->next != NULL)//p1的节点不是要删除的,且不是最后一个节点 
	{
		p2=p1;//保存当前节点地址 
		p1=p1->next ;//后移一个节点 
	}
	if(p1->num == num)//找到要删除的节点 
	{
		if(p1==head)//若是头节点 
		{
			head=p1->next ;
		} 
		else//若是其他节点 
		{
			p2->next = p1->next ;
		}
		free(p1);//释放当前节点 
		p1=NULL;//将清空的变量置NULL 
		cout << "\ndelete " << num << " success!\n";
		n--;//节点总数减一 
	}
	else//没有找到 
	{
		cout << "\n" << num << " not been found!\n";
	}
	return head;
}

/*
=========================
功能:插入指定节点的后面(此例是指定学号的节点)
返回:指向链表表头的指针  
=========================
*/
struct Student *Insert(struct Student *head,int num,struct Student *node)
{
	struct Student *p1;//p1保存当前需要检查的节点的地址 
	if(head==NULL)//若为空链表 
	{
		head=node;
		node->next =NULL;
		n++;
		return head;
	}
	p1=head;
	while(p1->num != num && p1->next != NULL)//p1不是我们要查找的节点,并且它不是最后一个节点 
	{
		p1=p1->next ;//p1后移 
	}
	if(p1->num == num)//若找到了节点 
	{
		node->next = p1->next ;
		p1->next = node;
		n++;
	}
	else//若找不到节点 
	{
		cout << "\n" << num << " not been found!\n";
	}
	return head;
} 

/*
=========================
功能:反序节点
(链表的头变成尾,尾变成头)
返回:指向链表表头的指针 
=========================
*/
struct Student *Reverse(struct Student *head)
{
	struct Student *p;//临时存储 
	struct Student *p1;//存储返回结果 
	struct Student *p2;//源结果节点一个个地取 
	p1=NULL;//开始颠倒时,已颠倒部分为空 
	p2=head;//p2指向链表头节点 
	while(p2!=NULL)
	{
		p=p2->next ;//临时存储p2的next节点 
		p2->next =p1;//然后改变p2的next节点 
		p1=p2;//用p2赋值给p1 
		p2=p;//p2后移到原链表的下一位置 
	}
	head=p1;
	return head;
}

/*
=========================
功能:直接插入排序(从小到大)
返回:指向链表表头的指针 
=========================
*/
struct Student *InsertSort(struct Student *head)
{
	struct Student *first;//为原链表剩下用于直接插入排序的节点头指针 
	struct Student *t;//临时指针变量,插入节点 
	struct Student *p,*q;//临时指针变量 
	first=head->next ; 
	head->next =NULL;//只含一个节点的有序链表 
	//整个链表相当于变成时两个链表 
	while(first != NULL)//遍历剩下的无序链表 
	{
		for(t=first,q=head,p=NULL;(q != NULL) && (q->num < t->num );p=q,q=q->next );
		/*
		让q从头开始
		当q不指向NULL 且 q->num < t ->num
		p=q,q后移 
		最后p,q定位到t需要插入的前后位置 
		*/ 
		first=first->next ;//first后移 
		if(q==head)
		{
			head=t;
		}
		else
		{
			p->next =t;
		}
		t->next =q;
	}
	return head;
}

/*
=========================
功能:冒泡排序(从小到大)
返回:指向链表表头的指针
========================= 
*/ 
struct Student *BubbleSort(struct Student *head)
{
	struct Student *endpt;//控制循环比较 
	struct Student *p;//临时指针变量 
	struct Student *p1,*p2;
	p1=(struct Student *)malloc( sizeof(struct Student) );//开辟新节点 
	p1->next =head;//我们增加一个节点,放在第一个节点前面
	//主要是为了便于比较,因为第一个节点没有前驱,不能交换地址 
	head=p1;//让head指向p1,排序完成后再释放掉 
	for(endpt=NULL;endpt!=head;endpt=p)//从后向前,这样后面的节点都是排序好的 
	{
		for(p=p1=head;p1->next ->next != endpt;p1=p1->next )
		{
			if(p1->next ->num >p1->next ->next ->num)
			{
				p2=p1->next ->next;//完成两节点的交换 
				p1->next ->next =p2->next ;
				p2->next =p1->next ;
				p1->next =p2;
				p=p1->next ->next ;
			}
		}
	}
	p1=head;
	head=head->next ;
	free(p1);
	p1=NULL;
	return head;
} 

/*
=========================
功能:插入有序链表的某个节点后面(从小到大) 
返回:指向链表表头的指针 
=========================
*/
struct Student *SortInsert(struct Student *head,struct Student *node)
{
	struct Student *p;//p保存当前需要检查的节点的地址 
	struct Student *t;//临时指针变量 
	if(head==NULL)//处理空链表 
	{
		head=node;
		node->next =NULL;
		n++;//节点总数加一 
		return head;
	}
	p=head;//有序链表非空 
	while(p->num <node->num && p != NULL)//p的num小于node的num,且p不等于NULL 
	{
		t=p;//保存当前节点的前驱,以便后面处理 
		p=p->next ;//p后移一个节点 
	}
	if(p==head)//刚好插在第一个节点之前 
	{
		head=node;
	}
	else//插在其他节点之后 
	{
		t->next =node;
	}
	node->next =p;//加入node节点 
	n++;//节点总数加一 
	return head;
} 

/*
=========================
功能:销毁链表
返回:void
=========================
*/
void DestoryList(struct Student *head)
{
	struct Student *p;
	if(head==NULL)
	{
		return ;
	} 
	while(head)
	{
		p=head->next ;
		free(head);
		head=p;
	}
	return ;
}

int main()
{
	ifstream fin("input.txt"); // 已有输入文件
	streambuf *cinbackup; 
	cinbackup= cin.rdbuf(fin.rdbuf()); //用 rdbuf() 重新定向
	/*
	如果不用文件重定向
	就把上面这几行及倒数第二行cin.rdbuf(cinbackup);
	删除即可
	*/
	
	struct Student *head,*stu;
	int Num;
	
	//测试Create,Print 
	head=Create();
	Print(head);
	
	//测试Del
	cout << "\nWhich one delete: ";
	cin >> Num;
	head=Del(head,Num);
	Print(head);
	
	//测试Insert
	stu=(struct Student *)malloc(sizeof(struct Student)); 
	cout << "\nPlease input the insert node -- num,score";
	cin >> stu->num >> stu->score ;
	cout << "\nInsert behind num: ";
	cin >> Num;
	head=Insert(head,Num,stu);
	Print(head);
	
	//测试Reverse
	cout << "\nReverse the linklist: \n";
	head=Reverse(head);
	Print(head);
	
	//测试InsertSort
	cout << "\nInsertSort the linklist: \n";
	head=InsertSort(head);
	Print(head);
	
	//测试BubbleSort
	cout << "\nBubbleSort the linklist: \n";
	head=BubbleSort(head);
	Print(head);
	
	//测试SortInsert
	cout << "\nSortInsert the linklist: \n";
	stu=(struct Student *)malloc(sizeof(struct Student)); 
	cout << "\nPlease input the insert node -- num,score";
	cin >> stu->num >> stu->score ;	 
	head=SortInsert(head,stu);
	Print(head);
	
	//销毁链表 
	DestoryList(head);
	
	cin.rdbuf(cinbackup); // 取消,恢复键盘输入

	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值