链表有一个很核心的思想操作:遍历
链表的插入,查找等等操作,都是需要遍历的 我之前最困惑的就是这一点,其他都还好
还有一点就是
指针是指针,内存块是内存块,释放内存并不会删除指针
指针仅仅只是保存地址
指针的问题就不细说
直接看代码吧
代码是在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;
}
}