彻底理解链表中为何使用二级指针或者一级指针的引用

数据结构之链表-链表实现及常用操作(C++篇)

  C语言实现单链表,主要功能为空链表创建,链表初始化(头插法),链表元素读取,按位置插入,(有序链表)按值插入,按位置删除,按值删除,清空链表,销毁链表。

  关键思路:(1)将结点创建结构体;(2)链表中添加头结点,以便统一操作;(3)使用结点一级指针和二级指针的异同点;(4)链表的最小操作单位是结点;(5)操作的起始位置是头结点还是第一个结点,及起始索引是0还是1. 

 #include <stdio.h>
#include <stdlib.h>
#include <time.h> //涉及到结构体自引用
typedef struct Node{
int data;
struct Node *next;
}Node; //创建空链表
//必须用二级指针,涉及到头指针的创建
int iniList(Node **List){
*List = (Node *)malloc(sizeof(Node));
if (NULL == *List){
return ;
} (*List)->next = NULL; //创建头结点
return ;
} //初始化链表(头插法)
//必须二级指针
int iniListHead(Node **List, int n){
*List = (Node *)malloc(sizeof(Node));
if (NULL == *List){
return ;
}
(*List)->next = NULL;
srand(time()); int i = ;
while (i < n){
Node *tmpNode = (Node *)malloc(sizeof(Node));
if (NULL == tmpNode){
return ;
}
tmpNode->data = rand() % + ;
tmpNode->next = (*List)->next;
(*List)->next = tmpNode; ++i;
}
return ;
} //初始化链表(尾插法)
//必须二级指针
//需要借助辅助变量pCurrent,将每次尾插的新元素当做当前元素
int iniListTail(Node **List, int n){
*List = (Node *)malloc(sizeof(Node));
if (NULL == *List){
return ;
}
(*List)->next = NULL;
srand(time()); Node *pCurrent = *List; int i = ;
while (i < n){
Node *tmpNode = (Node *)malloc(sizeof(Node));
if (NULL == tmpNode){
return ;
}
tmpNode->data = rand() % + ;
tmpNode->next = NULL;
pCurrent->next = tmpNode;
pCurrent = tmpNode; ++i;
}
return ;
} //清空链表(不删除头结点)
//一级,二级指针均可
//首先找到链表地址,然后移动至表尾
//判断条件为指针域是否为空,即到达结尾
int deleteList(Node *List){ //这种方法无法删除尾结点
//Node *p= List;
//Node *q = NULL;
//
//while (p->next){
// q = p;
// free(p);
// p = q->next;
//} Node *p = List->next;
Node *q = NULL; while (p){
q = p->next;
free(p);
p = q;
} List->next = NULL;
return ;
} //销毁链表
//必须使用二级指针,销毁头结点和头指针
//最后将链表头指针置空
int desrotyList(Node **List){ Node *p = *List;
Node *q = NULL; //如果为空链表,直接删除头结点
//如果不是空链表,从头结点开始删除
while (p){
q = p->next;
free(p);
p = q;
}
(*List) = NULL; //下面是从第一个结点开始删除
//最后释放掉头结点
//Node *p = (*List)->next;
//Node *q = NULL;
//
//while (p){
// q = p->next;
// free(p);
// p = q;
//}
//free(*List);
//(*List) = NULL; return ;
} //链表获取元素
//一级,二级指针均可
//头结点无意义,从第一个结点开始遍历,i从1开始
//每次都指向下一结点,到pos-1即可
int getList(Node *List, int pos, int *element){
Node *p = List->next; int i = ;
while (p && i < pos){
p = p->next;
++i;
}
*element = p->data;
return ;
} //链表按位置插入
//一级,二级指针均可
//从头结点开始,有可能插入在第一个位置,遍历从1开始
int insertListPos(Node *List, int pos, int value){
Node *p = List; int i = ;
while (p && i < pos){
p = p->next;
++i;
}
Node *tmpNode = (Node *)malloc(sizeof(Node));
tmpNode->data = value;
tmpNode->next = p->next;
p->next = tmpNode;
return ;
} //有序链表,按值插入
//一二级指针均可
int insertListValue(Node *List, int value){
Node *pCur = List->next;
Node *pPer = List;
while (pCur && pCur->data < value){
pPer = pCur;
pCur = pCur->next;
} Node *tmpNode = (Node *)malloc(sizeof(Node));
if (NULL == tmpNode){
return ;
}
tmpNode->data = value;
tmpNode->next = pPer->next;
pPer->next = tmpNode;
return ;
} //链表按位置删除
//一二级指针均可
//记得释放结点内存
//如果删除第一个结点,需要操纵头结点
int deleteListPos(Node *List, int pos){
Node *p = List; int i = ;
while (p && i < pos){
p = p->next;
++i;
}
if (NULL == p->next)
return ; Node *tmpNode = p->next;
p->next = p->next->next; free(tmpNode);
return ;
} //链表按值删除元素
//一二级指针均可
//从第一个结点开始
int deleteListValue(Node *List, int value){
Node *pCur = List->next;
Node *pPer = List; while (pCur && pCur->data != value){
pPer = pCur;
pCur = pCur->next;
}
if (pCur == NULL){ //空链表不删除任何结点
return ;
}
else{
pPer->next = pCur->next;
free(pCur);
}
return ;
} int main(){ Node *testList = NULL;
iniList(&testList);
//iniListHead(&testList, 3);
//iniListTail(&testList, 3); insertListPos(testList, , );
insertListPos(testList, , );
insertListPos(testList, , );
insertListPos(testList, , );
//insertListPos(testList, 1, 1);
insertListValue(testList, ); //deleteListPos(testList, 1);
//deleteListValue(testList, 4); //deleteList(testList); //printf("%d\n", testList);
//desrotyList(&testList);
//printf("%d\n", testList); Node * tmpNode = testList->next;
while (tmpNode){
printf("%d\n", tmpNode->data);
tmpNode = tmpNode->next;
} printf("----------------------\n"); int a = ;
getList(testList, , &a);
printf("%d\n", a); system("pause"); return ;
}

C语言完整代码

  通过C++实现C语言的链表,主要区别:(1)struct可以不通过typedef,直接使用Node;(2)将malloc和free更换为new和delete

 #include<iostream>
#include<ctime> using namespace std; struct Node{
int data;
Node *next;
}; //创建空链表
//必须用二级指针,涉及到头指针的创建
int iniList(Node **List){
*List = new Node; (*List)->next = NULL; //创建头结点
return ;
} //初始化链表(头插法)
//必须二级指针
int iniListHead(Node **List, int n){
*List = new Node; (*List)->next = NULL;
srand(time()); int i = ;
while (i < n){
Node *tmpNode = new Node; tmpNode->data = rand() % + ;
tmpNode->next = (*List)->next;
(*List)->next = tmpNode; ++i;
}
return ;
} //初始化链表(尾插法)
//必须二级指针
//需要借助辅助变量pCurrent,将每次尾插的新元素当做当前元素
int iniListTail(Node **List, int n){
*List = new Node; (*List)->next = NULL;
srand(time()); Node *pCurrent = *List; int i = ;
while (i < n){
Node *tmpNode = new Node; tmpNode->data = rand() % + ;
tmpNode->next = NULL;
pCurrent->next = tmpNode;
pCurrent = tmpNode; ++i;
}
return ;
} //清空链表(不删除头结点)
//一级,二级指针均可
//首先找到链表地址,然后移动至表尾
//判断条件为指针域是否为空,即到达结尾
int deleteList(Node *List){ //这种方法无法删除尾结点
//Node *p= List;
//Node *q = NULL;
//
//while (p->next){
// q = p;
// free(p);
// p = q->next;
//} Node *p = List->next;
Node *q = NULL; while (p){
q = p->next;
delete p;
p = q;
} List->next = NULL;
return ;
} //销毁链表
//必须使用二级指针,销毁头结点和头指针
//最后将链表头指针置空
int desrotyList(Node **List){ Node *p = *List;
Node *q = NULL; //如果为空链表,直接删除头结点
//如果不是空链表,从头结点开始删除
while (p){
q = p->next;
delete p;
p = q;
}
(*List) = NULL; //下面是从第一个结点开始删除
//最后释放掉头结点
//Node *p = (*List)->next;
//Node *q = NULL;
//
//while (p){
// q = p->next;
// free(p);
// p = q;
//}
//free(*List);
//(*List) = NULL; return ;
} //链表获取元素
//一级,二级指针均可
//头结点无意义,从第一个结点开始遍历,i从1开始
//每次都指向下一结点,到pos-1即可
int getList(Node *List, int pos, int *element){
Node *p = List->next; int i = ;
while (p && i < pos){
p = p->next;
++i;
}
*element = p->data;
return ;
} //链表按位置插入
//一级,二级指针均可
//从头结点开始,有可能插入在第一个位置,遍历从1开始
int insertListPos(Node *List, int pos, int value){
Node *p = List; int i = ;
while (p && i < pos){
p = p->next;
++i;
}
Node *tmpNode = new Node;
tmpNode->data = value;
tmpNode->next = p->next;
p->next = tmpNode;
return ;
} //有序链表,按值插入
//一二级指针均可
int insertListValue(Node *List, int value){
Node *pCur = List->next;
Node *pPer = NULL;
while (pCur && pCur->data < value){
pPer = pCur;
pCur = pCur->next;
} Node *tmpNode = new Node;
if (NULL == tmpNode){
return ;
}
tmpNode->data = value;
tmpNode->next = pPer->next;
pPer->next = tmpNode;
return ;
} //链表按位置删除
//一二级指针均可
//记得释放结点内存
//如果删除第一个结点,需要操纵头结点
int deleteListPos(Node *List, int pos){
Node *p = List; int i = ;
while (p && i < pos){
p = p->next;
++i;
}
Node *tmpNode = p->next;
p->next = p->next->next; delete tmpNode;
return ;
} //链表按值删除元素
//一二级指针均可
//从第一个结点开始
int deleteListValue(Node *List, int value){
Node *pCur = List->next;
Node *pPer = List; while (pCur && pCur->data != value){
pPer = pCur;
pCur = pCur->next;
}
if (pCur == NULL){
return ;
}
else{
pPer->next = pCur->next;
delete pCur;
}
return ;
}

C++完整代码

结点结构体

  将结点创建为结构体,其中包含数据域和指针域成员,指针域涉及结构体自引用。指针域成员之所以为结点类指针,一是为了编译时能明确结构体大小,二是为了指向下一个结点,因此初始化时不用开辟内存,只需要赋值为NULL即可。当然了,开辟内存也是可以的,这样在删除结点,及清空链表时需要记得将其释放,多此一举,不提倡。

 //涉及到结构体自引用
typedef struct Node{
int data;
struct Node *next;
}Node;

空链表创建(二级指针)

  空链表创建时,建议创建头结点。在插入和删除第一个位置的元素时,需要用结点的二级指针移动头指针,但普通结点的插入和删除并不需要移动头指针。为了便于操作的统一(指插入和删除操作),这里先创建头结点。

  另外,在创建空链表时,需要传入二级指针,主要是为了操作头指针,只要涉及操作头指针的都需要使用二级指针。

 //创建空链表
//必须用二级指针,涉及到头指针的创建
int iniList(Node **List){
*List = (Node *)malloc(sizeof(Node));
if (NULL == *List){
return ;
} (*List)->next = NULL; //创建头结点,将其指针域置空
return ;
}

链表初始化(二级指针)

  链表初始化,即是创建空链表,并对链表中的n个元素进行赋值。一般来说,有头插法、尾插法两种,头插法是在头结点后插入,尾插法是在链表尾插入。

  头插法

  头插法一般思路:(1)创建带头结点的空链表;(2)创建新结点,对新结点赋值;(3)新结点指向头结点的下一个结点,头结点指向新结点。

 //初始化链表(头插法)
//必须二级指针
int iniListHead(Node **List, int n){
*List = (Node *)malloc(sizeof(Node));
if (NULL == *List){
return ;
}
(*List)->next = NULL;
srand(time()); int i = ;
while (i < n){
Node *tmpNode = (Node *)malloc(sizeof(Node));
if (NULL == tmpNode){
return ;
}
tmpNode->data = rand() % + ;
tmpNode->next = (*List)->next;
(*List)->next = tmpNode; ++i;
}
return ;
}

  尾插法

  尾插法一般思路:(1)创建带头结点的空链表;(2)创建新结点,对新结点数据域赋值,指针域置空;(3)建立临时变量指向头结点,头结点指向新结点;(4)将临时变量往后移动,指向新结点。

 //初始化链表(尾插法)
//必须二级指针
//需要借助辅助变量pCurrent,将每次尾插的新元素当做当前元素
int iniListTail(Node **List, int n){
*List = (Node *)malloc(sizeof(Node));
if (NULL == *List){
return ;
}
(*List)->next = NULL;
srand(time()); Node *pCurrent = *List; int i = ;
while (i < n){
Node *tmpNode = (Node *)malloc(sizeof(Node));
if (NULL == tmpNode){
return ;
}
tmpNode->data = rand() % + ;
tmpNode->next = NULL;
pCurrent->next = tmpNode;
pCurrent = tmpNode; ++i;
}
return ;
}

链表元素读取(一、二级指针)

  链表元素读取,需要涉及到索引位置。我们知道头结点的数据域没有意义,因此起始位置从第一个结点开始,遍历值从1开始,到pos-1为止,此时p指向pos结点。

 //链表获取元素
//一级,二级指针均可
//头结点无意义,从第一个结点开始遍历,i从1开始
//每次都指向下一结点,到pos-1即可
int getList(Node *List, int pos, int *element){
Node *p = List->next; int i = ;
while (p && i < pos){
p = p->next;
++i;
}
*element = p->data;
return ;
}

按位置插入(一、二级指针)

  插入时,需要考虑任意位置,由于头结点的设立,我们不需要单独考虑第一个位置的结点插入。

  一般思路:(1)起始位置从头结点开始,遍历值从1开始,到pos-1为止,此时指向pos-1结点;(2)创建新结点,给新结点数据域赋值;(3)新结点指向pos位置的下一结点,pos位置指向新结点。

 //链表按位置插入
//一级,二级指针均可
//从头结点开始,有可能插入在第一个位置,遍历从1开始
int insertListPos(Node *List, int pos, int value){
Node *p = List; int i = ;
while (p && i < pos){
p = p->next;
++i;
}
Node *tmpNode = (Node *)malloc(sizeof(Node));
tmpNode->data = value;
tmpNode->next = p->next;
p->next = tmpNode;
return ;
}

有序链表按值插入(一、二级指针)

  有序链表中涉及到按值插入,按值删除时,需要建立两个临时变量,不同于按位置插入,我们可以在pos前一个停止遍历,按值插入,我们需要遍历找到插入位置,操作插入位置的前一个结点。

  一般思路:(1)从第一个结点开始遍历;(2)创建per变量标记当前结点的前一结点;(3)创建新结点,操作per结点;(4)常规插入操作。

 //有序链表,按值插入
//一二级指针均可
int insertListValue(Node *List, int value){
Node *pCur = List->next;
Node *pPer = NULL;
while (pCur && pCur->data < value){
pPer = pCur;
pCur = pCur->next;
} Node *tmpNode = (Node *)malloc(sizeof(Node));
if (NULL == tmpNode){
return ;
}
tmpNode->data = value;
tmpNode->next = pPer->next;
pPer->next = tmpNode;
return ;
}

按位置删除(一、二级指针)

  删除时,由于头结点的设立,因此不需要特殊考虑第一个位置的操作和头指针的移动。需要设置临时变量是释放p->next后,后面还会使用p->next的结点,这样会造成访问出错。

  一般思路:(1)起始位置从头结点开始,遍历从1开始,到pos-1为止,此时指向pos-1结点;(2)创建临时变量,指向pos结点;(3)将pos-1结点指向pos的下一结点,释放掉pos结点。

 //链表按位置删除
//一二级指针均可
//记得释放结点内存
//如果删除第一个结点,需要操纵头结点
int deleteListPos(Node *List, int pos){
Node *p = List; int i = ;
while (p && i < pos){
p = p->next;
++i;
}
Node *tmpNode = p->next;
p->next = p->next->next; free(tmpNode);
return ;
}

按值删除(一、二级指针)

  按值删除与按值插入一样,需要两个临时变量。

  一般思路:(1)起始位置从第一个结点开始,(2)设立per结点指针保存当前结点的上一结点;(3)常规删除操作。

 //链表按值删除元素
//一二级指针均可
//从第一个结点开始
int deleteListValue(Node *List, int value){
Node *pCur = List->next;
Node *pPer = List; while (pCur && pCur->data != value){
pPer = pCur;
pCur = pCur->next;
}
if (pCur == NULL){ //空链表不删除任何结点
return ;
}
else{
pPer->next = pCur->next;
free(pCur);
}
return ;
}

清空链表(一、二级指针)

  清空链表,只是将链表中的结点删除,并释放掉结点空间,保留头结点,并将头结点的指针域置空。

  一般思路:(1)起始位置从第一个结点开始;(2)设置临时变量q指向当前结点p的下一结点,释放掉当前结点p,将临时变量q赋给p;(3)最后将头结点的指针置空。

 //清空链表(不删除头结点)
//一级,二级指针均可
//首先找到链表地址,然后移动至表尾
//判断条件为指针域是否为空,即到达结尾
int deleteList(Node *List){ //这种方法无法删除尾结点,当p->next是尾结点上一节点时,走一遍程序
//Node *p= List;
//Node *q = NULL;
//
//while (p->next){
// q = p;
// free(p);
// p = q->next;
//} Node *p = List->next;
Node *q = NULL; while (p){
q = p->next;
free(p);
p = q;
} List->next = NULL;
return ;
}

销毁链表(二级指针)

  销毁链表,是在清空链表的基础上,删除头结点,将头指针置空,涉及到头指针的操作,需要二级指针。

  思路一:(1)与清空链表操作相同,从第一个结点开始删除;(2)最后释放掉头结点,将头指针置空

  思路二:(1)起始位置从头结点开始删除;(2)将头指针置空

 //销毁链表
//必须使用二级指针,销毁头结点和头指针
//最后将链表头指针置空
int desrotyList(Node **List){ Node *p = *List;
Node *q = NULL; //如果为空链表,直接删除头结点
//如果不是空链表,从头结点开始删除
while (p){
q = p->next;
free(p);
p = q;
}
(*List) = NULL; //下面是从第一个结点开始删除
//最后释放掉头结点
//Node *p = (*List)->next;
//Node *q = NULL;
//
//while (p){
// q = p->next;
// free(p);
// p = q;
//}
//free(*List);
//(*List) = NULL; return ;
}

 二级指针和一级指针插入解析

-------------------------------------------------------------------------------------------------------------

如果上面的资料对你有启发,麻烦点个推荐,让更多人的人看到哦。

关注公众号【两猿社】,懂点互联网,懂点IC的程序猿,带你丰富项目经验哦。

C/C++语言实现单链表(带头结点)的更多相关文章

  1. C语言实现单链表(带头节点)

    C语言在实现单链表存储时需要注意的几点: 1.定义结构体,typedef:用于给结构体另命名 // 定义结构体类型 typedef struct Node{ int data; struct Node ...

  2. C语言实现单链表节点的删除(带头结点)

    我在之前一篇博客<C语言实现单链表节点的删除(不带头结点)>中具体实现了怎样在一个不带头结点的单链表的删除一个节点,在这一篇博客中我改成了带头结点的单链表.代码演示样例上传至 https: ...

  3. C语言实现单链表,并完成链表常用API函数

    C语言实现单链表,并完成链表常用API函数: 1.链表增.删.改.查. 2.打印链表.反转打印.打印环形链表. 3.链表排序.链表冒泡排序.链表快速排序. 4.求链表节点个数(普通方法.递归方法). ...

  4. C语言实现单链表-03版

    在C语言实现单链表-02版中我们只是简单的更新一下链表的组织方式: 它没有更多的更新功能,因此我们这个版本将要完成如下功能: Problem 1,搜索相关节点: 2,前插节点: 3,后追加节点: 4, ...

  5. C语言实现单链表-02版

    我们在C语言实现单链表-01版中实现的链表非常简单: 但是它对于理解单链表是非常有帮助的,至少我就是这样认为的: 简单的不能再简单的东西没那么实用,所以我们接下来要大规模的修改啦: Problem 1 ...

  6. 单链表的插入伪算法和用C语言创建单链表,并遍历

    非循环单链表插入结点伪算法讲解 q插入p之后的伪算法:第一种表示方法:r = p->pNext; // p->pNext表示的是所指向结点的指针域,指针域又是指向下一个结点的地址p-> ...

  7. C语言实现单链表(不带头结点)节点的插入

    对单链表进行增删改查是最主要的操作.我在上一篇博客<C语言实现链表节点的删除>实现了删除单链表中的某个节点. 这里我们要来实如今某个位置插入节点.演示样例代码上传至https://gith ...

  8. 「C语言」单链表/双向链表的建立/遍历/插入/删除

    最近临近期末的C语言课程设计比平时练习作业一下难了不止一个档次,第一次接触到了C语言的框架开发,了解了View(界面层).Service(业务逻辑层).Persistence(持久化层)的分离和耦合, ...

  9. C语言之单链表的使用

    #include <stdio.h> #include <stdlib.h> typedef struct LNode{ int data; struct LNode *nex ...

随机推荐

  1. css切角效果,折角效果

    html <div class="one">12345</div> <div class="two">abcde</d ...

  2. useradd -帐号建立或更新新使用者的资讯

    总览 SYNOPSIS useradd [-c comment] [-d home_dir] [-e expire_date] [-f inactive_time] [-g initial_group ...

  3. unicode_start - 将控制台设为Unicode模式.

    总览 unicode_start [ font [ screen-font-map ] ] 描述 unicode_start 命令将显示屏及键盘设为 Unicode 模式, 并且有可能还会装载所用的 ...

  4. 笔记43 Spring Security简介

    基于Spittr应用 一.Spring Security简介 Spring Security是为基于Spring的应用程序提供声明式安全保护的安全 性框架.Spring Security提供了完整的安 ...

  5. CSIC_716_20191205【TCP-解决粘包问题、UDP模板】

    ---------------------------------------------------------------------------------------------------- ...

  6. Shell while循环详解

    while 循环是 Shell 脚本中最简单的一种循环,当条件满足时,while 重复地执行一组语句,当条件不满足时,就退出 while 循环. Shell while 循环的用法如下: while  ...

  7. mysql 存储过程 随笔

    CREATE PROCEDURE `g2`( in sts int,in type int, code varchar(20),in s int)begin declare i int; declar ...

  8. PHP PDO 大对象 (LOBs)

    应用程序在某一时刻,可能需要在数据库中存储"大"数据. "大"通常意味着"大约 4kb 或以上",尽管某些数据库在数据达到"大&q ...

  9. 帝国cms过滤采集内容

    在过滤广告正则的[!--ad--]标识处,加上过滤正则即可 https://jingyan.baidu.com/article/c275f6bae3ea0de33d75671c.html

  10. 【SPOJ DQUERY】区间数字统计

    [链接] 我是链接,点我呀:) [题意] 题意 [题解] 因为区间的端点移动一个单位的话,只会涉及到一个元素的增多或减少. 因此可以用莫队算法来解决. 只需要开一个数组(大小1百万),用下标来快速检索 ...