单链表(带头结点)的存储结构与基本操作(c语言)------亲测可用
编程语言:c语言
编译环境:Dev-c++
实现功能:实现功能:单链表(带头结点)结点结构体的定义,单链表(带头结点)初始化、求元素个数、插入元素、删除元素、取元素、打印所有元素、就地逆置、撤销的操作。
- 结构体定义
- 初始化
- 求元素个数
- 插入元素
- 删除元素
- 取元素
- 打印所有元素
- 就地逆置
- 撤销
- 测试及完整代码
结构体定义
定义如下图结点结构体,很显然单链表是链式存储结构,逻辑上相邻的两个元素,物理地址不一定相邻。
typedef struct Node //定义单链表结点结构体
{DataType data;struct Node *next;
} SLNode;
带头结点单链表结构如下图。
既然涉及到头指针,那当然要区分一下头指针、头结点、首元结点,如下图是带头结点的单链表。
初始化
算法思想:将头·结点的指针域赋为NULL。
为什么使用**head?
首先声明指针就是地址,头指针即我们定义的结构体变量的地址,指针的指针即存放这个地址的内存单元的地址。因为我们要初始化一个头指针,那么我们就要操作它的地址空间,即使用指针的指针。在这个函数中,我们输入头指针的指针,*head即为取这个头指针,(*head) ->next = NULL即为初始化头指针指向结点结构体的指针域值为NULL。对照带头结点单链表结构图,我们可以很清晰的理解。
void ListInitiate(SLNode **head) //初始化
{*head = (SLNode*)malloc(sizeof(SLNode)); //申请头节点 (*head) ->next = NULL; //置结束标记NULL
}
求元素个数
算法思想:设置零时指针和计数器,零时指针每移动一位,计数器加一,直到遍历完单链表,返回最终计数值,如下图。
int ListLength(SLNode *head) //求当前数据元素个数
{SLNode *p = head;int size = 0;while(p->next != NULL) //指针位置移动,size增加 {p = p->next;size++;} return size;}
插入元素
算法思想:设置零时指针,移动后指向第i-1个元素,再进行连接和断链,如下图。
int ListInsert(SLNode *head,int i,DataType x) //在第i(0<=i<=size)个位置前插入数据元素值x
{SLNode *p,*q;int j=-1;p=head;while(p->next!=NULL && j<i-1) //定位,使p指向第i-1个元素 {p=p->next;j++;}if(j!=i-1) //当i的值不合法时则返回 {printf("插入元素位置参数错!"); return 0; }q = (SLNode *)malloc(sizeof(SLNode)); //申请新结点 q->data = x; //新结点数据域赋值 q->next = p->next; //插入步骤① p->next = q; //插入步骤② return 1;}
删除元素
算法思想:设置零时指针,移动后指向第i-1个元素,再进行连接和断链,如下图。
int ListDelete(SLNode *head,int i,DataType *x) //删除第i(0<=i<=size-1)个位置处的数据元素并保存到x中
{SLNode *p,*s;int j=-1;p=head;while(p->next!=NULL && j<i-1) //定位,使p指向第i-1个元素 {p=p->next;j++;}if(j!=i-1) //当i的值不合法时则返回 {printf("插入元素位置参数错!");return 0; }s = p->next; //指针s指向第i个结点 *x=s->data; //把指针s所指结点数据域指赋给x p->next = p->next->next; //删除free(s); return 1; }
取元素
算法思想:设置零时指针,移动后指向第i-1个元素,再取出下个元素数据域的值。
int ListGet(SLNode *head,int i,DataType *x) //取第i(0<=i<=size)个位置处的数据元素并保存到x中 ,成功返回1,失败返回0
{SLNode *p;int j=-1;p= head;while(p->next!=NULL && j<i-1) //定位,使p指向第i-1个元素 {p=p->next;j++;}if(j!=i-1) //当i的值不合法时则返回 {printf("插入元素位置参数错!");return 0; } *x = p->next->data; //指针p所指第i个结点数据域的值赋给x return 1; }
打印所有元素
算法思想:设置零时指针遍历单链表,每移动一次打印一个值。
void Print(SLNode *head) //打印单链表
{SLNode *p;p=head;while(p->next!=NULL){printf("%d ",p->next->data);p=p->next;}}
就地逆置
算法思想:设置两个零时指针,首先将头结点断链,然后将剩余的元素逆序插到头结点之后,如下图。
void Reverse(SLNode *head) //单链表就地逆置
{SLNode *p,*q;p=head->next;head->next=NULL; //头结点断链 while(p!=NULL) //将元素逆序插到头结点后面 {q=p;p=p->next;q->next=head->next;head->next=q;}}
撤销
算法思想:释放单链表的结点,需要输入头结点的地址。
void Destory(SLNode **head) //撤销单链表
{SLNode *p,*q;p=*head;while(p!=NULL){q=p;p=p->next;free(q);}*head = NULL;
}
测试及完整代码
我们在Dev-c++创建console application的c项目,并添加三个工程文件:test.c,SLNode.c,SLNode.h。步骤如下:
- 新建项目,选console application(控制台应用程序)、c项目。
- 给项目添加三个文件,再编译运行。
运行结果:
完整代码:
test.c:
#include <stdio.h>
#include"SLNode.h"int main()
{int i;int x;SLNode *head;ListInitiate(&head);for(i=0;i<10;i++) //插入10个元素 {ListInsert(head,i,i*i); }printf("插入10个元素后单链表表中的元素依次为:");Print(head);printf("\n");ListDelete(head,5,&x); //删除第0个位置处元素printf("删除第5个位置处元素后单链表中的元素依次为:");Print(head);printf("\n");printf("删除的元素为:%d",x);printf("\n");ListGet(head,4,&x); printf("取第4个位置处元素元素为:%d",x);printf("\n");Reverse(head); //就地逆置 printf("逆置后单链表中的元素依次为:");Print(head);Destory(&head);return 0;
}
SLNode.h:
#ifndef _SLNODE_H
#define _SLNODE_H
typedef int DataType;typedef struct Node //定义单链表结点结构体
{DataType data;struct Node *next;
} SLNode;void ListInitiate(SLNode **head); //初始化
int ListLength(SLNode *head); //求当前数据元素个数
int ListInsert(SLNode *head,int i,DataType x); //在第i(0<=i<=size)个位置前插入数据元素值x
int ListDelete(SLNode *head,int i,DataType *x); //删除第i(0<=i<=size)个位置处的数据元素并保存到x中
int ListGet(SLNode *head,int i,DataType *x); //取第i(0<=i<=size)个位置处的数据元素并保存到x中 ,成功返回1,失败返回0
void Print(SLNode *head); //打印单链表
void Reverse(SLNode *head); //单链表就地逆置
void Destory(SLNode **head); //撤销单链表 #endif
SLNode.c:
#include<stdio.h>
#include<malloc.h>
#include"SLNode.h"void ListInitiate(SLNode **head) //初始化
{*head = (SLNode*)malloc(sizeof(SLNode)); //申请头节点 (*head) ->next = NULL; //置结束标记NULL
} int ListLength(SLNode *head) //求当前数据元素个数
{SLNode *p = head;int size = 0;while(p->next != NULL) //指针位置移动,size增加 {p = p->next;size++;} return size;} int ListInsert(SLNode *head,int i,DataType x) //在第i(0<=i<=size)个位置前插入数据元素值x
{SLNode *p,*q;int j=-1;p=head;while(p->next!=NULL && j<i-1) //定位,使p指向第i-1个元素 {p=p->next;j++;}if(j!=i-1) //当i的值不合法时则返回 {printf("插入元素位置参数错!"); return 0; }q = (SLNode *)malloc(sizeof(SLNode)); //申请新结点 q->data = x; //新结点数据域赋值 q->next = p->next; //插入步骤① p->next = q; //插入步骤② return 1;} int ListDelete(SLNode *head,int i,DataType *x) //删除第i(0<=i<=size-1)个位置处的数据元素并保存到x中
{SLNode *p,*s;int j=-1;p=head;while(p->next!=NULL && j<i-1) //定位,使p指向第i-1个元素 {p=p->next;j++;}if(j!=i-1) //当i的值不合法时则返回 {printf("插入元素位置参数错!");return 0; }s = p->next; //指针s指向第i个结点 *x=s->data; //把指针s所指结点数据域指赋给x p->next = p->next->next; //删除free(s); return 1; } int ListGet(SLNode *head,int i,DataType *x) //取第i(0<=i<=size)个位置处的数据元素并保存到x中 ,成功返回1,失败返回0
{SLNode *p;int j=-1;p= head;while(p->next!=NULL && j<i-1) //定位,使p指向第i-1个元素 {p=p->next;j++;}if(j!=i-1) //当i的值不合法时则返回 {printf("插入元素位置参数错!");return 0; } *x = p->next->data; //指针p所指第i个结点数据域的值赋给x return 1; } void Print(SLNode *head) //打印单链表
{SLNode *p;p=head;while(p->next!=NULL){printf("%d ",p->next->data);p=p->next;}} void Reverse(SLNode *head) //单链表就地逆置
{SLNode *p,*q;p=head->next;head->next=NULL; //头结点断链 while(p!=NULL) //将元素逆序插到头结点后面 {q=p;p=p->next;q->next=head->next;head->next=q;}} void Destory(SLNode **head) //撤销单链表
{SLNode *p,*q;p=*head;while(p!=NULL){q=p;p=p->next;free(q);}*head = NULL;
}
总结一下,单链表中初始化操作的时间复杂度是O(1),其他操作的时间复杂度为O(n),作为链式存储结构的单链表,操作都涉及到指针,结构体,所以理解这两个知识点就非常重要了。源代码在我的博客资源区,点击文字链接直达
目录 一.链表(链式存储结构)的特点 二.单链表的定义和表示 1.带头结点的单链表 2.单链表的存储结构 三.单链表基本操作的实现 1.单链表的初始化(带头结点的单链表) 2.补充单链表的几个常用简单 ... 单链表:带头结点与不带头结点详解 在本人的另一篇文章中,通过java实现了带头结点和不带头结点的单链表,有兴趣的小伙伴可以去看下数据结构(一):链表(Linked List) 这两天回顾下链表的知识, ... 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.单链表:带头结点跟不带头结点 二.使用步骤 总结 前言 数据结构中单链表的创建 带头结点跟不带头结点的区别 一.单 ... 结构(带头结点) 初始化 创建(n个结点) 插入 删除 遍历 销毁 结构: 判空:(head->next==head) 初始化:主要注意链表为空的条件,执行语句:L->next=L; vo ... 今天和大家分享一道2009年代码为408的一道真题: 已知一个带有表头的单链表,结点结构为data-link,假设该链表只给出了头指针list.在不改变链表的前提下,请设计一个尽可能高效的算法,查找链 ... /*实验2 1. 写一个函数DeleteRange删除单链表中结点的值在low 和high之间的结点 (low和high的值是多少可自由设计).并且要在程序中验证其功能实现. (可在实验1的第3题的基 ... 已知线性表最多可能有20个元素,存储每个元素需要8字节,存储每个指针需要4字节.当元素个数为( 大于等于13 )时使用单链表比使用数组存储此线性表更加节约空间. 使用数组存储线性表需要提前分配好数组空 ... 狸猫换太子–删除无头单链表中结点 @(算法学习) 学习自<编程之美>. 很有意思的一种做法. 给定一个没有头指针的单链表,一个指针指向次单链表中的一个中间结点,删除此结点. 分析:这种根本 ... 广州大学学生实验报告 开课实验室:计算机科学与工程实验(电子楼418A) 2019年4月27日 学院 计算机科学与教育软件学院 年级.专业.班 计算机科学与技术172班 姓名 xxx 学号 17061 ...单链表(带头结点)的存储结构与基本操作(c语言)------亲测可用相关推荐
最新文章
热门文章