1.单链表:

  在顺序表中,用一组地址连续的存储单元来一次存放线性表的结点,因此结点的逻辑顺序与物理顺序是一致的。但链表却不同,链表是用一组任意的存储单元来存放 线性表的结点,这组存储单元可以是连续的,也可以是非连续的,甚至是零散分布在内存的任何位置上。因此,链表中结点的逻辑顺序与物理顺序不一定相同。为了正确表示节点间的逻辑关系,必须在存储线性表的每个数据元素的同时,存储指示其后继结点的地址信息,这两部分信息共同构成了单链表结点的结构,如下图:

结点包括两个域,数据域用来存放结点的值,指针域用来存储数据元素的直接后继的地址(或位置)。线性表正是通过每个结点的指针域将线性表的n个结点按其逻辑顺序连接在一起的。由于此线性表的每个节点只有一个next指针域,故将这种链表叫做单链表。

由于单链表中的每一个结点除了第一个节点外,它们的存储地址存放在其前驱结点的指针域中,由于第一个节点无前驱,所以应该设一个头指针指向第一个节点,本文中设置了first指针指向第一个结点。单链表中的最后一个节点无直接后继,所以指定单链表的最后一个结点的指针域为"空"(NULL)。

  一般情况下,使用链表,只关心链表中结点的逻辑顺序,并不关心每个结点的实际存储位置,因此通常用箭头来表示链域中的指针,于是链表就可以更直观地画成用箭头链接起来的结点序列,如图:

  有时候,为了操作的统一方便,可以在单链表的第一个结点前附设一个头结点,头结点的数据域可以存储一些关于线性表的长度等附加信息,也可以不存储任何信息,对头结点的数据域无特别规定,而头结点的指针域用来存储指向第一个结点的指针(即第一个结点的存储位置)。如果线性表为空,则头结点的指针域为"空"。如图所示:

      

2.单链表操作

   头插过程:

  头插的方式如上图所示。采用头插法得到的单链表的逻辑顺序与输入元素顺序相反,亦称头插法为逆序建表法。在这只介绍头插法的示意图,对于尾插、头删、尾删、可以参考《数据结构----用C语言描述》(耿国华主编)这本书。下面的代码中,实现了头插、尾插、头删、尾删,读者可以仔细研究研究。(本文所编写的代码是在VS2013编译环境下运行的)

3.运行代码:

  Linklist.h文件包括各个操作函数的声明以及包含的头文件,test.c包含了测试代码,Linklist.c文件里主要是各个操作函数的具体实现。

 //Linklist.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h> typedef int LDataType;
typedef struct Linklist
{
LDataType data;
struct Linklist *next;
}Linklist, *pLinklist; //接口函数
pLinklist BuyNewNode(LDataType data);//动态生成新节点
void InitLinklist(pLinklist *pL);//初始化单链表
void PushBackLinklist(pLinklist *pL, LDataType data);//尾插
void PushFrontLinklist(pLinklist *pL, LDataType data);//头插
void PopBackLinklist(pLinklist *pL);//尾删
void PopFrontLinklist(pLinklist *pL);//头删
void PrintLinklist(Linklist *pL);//打印单链表
pLinklist FindLinklist(pLinklist *pL, LDataType data);//查找指定元素,返回指定元素的位置
void InsertLinklist(pLinklist *pL, pLinklist p, LDataType data);//指定位置插入
void RemoveLinklist(pLinklist *pL, LDataType data);//删除第一个指定元素
void RemoveAllLinklist(pLinklist *pL, LDataType data);//删除所有的指定元素
int IsEmptyLinklist(pLinklist pL);//判断链表是否为空,为空返回1;不为空返回0;
void DestoryLinklist(pLinklist *pL);//销毁单链表
 //Linklist.c
#include"Linklist.h"
pLinklist BuyNewNode(LDataType data)//生成新节点
{
pLinklist NewNode = (pLinklist)malloc(sizeof(Linklist));
if (NewNode == NULL)
{
printf("动态开辟内存空间失败\n");
return;
}
NewNode->data = data;
NewNode->next = NULL;
return NewNode;
}
void InitLinklist(pLinklist *pL)//初始化
{
assert(pL != NULL);
(*pL) = NULL;
}
void PushBackLinklist(pLinklist *pL, LDataType data)//尾插
{
assert(pL != NULL);
pLinklist NewNode = BuyNewNode(data);
if (*pL == NULL)
{
*pL = NewNode;
return;
}
pLinklist cur = *pL;
while (cur->next)
{
cur = cur->next;
}
cur->next = NewNode;
}
void PushFrontLinklist(pLinklist *pL, LDataType data)//头插
{
assert(pL != NULL);
pLinklist NewNode = BuyNewNode(data);
if (*pL == NULL)
{
*pL = NewNode;
return;
}
NewNode->next = *pL;
*pL = NewNode;
}
int IsEmptyLinklist(pLinklist pL)//判断是否为空链表
{
if (pL == NULL)
return ;
return ;
}
void PopBackLinklist(pLinklist *pL)//尾删
{
assert(pL != NULL);
if (IsEmptyLinklist(*pL))//链表为空,没有节点
{
printf("链表为空,删除操作失败\n");
return;
}
pLinklist cur = *pL;
pLinklist pre = NULL;//保存cur的前一个节点
if (cur->next == NULL)//有一个节点
{
*pL = NULL;
free(cur);
cur = NULL;
return;
}
while (cur->next)
{
pre = cur;
cur = cur->next;
}
pre->next = NULL;
free(cur);
cur = NULL;
}
void PopFrontLinklist(pLinklist *pL)//头删
{
assert(pL != NULL);
if (*pL == NULL)
{
printf("链表为空,删除操作失败\n");
return;
}
pLinklist cur = *pL;
*pL = cur->next;
free(cur);
cur = NULL;
}
pLinklist FindLinklist(pLinklist *pL, LDataType data)//查找指定元素,返回指定元素的位置
{
assert(pL != NULL);
pLinklist cur = *pL;
while (cur)
{
if (cur->data == data)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void InsertLinklist(pLinklist *pL, pLinklist p, LDataType data)//指定位置前面插入
{
assert(pL != NULL);
pLinklist NewNode = BuyNewNode(data);
pLinklist cur = *pL;
while (cur->next != p)
{
cur = cur->next;
}
NewNode->next = p;
cur->next = NewNode;
}
void RemoveLinklist(pLinklist *pL, LDataType data)//删除第一个指定元素
{
assert(pL != NULL);
pLinklist cur = NULL;
pLinklist p = *pL;
pLinklist pre = NULL;
cur = FindLinklist(pL, data);//找到要删除的指定元素
if (cur == NULL)
{
printf("没找到要删除的指定元素,删除失败\n");
return;
}
if (*pL == cur)//位于第一个节点
{
*pL= cur->next;
free(cur);
cur = NULL;
return;
}
while (p!= cur)
{
pre = p;
p = p->next;
}
pre->next = cur->next;
free(cur);
cur = NULL;
}
void RemoveAllLinklist(pLinklist *pL, LDataType data)//删除所有的指定元素
{
assert(pL != NULL);
pLinklist cur = NULL;
pLinklist p = *pL;
pLinklist pre = *pL;
while (p)
{ if (p->data == data && (*pL) == p)
{
pre = p;
p = p->next;
*pL = p;
free(pre);
pre = NULL;
}
else if (p->data == data)
{
cur = p;
p = p->next;
pre->next = p;
free(cur);
cur = NULL;
}
else
{
pre = p;
p = p->next;
}
} }
void PrintLinklist(Linklist *pL)//打印单链表
{
pLinklist cur = pL;
while (cur)
{
printf("%d-->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
void DestoryLinklist(pLinklist *pL)//销毁单链表,放置内存溢出
{
assert(pL != NULL);
pLinklist cur = *pL;
pLinklist pre = NULL;//保存cur的前一个节点
if (*pL == NULL)
{
printf("链表为空\n");
return;
}
if (cur->next == NULL)//只有一个节点
{
*pL = NULL;
free(cur);
cur = NULL;
return;
}
while (cur)
{
pre = cur;
cur = cur->next;
free(pre);
pre = NULL;
}
}
 //test.c测试函数
#include"Linklist.h" void test()
{
pLinklist cur = NULL;//用来接收FindLinklist的返回值
Linklist *first = NULL;
InitLinklist(&first);//初始化
//PushBackLinklist(&first, 1);//尾插元素
//PushBackLinklist(&first, 2);
//PushBackLinklist(&first, 3);
//PushBackLinklist(&first, 4);
//PushBackLinklist(&first, 5);
//PrintLinklist(first);//打印单链表
PushFrontLinklist(&first, );//头插元素
PushFrontLinklist(&first, );
PushFrontLinklist(&first, );
PushFrontLinklist(&first, );
PushFrontLinklist(&first, );
//PopBackLinklist(&first);//尾删一个节点
//PopFrontLinklist(&first);//头删一个节点
//PrintLinklist(first);//打印单链表
//DestoryLinklist(&first);
//cur = FindLinklist(&first, 8);
//InsertLinklist(&first, cur, 11);
//printf("在8前面插入11得:");
//PrintLinklist(first);//打印单链表
//printf("删除11得:");
//RemoveLinklist(&first, 11);
//PrintLinklist(first);
PushFrontLinklist(&first, );
PushFrontLinklist(&first, );
//RemoveLinklist(&first, 7);
RemoveAllLinklist(&first, );
PrintLinklist(first); //RemoveAllLinklist(&first, 7);//删除所有的7
}
int main()
{
test();
system("pause");
return ;
}

 4.尾插法建立单链表如下图:

  测试图:                                                                                                                         运行图:

 

 5.头插法建立单链表如下图:

   测试图:                                                                                    运行图:

 

6.头删和尾删一个结点:

  测试图:                                                                              运行图:

 

 7.在指定位置插入元素:

  测试图:                                                                          运行图:

 

   除这几个操作外,还有删除指定元素RemoveLinklist(删除第一个找到的指定元素),删除所有的指定元素RemoveAllLinklist。因为本文中单链表的结点是动态开辟的,因此还要实现销毁函数DestoryLinklist,防止内存泄漏。

无头结点的单链表(C语言)的更多相关文章

  1. 不带头结点的单链表------C语言实现

    File name:no_head_link.c Author:SimonKly Version:0.1 Date: 2017.5.20 Description:不带头节点的单链表 Funcion L ...

  2. 不带头结点的单链表(基于c语言)

    本篇文章的代码大多使用无头结点的单链表: 相关定义: #include <stdio.h> #include <stdlib.h> #include <assert.h& ...

  3. c语言实现--不带头结点的单链表操作

    1,不带头结点的单链表操作中,除了InitList(),GetElem(),ListInsert(),ListDelete()操作与带头结点的单链表有差别外,其它的操作基本上一样. 2,不带头结点单链 ...

  4. java编写带头结点的单链表

    最近在牛客网上练习在线编程,希望自己坚持下去,每天都坚持下去练习,给自己一个沉淀,不多说了 我遇到了一个用java实现单链表的题目,就自己在做题中将单链表完善了一下,希望大家作为参考也熟悉一下,自己 ...

  5. 单链表 C语言 学习记录

    概念 链接方式存储 链接方式存储的线性表简称为链表(Linked List). 链表的具体存储表示为: 用一组任意的存储单元来存放线性表的结点(这组存储单元既可以是连续的,也可以是不连续的). 链表中 ...

  6. C/C++中创建(带头结点、不带头结点的)单链表

    1.带头结点的单链表(推荐使用带头结点的单链表)(采用尾插法) 了解单链表中节点的构成 从上图可知,节点包含数据域和指针域,因此,在对节点进行定义时,我们可以如下简单形式地定义: /* 定义链表 */ ...

  7. 链表习题(2)-一个集合用带头结点的单链表L表示,编写算法删除其值最大的结点。

    /*一个集合用带头结点的单链表L表示,编写算法删除其值最大的结点.*/ /* 算法思想:使用pre,p,premax,max四个指针,pre和p进行比较,premax和max进行最后的删除操作 通过遍 ...

  8. 链表习题(1)-设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点

    /*设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点*/ /* 算法思想:设f(L,x)的功能是删除以L为首结点指针的单链表中所有值等于x的结点, 则显然有f(L->next,x)的 ...

  9. 有一个线性表,采用带头结点的单链表L来存储,设计一个算法将其逆置,且不能建立新节点,只能通过表中已有的节点的重新组合来完成。

    有一个线性表,采用带头结点的单链表L来存储,设计一个算法将其逆置,且不能建立新节点,只能通过表中已有的节点的重新组合来完成. 分析:线性表中关于逆序的问题,就是用建立链表的头插法.而本题要求不能建立新 ...

随机推荐

  1. 解决Android启动显示空白界面的问题

    Android程序启动时,第一个看的界面并不是我们的指定的第一个Activity界面,而是显示了一个空白的界面,带标题栏的,但是界面什么内容都没有,这个界面只显示不到1秒左右的时间就会切换到我们的第一 ...

  2. Windows Server 2012如何实现双网卡绑定

    在windows server 2012 之前我们在服务器上如果要实现双网卡绑定则需要向服务器厂家所要相应的软件,但是现在强大的windows server 2012的到来使我们省去了所有的麻烦,因为 ...

  3. 661. Image Smoother

    static int wing=[]() { std::ios::sync_with_stdio(false); cin.tie(NULL); ; }(); class Solution { publ ...

  4. 2018.08.28 洛谷P3345 [ZJOI2015]幻想乡战略游戏(点分树)

    传送门 题目就是要求维护带权重心. 因此破题的关键点自然就是带权重心的性质. 这时发现直接找带权重心是O(n)的,考虑优化方案. 发现点分树的树高是logn级别的,并且对于以u为根的树,带权重心要么就 ...

  5. 2018.08.17 洛谷P3135 [USACO16JAN]堡哞(前缀和处理)

    传送门 有趣的前缀和. 数据范围中的n≤200" role="presentation" style="position: relative;"> ...

  6. 2018.07.20 bzoj2152: 聪聪可可(点分治)

    传送门 本蒟蒻AC的第二道点分治,调了30min" role="presentation" style="position: relative;"&g ...

  7. webuploader php上传视频

    webuploader 上传大视频文件 在网上找了一个,自己重新组合了下,两个主要的文件,再加上官方下载的文件.其中有几个重要的点. 1.上传存放视频目录为了测试 直接777 2.fileupload ...

  8. The First Android App----Starting Another Activity

    To respond to the button's on-click event, open the activity_main.xml layout file and add the androi ...

  9. 4张图看懂delphi 10生成ipa和在iPhone虚拟器上调试(教程)

    4张图看懂delphi 10生成ipa和在iPhone虚拟器上调试(教程) (2016-02-01 03:21:06) 转载▼ 标签: delphi ios delphi10 教程 编程 分类: 编程 ...

  10. Android 如何修改gen下包的名字

    前言 当将项目中包进行重命名后,所有包名字修改了,但是在gen目录下android sdk 自动生成的包名并没有修改,如果要引用R.java 中的字段, 又得import以前的包名字.   原因 出现 ...