提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
链表
相信很多友友们在学习链表的时候会有一点点小疑惑吧,那么看这篇文章就可以一解烦恼
提示:以下是本篇文章正文内容,下面案例可供参考
一、链表结构
平时不怎么画图所以略显难看,但斯是图陋,内容则精
struct node {
int data;
node* next;
node() :next(NULL) {};
};
链表由一个又一个结点构成,每个结点都有一个数据域(data)和指针域(next)结点之间就是通过指针域相连接。
通常来说链表第一个结点称之为头结点,由头指针指向它,并且头结点不存储数据也就是数据域data为空,规范书写代码所以在构造函数里用初始化列表将next指针设为空指针
二、链表
1.单向链表
话不多说,上代码清晰了然
#include <iostream>
using namespace std;
struct node {
int data;
node* next;
node() :next(NULL) {};
};
//遍历链表
node* traverse(node* h)
{
if (h == NULL)
{
cout << "链表为空" << endl;
return NULL;
}
h = h->next;
while (h->next != NULL)
{
cout << h->data;
h = h->next;
}
return NULL;
}
//查找数据
node* search(node* h, int x)
{
if (h ==NULL)
{
cout << "空链表查找失败" << endl;
return NULL;
}
while (h->next != NULL)
{
h = h->next;
if (h->data == x)
{
cout << "找到此节点了" << endl;//这里最好是可以给每个节点加一个编号
return h;
}
}
return NULL;
}
//增加一个新节点
void addnode(node* p, int x)
{
node* q = new node;
if (q == NULL)
{
cout << "堆区内存溢出,创建失败" << endl;//在正式代码书写中一定要判断节点到底有没有创建成功,一旦内存溢出就g了
return;
}
p->data = x;
q->next = p->next;
p->next = q;
}
//创建链表这里就会用到二级指针了,当然也可以一级指针加引用
void create_node(node** phead, int a[], int n)//这里用一个数组来分配数据域,n是元素个数
{
if (*phead == NULL)
{
*phead = new node;
}
for (int i = 0; i < n; i++)
{
addnode(*phead, a[i]);
}
}
//做删除操作,删除p的直接后继
void delnode(node* p)
{
if (p = NULL)return;
node * q = p->next;
if (q = NULL)return;
p->next = q->next;
delete q;
q = NULL;
}
上面代码基本已经是最简化的样子了,是很好理解的就不多赘叙了,有一个小注意点还是要说下的,就是在创建一个新结点时都要判断下是否创建成功,不然等堆区内存溢出程序报错的时候根本看不明白是哪里出来问题
(经验告诉我这一个小举措用处非常大的)
2.双向链表
还是这个图
相比单链表,双向链表多了一个指针域(前继链域)除此之外基本没差
上代码
代码如下(示例):
#include<iostream>
using namespace std;
const int n = 5;
int a[n];
struct node
{
int data;
node* pre;
node* next;
node() :pre(NULL), next(NULL) {};//初始化指针域
};
void nexinsert(node* p, int x)//在p后面插入一个节点
{
node* q = new node;
if (q == NULL)
{
cout << "堆区内存溢出,插入失败" << endl;
return;
}
if(p->next!=NULL)
{
q->data = x;
q->next = p->next;
p->next->pre = q;
p->next = q;
q->pre = p;}
else
{
p->next = q;
q->pre = p;
}
}
void preinsert(node*&p, int x)
{
node* q = new node;
node* pre = p->pre;
if (q == NULL)
{
cout << "堆区内存溢出,插入失败" << endl;
return;
}//这块都是一样的,很多人嫌麻烦就省去了,等报错的时候都不懂报错的原因是什么
q->data = x;
q->next = p;
p->pre = q;
if (p->pre != NULL)
{
q->pre = pre;
pre->next = q;
}
else
{
p = q;//当p是表头的时候将头指针往前移动指向q
}
}
void create_node(node** p)//创建双向链表
{
*p = new node;
for (int i = 0; i < n; i++)
{
nexinsert(*p, a[i]);
}
}
3.循环链表
循环链表和单链表只多了一个最后结点的next指向头结点,但是在循环链表中大多称呼最后一个结点为头结点
总结
链表基础部分就这些了,还有个双向循环链表跟上面几乎都是一样的,所以就没写了。
代码很基础,也很好理解希望各位友友们点个赞跟关注(▽)
有任何问题都可以评论区提出也可以私信发我,代码如有错误也希望指出!