单链表(带头节点)(附带简陋的成绩管理系统)

链表有一个很核心的思想操作:遍历

链表的插入,查找等等操作,都是需要遍历的  我之前最困惑的就是这一点,其他都还好

还有一点就是

指针是指针,内存块是内存块,释放内存并不会删除指针

指针仅仅只是保存地址

指针的问题就不细说

直接看代码吧

代码是在vs上写的 在devcpp上只需要把nullptr换成NULL就行了

简单演示

//带头结点单链表
//简单的演示单链表结构
//结合图例更好理解,更好写代码实现

#include<iostream>
using namespace std;
struct node				//链表节点
{
	string name;
	node* next = nullptr;
};
int main()
{
	node* head = nullptr;//计划使用堆
	head = new node;	 //为temp分配空间
	node* temp = head;	 //temp只是为了便于后面对链表的操作始于头节点

	temp->name = "q";	 //此时的temp就是 head
	temp->next = new node;//每次添加新的节点都需要分配空间
	temp = temp->next;	  //节点后移 此时的temp变成head->next 也就是head的下一个节点

	temp->name = "w";	  
	temp->next = new node;
	temp = temp->next;	  //节点后移 此时的temp保存head->next->next 也就是head的下下一个节点

	temp->name = "e";	  //16到25行代码是最朴实的链表初始化

	//插入节点
	//插入一个节点分两种情况 尾结点 以及非尾结点
	//在链表尾部插入节点时,只需要改变尾节点的next指针,然后置空插入节点的next指针
	//在链表非尾部插入一个节点时,改变插入位置节点的next指针,以及被插入节点的next指针
	string na;
	cout << "please input the node which you want insert:";
	cin >> na;
	cout << endl;
	temp = head;
	while (temp->name != na)
		temp = temp->next;
	
	node* newnode = new node;
	cout << "pleadse input name:";
	cin >> newnode->name;
	cout << endl;

	//非尾部插入  目前我写这个是存在问题的  因为要插入的那个位置可能是尾结点,这里只是简单示范一下 
	//在输入插入节点的时候不输入最后一个节点就行了
	newnode->next = temp->next;					//好好理解这一行代码,最好结合图来理解	
	temp->next = newnode;						
	/*temp->next = newnode;
	newnode->next = temp->next->next;*/			//注释掉的代码是新手特别容易犯的错误
												//按照正常人的逻辑 temp的next指针指向新节点 然后新节点的next指针 指向temp的下一节点
												//看上去还真是那么回事 但是 但是 你的temp->next=newnode;这一行代码 
												//是不是已经更改了temp->next的地址
												//我是优先处理靠后的节点也就是newnode->next来避免错误  思路不止一个,适合自己的,才是最好的
	

	//尾部插入
	temp = head;
	while (temp->next != nullptr)
		temp = temp->next;
	/*node* _newnode = new node;
	temp->next = _newnode;
	_newnode->next = nullptr;*/
	temp->next = new node;						//这一行代码等价于上面三行代码 当然置空为指针那一行可以省去
												//原因是我写的node节点已经置空了next指针

	cout << "pleadse input name(at end):";
	//cin >> _newnode->name;					 //启用注释的三行代码,就应该启用这一行
	cin >> temp->next->name;
	cout << endl;

	temp = head;								//头节点传给temp 使用temp对链表进行遍历
	while (temp != nullptr)                     //节点为空时,停止遍历
	{
		cout << temp->name << " ";
		temp = temp->next;                      //节点后移
	}

	return 0;
}

这是一个简易的成绩的管理系统(没有文件操作)

//手搓带头结点单链表    如果直接操作头指针的话,会导致头指针保存的地址发生变化,后面可能会导致代码报错(头指针可能指向尾节点的地址)
//主要结构是结构体
//temp每一次都暂时保存新节点地址
//关键思想是遍历
//按值在指定节点之后插入节点
//删除链表
//该源码可以用来后期写学生信息管理  开始只读 导出所有信息  然后对链表进行一系列操作  最后写入新链表(只在最后写,减少对写入函数的调用)

//之前是知道输入的age为0时间 才停止输入信息 然后是插入节点 
//接下来增加菜单,主动选择追加节点还是插入节点还是删除等等

//emmm 这个代码是改的之前的,函数返回值就不想改了(将就看吧,虽然也没有什么问题,但是我觉得它不够完美)
//emmm 有个问题
#include<iostream>
using namespace std;
struct node								//链表节点
{
	int num;
	string name;
	int age;
	node* next;
};

//功能函数
struct node* new_node(node* phead);							//添加节点的函数
void putout(node* phead);									//遍历输出链表
string insert_node(node* phead);								//在指定位置插入节点
void delete_node(node* phead);								//清空链表
int main()
{
	struct node* phead = new node;									//创建头节点
	phead->next = nullptr;
	int k = 0;
	while (true)
	{
		cout << "please choose function:" << endl;
		cout << "1. 在链表末尾插入节点" << endl;
		cout << "2. 在链表非尾部插入节点" << endl;
		cout << "3. 输出链表" << endl;
		cout << "4. 退出程序" << endl;
		cout << "5. 删除指定节点" << endl;
		cout << "6. 清屏" << endl;
		cin >> k;
		switch (k)
		{
		case 1:
			new_node(phead); break;
		case 2:
			cout << insert_node(phead) << endl; break;
		case 3:
			putout(phead); break;
		case 4:
			exit(0);
		case 5:
			delete_node(phead); break;
		case 6:
			system("cls"); break;
		default:
			cout << "error input please reinput." << endl; break;
		}
	}
	return 0;
}
node* new_node(node* phead)
{
	if (phead == nullptr) { phead = new node; phead->next = nullptr; }//检查头节点是否为空,则会很重要
	node* temp = phead;					//避免改动头指针
	node* newnode = new node;
	if (newnode == nullptr)return nullptr;
	while (temp->next != nullptr)
	{
		temp = temp->next;
	}
	temp->next = newnode;				//把新节点地址传给temp(当前末尾的节点)
	newnode->next = nullptr;			//尾节点next指针置空
	cout << "请依次输入num name age:";
	cin >> newnode->num >> newnode->name >> newnode->age;
	return newnode;
}
void putout(node* phead)
{
	if (phead->next == nullptr) { cout << "the list is null." << endl; return; }
	node* temp = phead->next;					//头节点没有内容,输出会导致乱码
	while (temp != nullptr)
	{
		cout << "num:" << temp->num << " name:" << temp->name << " age:" << temp->age << endl;
		temp = temp->next;				//遍历指针需要偏移
	}
}
string insert_node(node* phead)
{
	if (phead->next == nullptr) { return "the list is null.\n"; }
	int num;
	cout << "please input the node which you want insert(num):";
	cin >> num;
	node* temp = phead;
	while (temp != nullptr)		//在链表中查找节点(遍历查找,在到尾节点还没查找到,链表不存在对应节点)
	{
		if (temp->num != num)temp = temp->next;
		break;
	}
	if (temp != nullptr)
	{
		if (temp->next == nullptr)return "该节点是尾节点";
	}
	else if (temp == nullptr)return "插入位置不存在或该节点是尾节点";		//不存在,则返回不成功的日志
	node* newnode = new node;
	cout << "请依次输入num name age:";
	cin >> newnode->num >> newnode->name >> newnode->age;//确保输入的格式正确,否则会导致程序不正常执行,可以优化(抛出异常等方式)
	newnode->next = temp->next;
	temp->next = newnode;
	return "插入成功";
}
void delete_node(node* phead)			//单链表这个还有一点麻烦,双链表方便多了
{
	if (phead->next == nullptr) { cout << "链表为空" << endl; return; }
	node* temp = phead;
	cout << "please input the node you want to delete(num):";
	int num; cin >> num;
	if (phead->next->next == nullptr)			//只有两个节点时
	{
		if (phead->next->num == num)
		{
			delete phead->next; phead->next = nullptr;
			return;
			cout << "succes" << " " <<phead->next<< endl; return;
		}
		else
		{
			cout << "节点不存在." << endl; return;
		}
	}
	while (temp->next != nullptr)
	{
		if (temp->next->num != num)temp = temp->next;
		else if (temp->next->num == num)break;
	}
		
	if (temp->next != nullptr)			//查找的节点不是尾节点
	{
		node* ptemp = temp->next;
		temp->next = temp->next->next;
		delete ptemp;
	}
	else if (temp->next == nullptr)//查找的节点是尾节点
	{
		node* ptemp = temp->next;
		temp->next = nullptr;
		delete ptemp;
	}											//查找的节点不存在
	else if (temp->num != num && temp->next == nullptr)
	{
		cout << "节点不存在" << endl; return;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值