学数据结构的时候,是不是一碰到链表就头疼?尤其是指针在里面绕来绕去,一会儿指这个节点,一会儿指那个节点,稍不注意就搞错了。其实啊,链表之所以离不开指针,就是因为指针能灵活地连接各个节点,让数据像链条一样串起来。今天兔子哥就用大白话讲讲,指针在链表中到底怎么用,从创建到增删改查,一步步给你说清楚,新手也能看明白,一起往下看吧!
为啥链表非要用指针?
你可能会问,数组也是存数据的,为啥非要用链表?数组的大小是固定的,想中间插个数据特别麻烦,得把后面的全挪位置。但链表不一样,每个节点存数据,还带个指针指向下一个节点,想加数据只要改改指针指向就行,灵活多了。
那为啥非得是指针呢?因为每个节点都是动态创建的,地址不连续,只有指针能记住下一个节点在哪儿。就像一串珠子,绳子就相当于指针,把每个珠子(节点)串起来,没有绳子,珠子就散了。
比如定义一个链表节点,肯定得有个指针成员:
c运行
struct Node {int data; // 存数据struct Node *next; // 指针,指向下一个节点};这个
next指针就是关键,没有它,节点之间就断了联系。第一步:用指针创建链表节点
创建节点就是申请一块内存,存数据,再让指针暂时指向空。这时候得用
malloc分配内存,返回的地址用指针接住。c运行
struct Node* createNode(int data) {// 申请内存,用指针p指向新节点struct Node *p = (struct Node*)malloc(sizeof(struct Node));if (p == NULL) { // 内存不够的情况printf("内存分配失败");return NULL;}p->data = data; // 存数据p->next = NULL; // 暂时指向空,后面再连其他节点return p;}这里的
p就是指向新节点的指针,没有它,我们根本没法操作这个节点。创建完节点,就可以用指针把它们串起来了。第二步:用指针把节点连起来(构建链表)
单独的节点没用,得用指针把它们连成链表。比如创建一个简单的链表,包含 3 个节点:
c运行
int main() {// 创建三个节点struct Node *n1 = createNode(10);struct Node *n2 = createNode(20);struct Node *n3 = createNode(30);// 用指针连接起来n1->next = n2; // n1的next指向n2n2->next = n3; // n2的next指向n3n3->next = NULL; // 最后一个节点指向空return 0;}你看,
n1->next这个指针一指向 n2,两个节点就连上了。整个链表的开头(头指针)是 n1,顺着 n1 的 next 能找到 n2,再顺着 n2 的 next 能找到 n3,这就是指针的作用。第三步:用指针遍历链表,读取数据
想看看链表存了啥,就得用指针从头走到尾。定义一个临时指针,从头部开始,每次往后移一步,直到指针指向空。
c运行
void printList(struct Node *head) {struct Node *p = head; // 临时指针p,从头部开始while (p != NULL) { // 没到末尾就继续printf("%d ", p->data); // 打印当前节点数据p = p->next; // 指针往后移,指向下一个节点}printf("\n");}调用的时候直接传头指针就行:
printList(n1);,会输出 “10 20 30”。这里的临时指针 p 特别重要,要是直接用 head 移动,移完之后头指针就找不到开头了。第四步:用指针在链表中插入节点
插入节点是链表的核心操作,全靠指针指向来实现。比如在 n1 和 n2 之间插一个节点 n4:
c运行
// 在pos节点后面插入新节点void insertAfter(struct Node *pos, int data) {if (pos == NULL) { // 不能往空节点后面插printf("pos节点为空,插入失败");return;}struct Node *newNode = createNode(data);// 新节点的next先指向pos原来的下一个newNode->next = pos->next;// 再让pos的next指向新节点pos->next = newNode;}// 调用:在n1后面插入40insertAfter(n1, 40);这两步顺序不能反,要是先改 pos->next,就找不到原来的 n2 了。你看,指针的指向一换,新节点就插进去了,多方便。
第五步:用指针删除链表中的节点
删除节点也得靠指针,先找到要删的节点的前一个节点,再让它的 next 跳过要删的节点,最后释放内存。
c运行
void deleteNode(struct Node *head, struct Node *del) {if (head == NULL || del == NULL) return;// 找到要删节点的前一个节点struct Node *p = head;while (p != NULL && p->next != del) {p = p->next;}if (p != NULL) {p->next = del->next; // 跳过要删的节点free(del); // 释放内存,别忘这步,不然内存泄漏}}删除的时候,指针 p 的作用就是找到前驱节点,没有 p,我们不知道该改哪个节点的 next。
指针操作链表常见错误对比表
| 错误操作 | 后果 | 正确做法 |
|---|---|---|
| 直接移动头指针遍历 | 遍历完找不到链表开头 | 用临时指针(如 p=head)移动 |
| 插入节点时顺序搞反 | 链表断裂,丢数据 | 先让新节点指向下一个,再让前节点指向新节点 |
| 删除节点后不释放内存 | 内存越用越少(内存泄漏) | 删完用 free 释放节点内存 |
| 没判断指针是否为 NULL | 程序崩溃(访问空指针) | 操作前先检查if (p != NULL) |
可能有人会问,链表操作这么多指针,怎么才能不搞混?兔子哥的经验是,多画图。每个节点画个框,指针用箭头表示,插入删除的时候,先在纸上画箭头怎么移,再写代码,就不容易错了。
刚开始学的时候,我也经常把指针指错,导致链表断成好几截。后来发现,只要记住 “指针就是连接节点的线,移动指针就是动线的方向”,慢慢就有感觉了。其实啊,指针在链表中的用法看着复杂,练多了就会发现套路都差不多,无非就是改改箭头指向。
希望今天讲的这些能帮你理清思路,下次操作链表的时候,不妨试试先画个图,再动手写代码,相信你很快就能掌握指针在链表中的用法!
标签: 数据结构 createNode
版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。
还木有评论哦,快来抢沙发吧~