对于数据结构的学习,亲手敲一遍还是很有必要的,一动手才能发现自己的知识还是存在很多的漏洞。由于是第一次写,可能存在些错误(我自己测试的时候是没发现问题的),欢迎评论区指正。
下面用C++来定义一个简单的单向链表,再写下默认构造和有参构造。
struct node
{
int val;
node* next;
node() = default;//默认构造
node(int val, node* next = nullptr)
: val(val), next(next) {}
};
写一个尾插函数,传入链表头结点,并给插入的新结点赋值key,这里不要忘记处理空链表的情况。注意!由于这里我是直接用head去遍历链表,所以参数是以形参的形式传入,不能以引用的形式传参(node*& head),不然head的值和指向会被改变。如果是引用传参,需要定义一个临时结点temp指向头节点(见下文),再通过temp去遍历
void end_insert(node* head,int key)
{
if (head == nullptr)
{// 处理空链表的情况
cout << "头结点为空!" << endl;
return;
}
node* new_node = new node(key);
while (head->next)//遍历
{
head = head->next;
}
head->next = new_node;
}
这里我给上个尾插函数做了些优化,如果是空链表,那么我们就直接给头指针插值,这里需要使用引用传参,因为直接改变了参数head本身
void end_insert(node*& head,int key)
{
if (head == nullptr)
{// 处理空链表的情况
head = new node(key);
return;
}
node* new_node = new node(key);
node* temp = head;
while (temp->next)//遍历
{
temp = temp->next;
}
temp->next = new_node;
}
再写一个头插
void begin_insert(node*& head, int val)
{
node* new_node = new node(val);
new_node->next = head;
head = new_node;
}
遍历链表的函数依然不能忘记处理空链表的情况,加个const修饰保证数据安全性
void visit_node(const node* head)
{
if (head == nullptr)
{
cout << "头结点为空" << endl;
return;
}
const node* temp = head;
int i = 1;
while (temp)
{
cout << "node[ " << i++ << " ] = " << temp->val << endl;
temp = temp->next;
}
}
查找值为指定值val的结点,并删除,一样要使用引用传参,不过这里只能删除第一个值为val的结点
void val_erase(node*& head,int val)
{
if (head == nullptr) // 检查头结点是否为空
{
cout << "未找到值相匹配的结点" << endl;
return;
}
else if (head->next == nullptr)//只有一个头结点
{
if (head->val == val)//头结点就是目标结点
{
delete head;
head = nullptr;
cout << "结点已删除!" << endl;
return;
}
}
node* temp = head;//临时结点
while (temp->next)
{
if (temp->next->val == val)//找到
{
node* dest = temp->next;//存储被删除的结点
temp->next = temp->next->next;//指向被删除结点的下一个结点
delete dest;//删除目标结点
dest = nullptr;
cout << "结点已删除!" << endl;
return;
}
temp = temp->next;//向后遍历
}
cout << "未找到值相匹配的结点" << endl;
}
根据下标index删除指定结点,下标从0开始。这里依然要把麻烦的头指针单独拿出来写,遍历的时候,要使temp指向被删除结点的前一个结点,好进行删除后的链表拼接。如果下标为1,是不会进入for循环的,因此在for循环后面需要一个if去判定第二个结点是否为空。
void index_erase(node*& head, int index)
{
node* temp = head;
if (index == 0)//删除头结点
{
head = head->next;//新头结点
delete temp;
temp = nullptr;
cout << "结点已删除!" << endl;
return;
}
for (int i = 0; i < index-1; i++)//指向目标结点前一个位置
{
if (temp == nullptr)//防止index大于链表结点数
{
cout << "下标不合法,删除失败" << endl;
return;
}
temp = temp->next;
}
if (temp->next == nullptr)
{
cout << "下标不合法,删除失败" << endl;
return;
}
node* dest = temp->next;//存储被删除的结点
temp->next = temp->next->next;//指向被删除结点的下一个结点
delete dest;//删除目标结点
dest = nullptr;
cout << "结点已删除!" << endl;
}
查询值为val的结点,并返回这个结点,打印出下标
node* find_node(node* head, int val)
{
int index = 0;
while (head)
{
if (head->val == val)
{
cout << "目标结点下标为: " << index << endl;
return head;
}
head = head->next;
index++;
}
cout << "未找到该节点" << endl;
return nullptr;
}
销毁链表,这里由于要让头指针head指向空,所以要以引用的形式传入
void destroy_node(node*& head)
{
node* temp;
while (head)
{
cout << "key = " << head->val << " 已销毁" << endl;
temp = head->next;
delete head;
head = temp;
}
head = nullptr;
}
最后写一个升序排序的函数,这个排序是根据val的值重排链表的所有结点,而不是简单的给结点的val值进行排序
void sortedInsert(node*& head, node* new_node);
//升序排序
void sort_node(node*& head)
{
// 空链表或只有一个元素,无需排序
if (head == nullptr || head->next == nullptr)
return;
node* sorted{}, * next{};
node* current = head;
while (current)
{
next = current->next;
sortedInsert(sorted, current);
current = next;
}
head = sorted;
}
void sortedInsert(node*& head, node* new_node)
{
if (head == nullptr || head->val >= new_node->val)//第一个值大
{
new_node->next = head;
head = new_node;
}
else//第二个值大
{
node* current = head;
while (current->next != nullptr && current->next->val < new_node->val)
{
current = current->next;
}
new_node->next = current->next;
current->next = new_node;
}
}
还有些头删尾删的函数比较简单,就不写了。如有错误,欢迎指正。