1,为什么要用到链表

数组作为存放同类数据的集合,给我们在程序设计时带来很多的方便,增加了灵活性。但数组也同样存在一些弊病。如数组的大小在定义时要事先规定,不能在程序中进行调整,这样一来,在程序设计中针对不同问题有时需要3 0个大小的数组,有时需要5 0个数组的大小,难于统一。我们只能够根据可能的最大需求来定义数组,常常会造成一定存储空间的浪费。

我们希望构造动态的数组,随时可以调整数组的大小,以满足不同问题的需要。链表就是我们需要的动态数组。它是在程序的执行过程中根据需要有数据存储就向系统要求申请存储空间,决不构成对存储区的浪费。

链表是一种复杂的数据结构,其数据之间的相互关系使链表分成三种:单链表、循环链表、双向链表,下面将逐一介绍。

2,单向链表

单链表有一个头节点head,指向链表在内存的首地址。链表中的每一个节点的数据类型为结构体类型,节点有两个成员:整型成员(实际需要保存的数据)和指向下一个结构体类型节点的指针即下一个节点的地址(事实上,此单链表是用于存放整型数据的动态数组)。链表按此结构对各节点的访问需从链表的头找起,后续节点的地址由当前节点给出。无论在表中访问那一个节点,都需要从链表的头开始,顺序向后查找。链表的尾节点由于无后续节点,其指针域为空,写作为NULL。

如图所示

上图还给出这样一层含义,链表中的各节点在内存的存储地址不是连续的,其各节点的地址是在需要时向系统申请分配的,系统根据内存的当前情况,既可以连续分配地址,也可以跳跃式分配地址。

3,单向链表程序的实现
(1),链表节点的数据结构定义

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
struct node
{
int num;
struct node *p;
} ;
在链表节点的定义中,除一个整型的成员外,成员p是指向与节点类型完全相同的指针。

在链表节点的数据结构中,非常特殊的一点就是结构体内的指针域的数据类型使用了未定义成功的数据类型。这是在C中唯一规定可以先使用后定义的数据结构。

(2),链表的创建、输出步骤
单链表的创建过程有以下几步:

1 ) 定义链表的数据结构;

2 ) 创建一个空表;

3 ) 利用malloc ( )函数向系统申请分配一个节点;

4 ) 将新节点的指针成员赋值为空。若是空表,将新节点连接到表头;若是非空表,将新

节点接到表尾;

5 ) 判断一下是否有后续节点要接入链表,若有转到3 ),否则结束;

单链表的输出过程有以下几步

1) 找到表头;

2) 若是非空表,输出节点的值成员,是空表则退出;

3 ) 跟踪链表的增长,即找到下一个节点的地址;

4) 转到2 ).

(3),程序代码例子:

创建一个存放正整数单链表,输入0或小于0的数,结束创建链表,并打印出链表中的值,程序如下:

//
//  main.cpp
//  List链表
//
//  Created by 于磊 on 16/8/19.
//  Copyright © 2016年 于磊. All rights reserved.
//# include <stdio.h>
//# include <malloc.h>
# include <stdlib.h>typedef struct Node
{int data;struct Node * pNext;
} * PNODE, NODE;PNODE establish_list (void);
void traverse_list (PNODE pHead);
bool is_empty(PNODE pHead);
int length_list(PNODE pHead);
void sort_list(PNODE pHead);
void insert_list(PNODE pHead, int pos, int val);
int delete_list(PNODE pHead, int pos, int val);
void freeer(PNODE pHead);int main(void)
{PNODE pHead;int len, i, j, val;pHead = establish_list();traverse_list(pHead);if(is_empty(pHead))printf("链表为空\n");elseprintf("链表不空\n");len = length_list(pHead);printf("链表的长度为: %d\n", len);sort_list(pHead);traverse_list(pHead);printf("请输入您要在第几个节点插入\n");scanf("%d", &i);printf("请输入您要在第%d个节点插入的值\n", i);scanf("%d", &j);insert_list(pHead, i, j);traverse_list(pHead);printf("请输入您要第几个删除的节点\n");scanf("%d", &i);val = delete_list(pHead, i, val);printf("您删除的节点值为: %d\n", val);traverse_list(pHead);freeer(pHead);return 0;
}PNODE establish_list(void)//初始化链表,返回头结点地址
{int val, len;PNODE Tem;PNODE pNew;PNODE pHead;pHead = (PNODE)malloc(sizeof(NODE));Tem = pHead;if(NULL == pHead){printf("分配失败");exit(-1);}Tem->pNext = NULL;printf("请输入您要定义节点的长度: ");scanf("%d", &len);for (int i=0;i<len;++i){printf("请输入第%d个节点的值: ", i+1);scanf("%d", &val);pNew = (PNODE)malloc(sizeof(NODE));if(NULL == pNew){printf("分配失败");exit(-1);}pNew->data = val;//首先把本次创建的新节点的值付给新节点的数据域Tem->pNext = pNew;//然后使用临时的节点变量的指针域保存了新节点的地址,也就是指向了新节点pNew->pNext = NULL;//如何再不循环,新节点成为最后一个节点Tem = pNew;//把本次分配的新节点完全的赋给Tem,Tem就成为了这次新节点的影子,那么下次分配新节点时可以使用上个新节点的数据}return pHead;
}void traverse_list(PNODE pHead)
{PNODE p = pHead;//使用P是为了不改写头结点里保存的地址p = pHead->pNext;//使P指向首节点while(p != NULL)//P本来就是头结点的指针域,也就是首节点的地址,既然是地址就可以直接判断p是否等于NULL{printf("%d ", p->data);p = p->pNext;//使P每循环一次就变成P的下一个节点}
}bool is_empty(PNODE pHead)
{if(NULL == pHead->pNext)return true;elsereturn false;
}int length_list(PNODE pHead)
{PNODE p = pHead->pNext;int len = 0;while(p != NULL){len++;p = p->pNext;}return len;
}void sort_list(PNODE pHead)
{int i, j, t, len;PNODE p, q;len = length_list(pHead);for(i=0,p=pHead->pNext;i<len;i++,p=p->pNext)//逗号后只是为了找到下一个节点,因为不是数组,所以不能使用下标来++{for(j=0,q=pHead->pNext;j<len;j++,q=q->pNext)if(q->data > p->data)//这里的大小与号可以决定是升序还是降序,如果是大于号就是升序,反之小于号就是降序{t = q->data;q->data = p->data;p->data = t;}}return;
}void insert_list(PNODE pHead, int pos, int val)
{int i;PNODE q = pHead;PNODE p = pHead;if(pos > 0 && pos <= length_list(pHead)){for(i=0;i<pos;i++){q = q->pNext;//q就是要插入的连接点}for(i=1;i<pos;i++){p = p->pNext;//p就是要插入连接点的前一个节点}PNODE pNew = (PNODE)malloc(sizeof(NODE));p->pNext = pNew;pNew->data = val;pNew->pNext = q;}else if(pos > length_list(pHead))//追加{PNODE t;t = pHead;PNODE PN;PN = (PNODE)malloc(sizeof(NODE));if(PN == NULL)printf("分配失败");elsewhile(t->pNext != NULL){t = t->pNext;//使T->pNext成为尾结点}PN->data = val;//给新节点赋予有效数据t->pNext = PN;//使尾结点的指针域指向了新的结点PN->pNext = NULL;//新节点成为尾结点}elseprintf("error\n");return;
}int delete_list(PNODE pHead, int pos, int val)
{int i, j;PNODE q, p;q = pHead;p = pHead;if(pos > 0 && pos <= length_list(pHead))//保证删除的是节点的有效数{for(i=0;i<pos;i++){p = p->pNext;}for(j=1;j<pos;j++){if(pos == 0)q = pHead;elseq = q->pNext;}q->pNext = p->pNext;val = p->data;free(p);return val;}elseprintf("error");return 0;
}void freeer(PNODE pHead)
{PNODE pT = pHead;while(NULL != pHead->pNext){free(pT);pT = pT->pNext;}return;
}

XCODE上实现:
目录结构

//NodeList.c
#include "NodeList.h"
#include <stdlib.h>
//功能 创建单链表和节点
NodeList *createNodeList(int value){NodeList *list = (NodeList *)malloc(sizeof(NodeList));list->head = createNode(value);list->head->next = NULL;list->count = 1;return list;
}
//创建节点
Node *createNode(int value){Node *node = (Node*)malloc(sizeof(Node));node->value = value;node->next = NULL;return node;
}//打印内容
void printfNodeList(NodeList *list){Node* curNode = list->head;while (curNode != NULL) {printf("%d ",curNode->value);curNode = curNode->next;}printf("= %d", list->count);printf("\n");}// 在链表尾部添加一个结点
Node* addNode(NodeList* list, int value){// 找到链表中最后一个结点的位置Node* curNode = list->head;while(curNode->next != NULL){curNode = curNode->next;}// 将最后的结点的next值指向新结点Node *newNode = createNode(value);curNode->next = newNode;// 将新结点的next值置为NULLnewNode->next = NULL;++list->count;return newNode;}// 通过下标添加一个结点
void insertNodeByIndex(NodeList* list, int value, int index){// 处理index = 0的情况if (index == 0) {// 创建一个新结点Node *newNode = createNode(value);// 新结点的next指向头结点newNode->next = list->head;// 将头结点head指针指向新结点list->head = newNode;// 更新链表元素数量++list->count;return;}Node *curNode = list->head;int curIndex = 0;while (curIndex != index - 1) {curNode = curNode ->next;curIndex ++;}// 创建一个新结点Node *newNode = createNode(value);// 将新结点的next指向curNode的next的位置newNode->next = curNode->next;// 将当前的结点的next指向新结点的位置curNode->next = newNode;++list->count;}// 删除一个结点
void removeNode(NodeList *list,Node *node){if (node == list->head) {list->head = list->head->next;free(node);--list->count;return;}Node*curNode = list->head;while (curNode->next!=node) {curNode = curNode->next;}curNode->next = node->next;free(node);
}
// 通过下标去移除结点
void removeNodeByIndex(NodeList* list, int index){if (index == 0) {removeNode(list, list->head);return;}//根据下标找到地址Node *curNode = list->head;int curIndex = 0;while (curIndex != index - 1) {curNode = curNode ->next;++curIndex;}curNode->next = curNode->next->next;free(curNode->next);
//    Node *delNode = curNode->next;
//    curNode->next = delNode ->next;
//    free(delNode);--list->count;
}// 求链表的元素个数
int getCount(NodeList* list){return  -1;
}// 销毁链表
void destroyNodeList(NodeList* list){// 销毁链表元素int i;for (i = list->count - 1; i >= 0; --i) {removeNodeByIndex(list, i);}// 销毁链表free(list);
}//NodeList.h
#ifndef NodeList_h
#define NodeList_h#include <stdio.h>//节点
struct Node{int value;struct Node *next;
};
typedef struct Node Node;//链表
struct NodeList{Node *head;int count;
};
typedef struct NodeList NodeList;//功能 创建单链表和节点
NodeList *createNodeList(int value);Node *createNode(int value);// 在链表尾部添加一个结点
Node* addNode(NodeList* list, int value);// 通过下标添加一个结点
void insertNodeByIndex(NodeList* list, int value, int index);//打印内容
void printfNodeList(NodeList *list);// 删除一个结点
void removeNode(NodeList *list,Node *node);// 通过下标去移除结点
void removeNodeByIndex(NodeList* list, int index);// 求链表的元素个数
int getCount(NodeList* list);// 销毁链表
void destroyNodeList(NodeList* list);
#endif /* NodeList_h *///主函数
#import "NodeList.h"
#import <Foundation/Foundation.h>int main(int argc, const char * argv[]) {@autoreleasepool {NodeList *list = createNodeList(0);addNode(list, 1);addNode(list, 2);addNode(list, 3);addNode(list, 100);printfNodeList(list);removeNode(list, list->head);insertNodeByIndex(list, 4, 4);printfNodeList(list);removeNode(list, list->head);printfNodeList(list);destroyNodeList(list);}return 0;
}

C语言(数据结构) - 链表的基本操作相关推荐

  1. C++数据结构链表的基本操作

    这篇文章主要为大家介绍了C++数据结构链表基本操作的示例过程有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪 首先创建好一个节点 typedef struct node {in ...

  2. 《数据结构》C语言版 链表的基本操作实现

    #include <stdio.h> #include <stdlib.h>#define ok 1 #define error -1typedef int ElemType; ...

  3. C语言实现链表的基本操作(超详细注释)

    第一次学数据结构的时候,c语言基础实在太差,在尝试用c语言来实现的时候一直碰壁,数据结构书里的代码是伪代码,使用的时候各种内存访问冲突,各种锟斤拷烫烫烫,各种变量级别不同.于是我又重新学了一遍c语言, ...

  4. C语言数据结构——链表

    目录 前言 一.什么是链表 1.1链表的结构和概念 1.2 链表的分类 二.无头单向非循环链表 2.1 创建结构体 2.2 动态申请一个节点 2.3 单链表打印 2.4 单链表尾插/尾删 2.4.1 ...

  5. C语言 数据结构 链表的增删查改

    分别用函数实现了链表的: 1.增加(头插法,尾插法,有序插入) 2.删除 3.修改 4.查找 C代码 #define _CRT_SECURE_NO_WARNINGS #include<stdio ...

  6. c语言单链表的基本操作

    该程序包含了链表的头插法.尾插法.求表长.按位查找.按值查找.插入.删除.销毁等几种操作: #include <stdio.h> #include <stdlib.h> //m ...

  7. C语言数据结构之二叉树的层次建树及遍历方法(前序,中序,后序,层次遍历)

    C语言数据结构之二叉树的层次建树及遍历方法(前序,中序,后序,层次遍历) tips:前些天学习了C语言数据结构链表,栈,队列.今天来学习一下C语言数据结构之二叉树的各种操作. 注意:二叉树的层次建树是 ...

  8. c语言求链表节点的删除,C语言实现链表节点的删除

    对链表节点进行增删改查是最基本的操作,这篇博客将会来实现对节点的删除.其他的操作可参考<c语言实现链表的基本操作>这篇博客.删除某个节点有两个类型: (1)删除i某个位置的节点: (2)判 ...

  9. 数据结构之【线性表】(顺序表、链表的基本操作实现)

    概念 线性表:是N个数据元素的有限序列. 顺序表:用一组地址连续的存储单元依次存储[线性表 ]的数据元素.(区别于有序表:表中的数据元素存在非递增或非递减有序) 链表:用一组任意的存储单元来存储[线性 ...

  10. c语言单链表功能,[数据结构]单链表(C语言)的各种功能

    06-03阅读200,000 + 链表是一种常见的基本数据结构,在此充分利用了结构指针. 链表可以动态存储和分配,即链表是一个功能非常强大的数组. 他可以在节点中定义多种数据类型,并可以根据需要随意添 ...

最新文章

  1. 图灵4月精彩新书预告
  2. android app 自动更新,AndroidUpdateDemo
  3. Aria2:轻量命令行下载工具
  4. SSM整合+分页+Druid+CRU+log4J+junit+事务+Json+Bootstrap入门教程总览目录
  5. boost::math模块演示负二项分布使用的简单示例的测试程序
  6. DjangoHTML页面加载和静态文件加载
  7. java treemap lastkey_Java TreeMap lastKey()用法及代码示例
  8. 【实用工具】linux Can‘t bind address: Address already in use
  9. Caused by: java.lang.ClassNotFoundException: Didn’t find class on path apk Android Studio解决方案
  10. linux下载jdk
  11. 解析大型门户网站教给我们的PV增加技巧
  12. 开篇:为什么开始写博客
  13. c语言逻辑运算符的作用,C语言逻辑运算符知识整理
  14. 解决Rigify报错:“Please specify a valid pivot bone position. Incorrect armature for type ‘basic_spine‘”
  15. WEB基础之:创建表格
  16. js拖拽(二)仿iGoogle自定义首页模块拖拽
  17. Google十大高薪职位:首席软件工程师居首
  18. Unity 异常记录日志功能
  19. [案例分享]根据现有产品数据,如何做一次数据分析呢?
  20. Quartz的一般配置方法

热门文章

  1. Google Earth Engine(GEE)——如何在线计算NDVI(归一化植被指数)和FVC(植被覆盖度)并批量下载
  2. Unity 建立的3D物体 导成 Maya能识别的obj文件
  3. xshell无法启动解决方案
  4. C# WPF 调用打印机的两种方法
  5. php 设置字符集函数,PHP 数组字符集编码转换的函数
  6. 【云原生系列】云计算概念与架构设计介绍
  7. 根据上下级关系统计数据
  8. arcgis select by attributes一次选多个_ArcGIS中属性表的常用操作汇总
  9. Offsetof用法
  10. (JavaWeb笔记)win10+Tomcat8.5.69安装和配置