怎么样让指针指向尾结点C语言,带头结点头指针与带头结点尾指针的学习
单循环链表
对于单链表而言,如果每次在遍历到单链表中间处时需要遍历整个链表,此时只能往后遍历,前方的指针便会丢失。如图1所示,此时若链表遍历到a2处依旧可以通过尾结点循环到a1处,这是单链表所不能解决的。
图1
带头结点头指针的单循环链表
关于单链表的存取,有时候我们在单链表的第一个结点(有效元素)之前附设一个结点,称之为头结点;指向头结点的指针,称之为头指针如图2;
图2
对于单链表来说,不同在于判空,以及尾结点的next为头结点。
/* 初始化链式线性表 */
Status InitList(LinkList* L)
{
*L = (LinkList)malloc(sizeof(Node)); /* 产生头结点,并使头指针L指向此头结点 */
if (!(*L)) /* 存储分配失败 */
return ERROR;
(*L)->next = *L; /* 单循环链表 */
return OK;
}
/* 初始条件:链式线性表L已存在。操作结果:将L重置为空表 */
Status ClearList(LinkList* L)
{
LinkList p, q;
p = (*L)->next; /* p指向第一个结点 */
while (p != (*L)) /* 没到表尾 */
{
q = p->next;
free(p);
p = q;
}
(*L)->next = *L; /* 头结点指针域为空 */
return OK;
}
插入和删除可由图三、图四看出差距不大,可参照单链表的编写改基本操作。
图3
图4
带头结点尾指针的单循环链表
对于带头结点尾指针的单循环链表而言,其不仅判空和初始化不同,就连插入操作和删除操作也不同。如图5.
图5
对于插入和删除而言,由于只知道尾指针,于是对于查找头结点和尾结点都可以O(1)进行。
L->rear->next->next //首节点。
L->rear //尾指针指向的尾结点。
学习心得
1.在头结点、头指针、尾结点和尾指针的设置时一定要弄清楚。malloc(sizeof(Node))创建的是一个结点,而(LinkList)malloc(sizeof(Node))是创建的是一个结构体指针。两者的返回值都是指向内存首地址的指针,但(LinkList)进行了强制的类型转换。因为malloc()函数的返回值类型是 void *,void 并不是说没有返回值或者返回空指针,而是返回的指针类型未知。所以在使用 malloc() 时通常需要进行强制类型转换,将 void 指针转换成我们希望的类型。
2.初始化工作在头结点头指针和头结点尾指针是不同的。
头结点头指针的定义:
typedef struct Node
{
ElemType data;
struct Node* next;/*定义下一个Node的指针,结点内依旧包含data和next指针*/
}Node;
typedef struct Node* LinkList; /* 定义LinkList */
头结点尾指针的定义:
typedef struct Node
{
ElemType data;
struct Node* next;/*定义下一个Node的指针,结点内依旧包含data和next指针*/
}Node;
typedef struct {
Node* rear;
}*LinkListLast;/* 定义LinkList */
我们可以看到两者在定义时是不同,头结点头指针可以在定义结点的时候直接定义结点首地址的指针,而头结点尾指针多了对于尾指针指向的尾结点的定义如图6。
图6
所以在头结点尾指针的初始化时候需要分配两次内存。
/* 初始化链式线性表 */
/* LinkListLast*表示一个结点,使用*L来接受它的返回的首地址
LinkListLast表示链表
*/
Status InitList(LinkListLast* L)
{
(*L) = (LinkListLast)malloc(sizeof(Node)); /*定义链表*/
(*L)->rear = (Node *)malloc(sizeof(Node)); /* 产生头结点,并使尾指针指向此头结点 */
if (!(*L)->rear) /* 存储分配失败 */
return ERROR;
(*L)->rear->next= (*L)->rear; /* 单循环链表 */
return OK;
}
3.在特定位置之前插入和删除某个特定的结点不同,由于前驱结点的指针无法知道所以容易free(p)后找不到前驱结点。此时我们可以创建一个q结点,q为p前驱,qp一起移动。
Status ListInsert(LinkListLast* L, int i, ElemType e,int k)
{
int j=1;
Node *p,*s;
p = (*L)->rear->next->next;
if (i == 1) {
ListInsertHead(*L, e);
return OK;
}
if (i == k) {
ListInsertTail(*L, e);
return OK;
}
while (p!=(*L)->rear && j < i-1) /* 寻找第i个结点 */
{
p = p->next;
++j;
}
if (!p||j > i) {
return ERROR;
}
s = malloc(sizeof(Node)); /* 生成新结点(C语言标准函数) */
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
Status ListDelete(LinkListLast* L, int i, ElemType* e)
{
int j;
Node *p,*q;
j = 1;
p = (*L)->rear->next->next;
q = malloc(sizeof(Node));
while (p != (*L)->rear->next && j < i) /* 遍历寻找第i个元素 */
{
q = p;
p = p->next;
++j;
}
if (p == (*L)->rear->next || j > i) {
return ERROR; /* 链表为空或第i个元素不存在 */
}
if (p == (*L)->rear) {
q->next = p->next;
*e = p->data;
(*L)->rear = q;
free(p);
return OK;
}
q->next = p->next;
*e = p->data;
free(p);
return OK;
}
4.对于头结点尾指针的链表来说,在首结点之前,尾结点之后,查找首尾结点时间复杂度都为O(1)。但需注意若改变在尾结点,那么讨论是否应该改变尾指针。
/* 初始条件:链式线性表L已存在,1≤i≤ListLength(L), */
/* 操作结果:在L表尾插入新的数据元素e,L的长度加1 */
Status ListInsertTail(LinkListLast* L, ElemType e)
{
Node * s;
if (!(*L)) {
return ERROR;
}
s = malloc(sizeof(Node)); /* 生成新结点(C语言标准函数) */
s->data = e;
s->next = (*L)->rear->next; /* 将p的后继结点赋值给s的后继 */
(*L)->rear->next = s; /* 将s赋值给p的后继 */
(*L)->rear = s;
return OK;
}
完整代码(带头结点尾指针的单循环链表)
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "math.h"
#include "time.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20 /* 存储空间初始分配量 */
typedef int Status;/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int ElemType;/* ElemType类型根据实际情况而定,这里假设为int */
Status visit(ElemType c)
{
printf("%d ", c);
return OK;
}
typedef struct Node
{
ElemType data;
struct Node* next;/*定义下一个Node的指针,结点内依旧包含data和next指针*/
}Node;
typedef struct {
Node* rear;
}*LinkListLast;/* 定义LinkList */
/* 初始化链式线性表 */
/* LinkListLast*表示一个结点,使用*L来接受它的返回的首地址
LinkListLast表示链表
*/
Status InitList(LinkListLast* L)
{
(*L) = (LinkListLast)malloc(sizeof(Node));
(*L)->rear = (Node *)malloc(sizeof(Node)); /* 产生头结点,并使尾指针指向此头结点 */
if (!(*L)->rear) /* 存储分配失败 */
return ERROR;
(*L)->rear->next= (*L)->rear; /* 单循环链表 */
return OK;
}
/* 初始条件:链式线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */
Status ListEmpty(LinkListLast L)
{
if (L->rear->next != L->rear)
return FALSE;
else
return TRUE;
}
/* 初始条件:链式线性表L已存在。操作结果:将L重置为空表 */
Status ClearList(LinkListLast* L)
{
Node* p, * q;
LinkListLast Head;
p = (*L)->rear->next->next; /* p指向第一个结点 */
Head = (*L)->rear->next;
while (p != Head) /* p是结构体指针,没到表尾 */
{
q = p->next;
free(p);
p = q;
}
(*L)->rear->next= (*L)->rear; /*尾指针指向头结点*/
return OK;
}
/* 初始条件:链式线性表L已存在。操作结果:返回L中数据元素个数 */
int ListLength(LinkListLast L)
{
int i = 0;
Node* p; /* p指向第一个结点 */
p = L->rear->next->next;
while (p != L->rear->next)
{
i++;
p = p->next;
}
return i;
}
/* 初始条件:链式线性表L已存在,1≤i≤ListLength(L) */
/* 操作结果:用e返回L中第i个数据元素的值 */
Status GetElem(LinkListLast L, int i, ElemType* e)
{
int j;
Node *p; /* 声明一结点p */
p = L->rear->next->next; /* 让p指向链表L的第一个结点 */
j = 1; /* j为计数器 */
while (p != L->rear->next && j < i) /* p不为空或者计数器j还没有等于i时,循环继续 */
{
p = p->next; /* 让p指向下一个结点 */
++j;
}
if ( !p||j > i)
return ERROR; /* 第i个元素不存在 */
*e = p->data; /* 取第i个元素的数据 */
return OK;
}
/* 初始条件:链式线性表L已存在 */
/* 操作结果:返回L中第1个与e满足关系的数据元素的位序。 */
/* 若这样的数据元素不存在,则返回值为0 */
int LocateElem(LinkListLast L, ElemType e)
{
int i = 0;
Node* p = L->rear->next->next;
while (p!=L->rear->next)
{
i++;
if (p->data == e) /* 找到这样的数据元素 */
return i;
p = p->next;
}
return 0;
}
/* 初始条件:链式线性表L已存在,1≤i≤ListLength(L), */
/* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */
Status ListInsert(LinkListLast* L, int i, ElemType e,int k)
{
int j=1;
Node *p,*s;
p = (*L)->rear->next->next;
if (i == 1) {
ListInsertHead(*L, e);
return OK;
}
if (i == k) {
ListInsertTail(*L, e);
return OK;
}
while (p!=(*L)->rear && j < i-1) /* 寻找第i个结点 */
{
p = p->next;
++j;
}
if (!p||j > i) {
return ERROR;
}
s = malloc(sizeof(Node)); /* 生成新结点(C语言标准函数) */
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
/* 初始条件:链式线性表L已存在,1≤i≤ListLength(L), */
/* 操作结果:在L中表头插入新的数据元素e,L的长度加1 */
Status ListInsertHead(LinkListLast* L, ElemType e)
{
Node * s;
if (!(*L)) {
return ERROR;
}
if ((*L)->rear == (*L)->rear->next) {
s = malloc(sizeof(Node)); /* 生成新结点(C语言标准函数) */
s->data = e;
s->next = (*L)->rear->next; /* 将p的后继结点赋值给s的后继 */
(*L)->rear->next = s; /* 将s赋值给p的后继 */
(*L)->rear = s;
return 0;
}
s = malloc(sizeof(Node)); /* 生成新结点(C语言标准函数) */
s->data = e;
s->next = (*L)->rear->next->next; /* 将p的后继结点赋值给s的后继 */
(*L)->rear->next->next =s ; /* 将s赋值给p的后继 */
return OK;
}
/* 初始条件:链式线性表L已存在,1≤i≤ListLength(L), */
/* 操作结果:在L表尾插入新的数据元素e,L的长度加1 */
Status ListInsertTail(LinkListLast* L, ElemType e)
{
Node * s;
if (!(*L)) {
return ERROR;
}
s = malloc(sizeof(Node)); /* 生成新结点(C语言标准函数) */
s->data = e;
s->next = (*L)->rear->next; /* 将p的后继结点赋值给s的后继 */
(*L)->rear->next = s; /* 将s赋值给p的后继 */
(*L)->rear = s;
return OK;
}
/* 初始条件:链式线性表L已存在,1≤i≤ListLength(L) */
/* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */
Status ListDelete(LinkListLast* L, int i, ElemType* e)
{
int j;
Node *p,*q;
j = 1;
p = (*L)->rear->next->next;
q = malloc(sizeof(Node));
while (p != (*L)->rear->next && j < i) /* 遍历寻找第i个元素 */
{
q = p;
p = p->next;
++j;
}
if (p == (*L)->rear->next || j > i) {
return ERROR; /* 链表为空或第i个元素不存在 */
}
if (p == (*L)->rear) {
q->next = p->next;
*e = p->data;
(*L)->rear = q;
free(p);
return OK;
}
q->next = p->next;
*e = p->data;
free(p);
return OK;
}
/* 初始条件:链式线性表L已存在 */
/* 操作结果:依次对L的每个数据元素输出 */
Status ListTraverse(LinkListLast L)
{
Node* p = L->rear->next->next;
while (p!=L->rear->next)
{
visit(p->data);
p = p->next;
}
printf("\n");
return OK;
}
int main()
{
LinkListLast L;
ElemType e;
Status i;
int j, k;
i = InitList(&L);
printf("初始化L后:ListLength(L)=%d\n", ListLength(L));
for (j = 1; j <= 10; j++)
ListInsertTail(&L, j);
printf("在L的表尾依次插入1~10后:L.data=");
ListTraverse(L);
for (j = 12; j <= 17; j++)
i = ListInsert(&L,3, j,ListLength(L));
printf("在L的第三个位置依次插入12~17后:L.data=");
ListTraverse(L);
printf("ListLength(L)=%d \n", ListLength(L));
i = ListEmpty(L);
printf("L是否空:i=%d(1:是 0:否)\n", i);
i = ClearList(&L);
printf("清空L后:ListLength(L)=%d\n", ListLength(L));
i = ListEmpty(L);
printf("L是否空:i=%d(1:是 0:否)\n", i);
for (j = 1; j <= 10; j++)
ListInsertHead(&L, j);
printf("在L的表头依次插入1~10后:L.data=");
ListTraverse(L);
printf("ListLength(L)=%d \n", ListLength(L));
ListInsertHead(&L, 0);
printf("在L的表头插入0后:L.data=");
ListTraverse(L);
printf("ListLength(L)=%d \n", ListLength(L));
GetElem(L, 5, &e);
printf("第5个元素的值为:%d\n", e);
for (j = 3; j <= 4; j++)
{
k = LocateElem(L, j);
if (k)
printf("第%d个元素的值为%d\n", k, j);
else
printf("没有值为%d的元素\n", j);
}
k = ListLength(L); /* k为表长 */
for (j = k + 1; j >= k; j--)
{
i = ListDelete(&L, j, &e); /* 删除第j个数据 */
if (i == ERROR)
printf("删除第%d个数据失败\n", j);
else
printf("删除第%d个的元素值为:%d\n", j, e);
}
printf("依次输出L的元素:");
ListTraverse(L);
j = 5;
ListDelete(&L, j, &e); /* 删除第5个数据 */
printf("删除第%d个的元素值为:%d\n", j, e);
printf("依次输出L的元素:");
ListTraverse(L);
i = ClearList(&L);
printf("\n删除L后:ListLength(L)=%d\n", ListLength(L));
return 0;
}
怎么样让指针指向尾结点C语言,带头结点头指针与带头结点尾指针的学习相关推荐
- c语言循环链表中设立尾链表,C语言实现双向非循环链表(带头结点尾结点)的节点插入...
对于双向链表,个人推荐使用带头结点尾结点的方式来处理会比较方便.我在<C语言实现双向非循环链表(不带头结点)的节点插入>中详细实现了在不带头结点的情况下的插入.这次我们将会来在使用头结点尾 ...
- c语言指针指向怎么指,C语言-基础教程-指向指针的指针
一个指针变量可以指向整型变量.实型变量.字符类型变量,当然也可以指向指针类型变量.当这种指针变量用于指向指针类型变量时,我们称之为指向指针的指针变量,这话可能会感到有些绕口,但你想到一个指针变量的地址 ...
- c语言指针指向字符串单个,C语言 有没有可能调用一个指向字符串的函数指针?...
我是个在学C的萌新,一天突发奇想,指令和数据只是对人来说才有意义, 一段二进制串对CPU来说既可是数据,也可是指令,IP指向哪里就当作指令执行.那这样的话是不是意味着可以在C中执行字符串呢? 可,在探 ...
- 【C 语言】二级指针作为输入 ( 自定义二级指针内存 | 二级指针排序 | 通过 交换指针指向的内存数据 方式进行排序 )
文章目录 一.二维指针 排序 ( 通过 交换指针指向的内存数据 方式进行排序 ) 二.完整代码示例 一.二维指针 排序 ( 通过 交换指针指向的内存数据 方式进行排序 ) 在上一篇博客 [C 语言]二 ...
- 【精华文】C语言结构体特殊情况分析:结构体指针 / 基本数据类型指针,指向其他结构体
参考链接:Structure pointer pointing to different structure instance 注:可以查看此篇的问题和唯一的回复,那是相对正确的,不要看comment ...
- c语言指针的地址存放,c语言 - *指针 和 地址
最近在研究oc的底层,全是c/c++的代码,虽然以前学过也写过,其实不怎么用都忘得差不多了. 首先我们来了解一下 * 和 &这两个符号 通俗点儿理解其实&地址就是就是一个存放地址的变量 ...
- 『C语言从入门到进阶』第 ⑥ 期 - 初识指针
学习导航:> 1.指针是什么? 2.指针和指针类型 2.1指针+-整数 2.2指针的解引用 3.野指针 3.1野指针成因 3.2如何规避野指针 4.指针运算 4.1指针+-整数 4.2指针-指针 ...
- C语言调用 free 函数释放内存后指针指向及内存中的值是否改变的问题
文章目录 1. 前言 2. 正文 2.1. "分配" 与 "释放" 2.2. 运行测试 2.2.1. VSCode 下使用 gcc 编译 2.2.2. VS20 ...
- 让指针指向初始位置c语言,初始C语言中的指针(翁凯男神MOOC)
运算符 & ●scanf("%d",&i); ●获得变量的地址,它的操作数必须是变量 ● int i; printf("%x",&i) ...
最新文章
- logback配置文件
- 释疑の资源短缺DATASET_CANT_CLOSE
- jdk注解_我们正在下注:这个注解很快就会出现在JDK中
- Linux开机启动过程(15):start_kernel()->rcu_init()初始化
- oracle视图查询机制,物化视图及日志内部机制的一点研究
- CSS自定义动画@keyframes的使用
- 第015讲 仿sohu首页面布局
- 基于matlab的64QAM通信系统的仿真
- postman设置成中文
- DBMS_AUDIT_MGMT.FLUSH_UNIFIED_AUDIT_TRAIL过程
- 用sk-learn实现新闻的分类预测(完整代码)
- 三层交换机配置实现不同网络互通
- 【小Game】C++ - EGE - 躲避球小游戏
- Fatal error: The slave I/O thread stops because master and slave have equal MySQL server UUIDs
- js-url转换blob以及blob与base64的相互转换
- LVS负载均衡—基于Keepalived做高可用
- 你管这叫操作系统源码(五)
- 名正则言顺�谈服装品牌名称(二)
- kali安装keylogger_小白日记48:kali渗透测试之Web渗透-XSS(二)-漏洞利用-键盘记录器,xsser...
- 新时达服务器说明书_新时达SM-01-F5021使用说明书V6.0